Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:f7f1f0d76dd6, committed 2018-08-23
- Comitter:
- XinZhangMS
- Date:
- Thu Aug 23 06:52:14 2018 +0000
- Commit message:
- azure-c-sdk for mbed os supporting NUCLEO_F767ZI
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/agenttime_mbed.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/agenttime.h" + +// mbed version of gmtime() returns NULL. +// system RTC should be set to UTC as its localtime + +time_t get_time(time_t* currentTime) +{ + return time(currentTime); +} + +double get_difftime(time_t stopTime, time_t startTime) +{ + return difftime(stopTime, startTime); +} + + +struct tm* get_gmtime(time_t* currentTime) +{ + return localtime(currentTime); +} + +char* get_ctime(time_t* timeToGet) +{ + return ctime(timeToGet); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/condition_rtx_mbed.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/condition.h" +#include "rtos.h" + +COND_HANDLE Condition_Init(void) +{ + return NULL; +} + +COND_RESULT Condition_Post(COND_HANDLE handle) +{ + COND_RESULT result; + if (handle == NULL) + { + result = COND_INVALID_ARG; + } + else + { + result = COND_ERROR; + } + return result; +} + +COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds) +{ + COND_RESULT result; + if (handle == NULL) + { + result = COND_INVALID_ARG; + } + else + { + result = COND_ERROR; + } + return result; +} + +void Condition_Deinit(COND_HANDLE handle) +{ + if (handle != NULL) + { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/httpapi_compact.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1417 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/shared_util_options.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +/*Codes_SRS_HTTPAPI_COMPACT_21_001: [ The httpapi_compact shall implement the methods defined by the `httpapi.h`. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_002: [ The httpapi_compact shall support the http requests. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_003: [ The httpapi_compact shall return error codes defined by HTTPAPI_RESULT. ]*/ +#include "azure_c_shared_utility/httpapi.h" + +#define MAX_HOSTNAME 64 +#define TEMP_BUFFER_SIZE 1024 + +/*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ +#define MAX_OPEN_RETRY 100 +/*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ +#define MAX_CLOSE_RETRY 100 +/*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ +#define MAX_SEND_RETRY 200 +/*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ +#define MAX_RECEIVE_RETRY 200 +/*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ +#define RETRY_INTERVAL_IN_MICROSECONDS 100 + +DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES) + +typedef struct HTTP_HANDLE_DATA_TAG +{ + char* certificate; + char* x509ClientCertificate; + char* x509ClientPrivateKey; + XIO_HANDLE xio_handle; + size_t received_bytes_count; + unsigned char* received_bytes; + unsigned int is_io_error : 1; + unsigned int is_connected : 1; + unsigned int send_completed : 1; +} HTTP_HANDLE_DATA; + +/*the following function does the same as sscanf(pos2, "%d", &sec)*/ +/*this function only exists because some of platforms do not have sscanf. */ +static int ParseStringToDecimal(const char *src, int* dst) +{ + int result; + char* next; + long num = strtol(src, &next, 0); + if (src == next || num < INT_MIN || num > INT_MAX) + { + result = EOF; + } + else + { + result = 1; + } + if (num < INT_MIN) num = INT_MIN; + if (num > INT_MAX) num = INT_MAX; + *dst = (int)num; + return result; +} + +/*the following function does the same as sscanf(pos2, "%x", &sec)*/ +/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined x numbers. */ +#define HEXA_DIGIT_VAL(c) (((c>='0') && (c<='9')) ? (c-'0') : ((c>='a') && (c<='f')) ? (c-'a'+10) : ((c>='A') && (c<='F')) ? (c-'A'+10) : -1) +static int ParseStringToHexadecimal(const char *src, size_t* dst) +{ + int result; + int digitVal; + if (src == NULL) + { + result = EOF; + } + else if (HEXA_DIGIT_VAL(*src) == -1) + { + result = EOF; + } + else + { + (*dst) = 0; + while ((digitVal = HEXA_DIGIT_VAL(*src)) != -1) + { + (*dst) *= 0x10; + (*dst) += (size_t)digitVal; + src++; + } + result = 1; + } + return result; +} + +/*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */ +/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */ +static int ParseHttpResponse(const char* src, int* dst) +{ + int result; + static const char HTTPPrefix[] = "HTTP/"; + bool fail; + const char* runPrefix; + + if ((src == NULL) || (dst == NULL)) + { + result = EOF; + } + else + { + fail = false; + runPrefix = HTTPPrefix; + + while((*runPrefix) != '\0') + { + if ((*runPrefix) != (*src)) + { + fail = true; + break; + } + src++; + runPrefix++; + } + + if (!fail) + { + while ((*src) != '.') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (!fail) + { + while ((*src) != ' ') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (fail) + { + result = EOF; + } + else + { + result = ParseStringToDecimal(src, dst); + } + } + + return result; +} + +HTTPAPI_RESULT HTTPAPI_Init(void) +{ +/*Codes_SRS_HTTPAPI_COMPACT_21_004: [ The HTTPAPI_Init shall allocate all memory to control the http protocol. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_007: [ If there is not enough memory to control the http protocol, the HTTPAPI_Init shall return HTTPAPI_ALLOC_FAILED. ]*/ +/** + * No memory is necessary. + */ + + /*Codes_SRS_HTTPAPI_COMPACT_21_006: [ If HTTPAPI_Init succeed allocating all the needed memory, it shall return HTTPAPI_OK. ]*/ +return HTTPAPI_OK; +} + +void HTTPAPI_Deinit(void) +{ + /*Codes_SRS_HTTPAPI_COMPACT_21_009: [ The HTTPAPI_Init shall release all memory allocated by the httpapi_compact. ]*/ + /** + * No memory was necessary. + */ +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_011: [ The HTTPAPI_CreateConnection shall create an http connection to the host specified by the hostName parameter. ]*/ +HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName) +{ + HTTP_HANDLE_DATA* http_instance; + TLSIO_CONFIG tlsio_config; + + if (hostName == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_014: [ If the hostName is NULL, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + LogError("Invalid host name. Null hostName parameter."); + http_instance = NULL; + } + else if (*hostName == '\0') + { + /*Codes_SRS_HTTPAPI_COMPACT_21_015: [ If the hostName is empty, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + LogError("Invalid host name. Empty string."); + http_instance = NULL; + } + else + { + http_instance = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA)); + /*Codes_SRS_HTTPAPI_COMPACT_21_013: [ If there is not enough memory to control the http connection, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + if (http_instance == NULL) + { + LogError("There is no memory to control the http connection"); + } + else + { + tlsio_config.hostname = hostName; + tlsio_config.port = 443; + tlsio_config.underlying_io_interface = NULL; + tlsio_config.underlying_io_parameters = NULL; + + http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config); + + /*Codes_SRS_HTTPAPI_COMPACT_21_016: [ If the HTTPAPI_CreateConnection failed to create the connection, it shall return NULL as the handle. ]*/ + if (http_instance->xio_handle == NULL) + { + LogError("Create connection failed"); + free(http_instance); + http_instance = NULL; + } + else + { + http_instance->is_connected = 0; + http_instance->is_io_error = 0; + http_instance->received_bytes_count = 0; + http_instance->received_bytes = NULL; + http_instance->certificate = NULL; + http_instance->x509ClientCertificate = NULL; + http_instance->x509ClientPrivateKey = NULL; + } + } + } + + /*Codes_SRS_HTTPAPI_COMPACT_21_012: [ The HTTPAPI_CreateConnection shall return a non-NULL handle on success. ]*/ + return (HTTP_HANDLE)http_instance; +} + +static void on_io_close_complete(void* context) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + http_instance->is_connected = 0; + } +} + +void HTTPAPI_CloseConnection(HTTP_HANDLE handle) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; + + /*Codes_SRS_HTTPAPI_COMPACT_21_020: [ If the connection handle is NULL, the HTTPAPI_CloseConnection shall not do anything. ]*/ + if (http_instance != NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_019: [ If there is no previous connection, the HTTPAPI_CloseConnection shall not do anything. ]*/ + if (http_instance->xio_handle != NULL) + { + http_instance->is_io_error = 0; + /*Codes_SRS_HTTPAPI_COMPACT_21_017: [ The HTTPAPI_CloseConnection shall close the connection previously created in HTTPAPI_ExecuteRequest. ]*/ + if (xio_close(http_instance->xio_handle, on_io_close_complete, http_instance) != 0) + { + LogError("The SSL got error closing the connection"); + /*Codes_SRS_HTTPAPI_COMPACT_21_087: [ If the xio return anything different than 0, the HTTPAPI_CloseConnection shall destroy the connection anyway. ]*/ + http_instance->is_connected = 0; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ + int countRetry = MAX_CLOSE_RETRY; + while (http_instance->is_connected == 1) + { + xio_dowork(http_instance->xio_handle); + if ((countRetry--) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_085: [ If the HTTPAPI_CloseConnection retries 10 seconds to close the connection without success, it shall destroy the connection anyway. ]*/ + LogError("Close timeout. The SSL didn't close the connection"); + http_instance->is_connected = 0; + } + else if (http_instance->is_io_error == 1) + { + LogError("The SSL got error closing the connection"); + http_instance->is_connected = 0; + } + else if (http_instance->is_connected == 1) + { + LogInfo("Waiting for TLS close connection"); + /*Codes_SRS_HTTPAPI_COMPACT_21_086: [ The HTTPAPI_CloseConnection shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + /*Codes_SRS_HTTPAPI_COMPACT_21_076: [ After close the connection, The HTTPAPI_CloseConnection shall destroy the connection previously created in HTTPAPI_CreateConnection. ]*/ + xio_destroy(http_instance->xio_handle); + } + + /*Codes_SRS_HTTPAPI_COMPACT_21_018: [ If there is a certificate associated to this connection, the HTTPAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ + if (http_instance->certificate) + { + free(http_instance->certificate); + } + + /*Codes_SRS_HTTPAPI_COMPACT_06_001: [ If there is a x509 client certificate associated to this connection, the HTTAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ + if (http_instance->x509ClientCertificate) + { + free(http_instance->x509ClientCertificate); + } + + /*Codes_SRS_HTTPAPI_COMPACT_06_002: [ If there is a x509 client private key associated to this connection, then HTTP_CloseConnection shall free all the allocated memory for the private key. ]*/ + if (http_instance->x509ClientPrivateKey) + { + free(http_instance->x509ClientPrivateKey); + } + free(http_instance); + } +} + +static void on_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + if (open_result == IO_OPEN_OK) + { + http_instance->is_connected = 1; + http_instance->is_io_error = 0; + } + else + { + http_instance->is_io_error = 1; + } + } +} + +static void on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + if (send_result == IO_SEND_OK) + { + http_instance->send_completed = 1; + http_instance->is_io_error = 0; + } + else + { + http_instance->is_io_error = 1; + } + } +} + +#define TOLOWER(c) (((c>='A') && (c<='Z'))?c-'A'+'a':c) +static int InternStrnicmp(const char* s1, const char* s2, size_t n) +{ + int result; + + if (s1 == NULL) result = -1; + else if (s2 == NULL) result = 1; + else + { + result = 0; + + while(n-- && result == 0) + { + if (*s1 == 0) result = -1; + else if (*s2 == 0) result = 1; + else + { + + result = TOLOWER(*s1) - TOLOWER(*s2); + ++s1; + ++s2; + } + } + } + + return result; +} + +static void on_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + unsigned char* new_received_bytes; + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + + if (buffer == NULL) + { + http_instance->is_io_error = 1; + LogError("NULL pointer error"); + } + else + { + /* Here we got some bytes so we'll buffer them so the receive functions can consumer it */ + new_received_bytes = (unsigned char*)realloc(http_instance->received_bytes, http_instance->received_bytes_count + size); + if (new_received_bytes == NULL) + { + http_instance->is_io_error = 1; + LogError("Error allocating memory for received data"); + } + else + { + http_instance->received_bytes = new_received_bytes; + if (memcpy(http_instance->received_bytes + http_instance->received_bytes_count, buffer, size) == NULL) + { + http_instance->is_io_error = 1; + LogError("Error copping received data to the HTTP bufffer"); + } + else + { + http_instance->received_bytes_count += size; + } + } + } + } +} + +static void on_io_error(void* context) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + if (http_instance != NULL) + { + http_instance->is_io_error = 1; + LogError("Error signalled by underlying IO"); + } +} + +static int conn_receive(HTTP_HANDLE_DATA* http_instance, char* buffer, int count) +{ + int result; + + if ((http_instance == NULL) || (buffer == NULL) || (count < 0)) + { + LogError("conn_receive: %s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer")); + result = -1; + } + else + { + result = 0; + while (result < count) + { + xio_dowork(http_instance->xio_handle); + + /* if any error was detected while receiving then simply break and report it */ + if (http_instance->is_io_error != 0) + { + LogError("xio reported error on dowork"); + result = -1; + break; + } + + if (http_instance->received_bytes_count >= (size_t)count) + { + /* Consuming bytes from the receive buffer */ + (void)memcpy(buffer, http_instance->received_bytes, count); + (void)memmove(http_instance->received_bytes, http_instance->received_bytes + count, http_instance->received_bytes_count - count); + http_instance->received_bytes_count -= count; + + /* we're not reallocating at each consumption so that we don't trash due to byte by byte consumption */ + if (http_instance->received_bytes_count == 0) + { + free(http_instance->received_bytes); + http_instance->received_bytes = NULL; + } + + result = count; + break; + } + + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + + return result; +} + +static void conn_receive_discard_buffer(HTTP_HANDLE_DATA* http_instance) +{ + if (http_instance != NULL) + { + if (http_instance->received_bytes != NULL) + { + free(http_instance->received_bytes); + http_instance->received_bytes = NULL; + } + http_instance->received_bytes_count = 0; + } +} + +static int readLine(HTTP_HANDLE_DATA* http_instance, char* buf, const size_t maxBufSize) +{ + int resultLineSize; + + if ((http_instance == NULL) || (buf == NULL) || (maxBufSize == 0)) + { + LogError("%s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer")); + resultLineSize = -1; + } + else + { + char* destByte = buf; + /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ + int countRetry = MAX_RECEIVE_RETRY; + bool endOfSearch = false; + resultLineSize = -1; + while (!endOfSearch) + { + xio_dowork(http_instance->xio_handle); + + /* if any error was detected while receiving then simply break and report it */ + if (http_instance->is_io_error != 0) + { + LogError("xio reported error on dowork"); + endOfSearch = true; + } + else + { + unsigned char* receivedByte = http_instance->received_bytes; + while (receivedByte < (http_instance->received_bytes + http_instance->received_bytes_count)) + { + if ((*receivedByte) != '\r') + { + (*destByte) = (*receivedByte); + destByte++; + receivedByte++; + + if (destByte >= (buf + maxBufSize - 1)) + { + LogError("Received message is bigger than the http buffer"); + receivedByte = http_instance->received_bytes + http_instance->received_bytes_count; + endOfSearch = true; + break; + } + } + else + { + receivedByte++; + if ((receivedByte < (http_instance->received_bytes + http_instance->received_bytes_count)) && ((*receivedByte) == '\n')) + { + receivedByte++; + } + (*destByte) = '\0'; + resultLineSize = (int)(destByte - buf); + endOfSearch = true; + break; + } + } + + http_instance->received_bytes_count -= (receivedByte - http_instance->received_bytes); + if (http_instance->received_bytes_count != 0) + { + (void)memmove(http_instance->received_bytes, receivedByte, http_instance->received_bytes_count); + } + else + { + conn_receive_discard_buffer(http_instance); + } + } + + if (!endOfSearch) + { + if ((countRetry--) > 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + LogError("Receive timeout. The HTTP request is incomplete"); + endOfSearch = true; + } + } + } + } + + return resultLineSize; +} + +static int readChunk(HTTP_HANDLE_DATA* http_instance, char* buf, size_t size) +{ + int cur, offset; + + // read content with specified length, even if it is received + // only in chunks due to fragmentation in the networking layer. + // returns -1 in case of error. + offset = 0; + while (size > (size_t)0) + { + cur = conn_receive(http_instance, buf + offset, (int)size); + + // end of stream reached + if (cur == 0) + { + break; + } + + // read cur bytes (might be less than requested) + size -= (size_t)cur; + offset += cur; + } + + return offset; +} + +static int skipN(HTTP_HANDLE_DATA* http_instance, size_t n) +{ + // read and abandon response content with specified length + // returns -1 in case of error. + + int result; + + if (http_instance == NULL) + { + LogError("Invalid HTTP instance"); + result = -1; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ + int countRetry = MAX_RECEIVE_RETRY; + result = (int)n; + while (n > 0) + { + xio_dowork(http_instance->xio_handle); + + /* if any error was detected while receiving then simply break and report it */ + if (http_instance->is_io_error != 0) + { + LogError("xio reported error on dowork"); + result = -1; + n = 0; + } + else + { + if (http_instance->received_bytes_count <= n) + { + n -= http_instance->received_bytes_count; + http_instance->received_bytes_count = 0; + } + else + { + http_instance->received_bytes_count -= n; + (void)memmove(http_instance->received_bytes, http_instance->received_bytes + n, http_instance->received_bytes_count); + n = 0; + } + + if (n > 0) + { + if ((countRetry--) > 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + LogError("Receive timeout. The HTTP request is incomplete"); + n = 0; + result = -1; + } + } + } + } + } + + return result; +} + + +/*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ +static HTTPAPI_RESULT OpenXIOConnection(HTTP_HANDLE_DATA* http_instance) +{ + HTTPAPI_RESULT result; + + if (http_instance->is_connected != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + else + { + http_instance->is_io_error = 0; + + /*Codes_SRS_HTTPAPI_COMPACT_21_022: [ If a Certificate was provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ + if ((http_instance->certificate != NULL) && + (xio_setoption(http_instance->xio_handle, "TrustedCerts", http_instance->certificate) != 0)) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_023: [ If the transport failed setting the Certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load certificate"); + } + /*Codes_SRS_HTTPAPI_COMPACT_06_003: [ If the x509 client certificate is provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ + else if ((http_instance->x509ClientCertificate != NULL) && + (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_CERT, http_instance->x509ClientCertificate) != 0)) + { + /*Codes_SRS_HTTPAPI_COMPACT_06_005: [ If the transport failed setting the client certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load the client certificate"); + } + else if ((http_instance->x509ClientPrivateKey != NULL) && + (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_instance->x509ClientPrivateKey) != 0)) + { + + /*Codes_SRS_HTTPAPI_COMPACT_06_006: [ If the transport failed setting the client certificate private key, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ] */ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load the client certificate private key"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ + if (xio_open(http_instance->xio_handle, on_io_open_complete, http_instance, on_bytes_received, http_instance, on_io_error, http_instance) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + result = HTTPAPI_OPEN_REQUEST_FAILED; + } + else + { + int countRetry; + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ + countRetry = MAX_OPEN_RETRY; + while ((http_instance->is_connected == 0) && + (http_instance->is_io_error == 0)) + { + xio_dowork(http_instance->xio_handle); + LogInfo("Waiting for TLS connection"); + if ((countRetry--) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_078: [ If the HTTPAPI_ExecuteRequest cannot open the connection in 10 seconds, it shall fail and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + LogError("Open timeout. The HTTP request is incomplete"); + result = HTTPAPI_OPEN_REQUEST_FAILED; + break; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + } + } + + if ((http_instance->is_io_error != 0) && (result == HTTPAPI_OK)) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + result = HTTPAPI_OPEN_REQUEST_FAILED; + } + + return result; +} + +static HTTPAPI_RESULT conn_send_all(HTTP_HANDLE_DATA* http_instance, const unsigned char* buf, size_t bufLen) +{ + HTTPAPI_RESULT result; + + http_instance->send_completed = 0; + http_instance->is_io_error = 0; + if (xio_send(http_instance->xio_handle, buf, bufLen, on_send_complete, http_instance) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ + int countRetry = MAX_SEND_RETRY; + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + while ((http_instance->send_completed == 0) && (result == HTTPAPI_OK)) + { + xio_dowork(http_instance->xio_handle); + if (http_instance->is_io_error != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else if ((countRetry--) <= 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_080: [ If the HTTPAPI_ExecuteRequest retries to send the message for 20 seconds without success, it shall fail and return HTTPAPI_SEND_REQUEST_FAILED. ]*/ + LogError("Send timeout. The HTTP request is incomplete"); + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_035: [ The HTTPAPI_ExecuteRequest shall execute resquest for types `GET`, `POST`, `PUT`, `DELETE`, `PATCH`. ]*/ +const char httpapiRequestString[5][7] = { "GET", "POST", "PUT", "DELETE", "PATCH" }; +const char* get_request_type(HTTPAPI_REQUEST_TYPE requestType) +{ + return (const char*)httpapiRequestString[requestType]; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ +static HTTPAPI_RESULT SendHeadsToXIO(HTTP_HANDLE_DATA* http_instance, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE httpHeadersHandle, size_t headersCount) +{ + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + int ret; + + //Send request + /*Codes_SRS_HTTPAPI_COMPACT_21_038: [ The HTTPAPI_ExecuteRequest shall execute the resquest for the path in relativePath parameter. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_036: [ The request type shall be provided in the parameter requestType. ]*/ + if (((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", get_request_type(requestType), relativePath)) < 0) || + ((size_t)ret >= sizeof(buf))) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ + result = HTTPAPI_STRING_PROCESSING_ERROR; + } + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + else if ((result = conn_send_all(http_instance, (const unsigned char*)buf, strlen(buf))) == HTTPAPI_OK) + { + size_t i; + //Send default headers + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + for (i = 0; ((i < headersCount) && (result == HTTPAPI_OK)); i++) + { + char* header; + if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ + result = HTTPAPI_STRING_PROCESSING_ERROR; + } + else + { + if ((result = conn_send_all(http_instance, (const unsigned char*)header, strlen(header))) == HTTPAPI_OK) + { + result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); + } + free(header); + } + } + + //Close headers + if (result == HTTPAPI_OK) + { + result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); + } + } + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ +static HTTPAPI_RESULT SendContentToXIO(HTTP_HANDLE_DATA* http_instance, const unsigned char* content, size_t contentLength) +{ + HTTPAPI_RESULT result; + + //Send data (if available) + /*Codes_SRS_HTTPAPI_COMPACT_21_045: [ If the contentLength is lower than one, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ + if (content && contentLength > 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_044: [ If the content is not NULL, the number of bytes in the content shall be provided in contentLength parameter. ]*/ + result = conn_send_all(http_instance, content, contentLength); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_043: [ If the content is NULL, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ +static HTTPAPI_RESULT ReceiveHeaderFromXIO(HTTP_HANDLE_DATA* http_instance, unsigned int* statusCode) +{ + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + int ret; + + http_instance->is_io_error = 0; + + //Receive response + if (readLine(http_instance, buf, TEMP_BUFFER_SIZE) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + //Parse HTTP response + else if (ParseHttpResponse(buf, &ret) != 1) + { + //Cannot match string, error + /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the received message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ + LogInfo("Not a correct HTTP answer"); + result = HTTPAPI_RECEIVE_RESPONSE_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_046: [ The HTTPAPI_ExecuteRequest shall return the http status reported by the host in the received response. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_048: [ If the statusCode is NULL, the HTTPAPI_ExecuteRequest shall report not report any status. ]*/ + if (statusCode) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_047: [ The HTTPAPI_ExecuteRequest shall report the status in the statusCode parameter. ]*/ + *statusCode = ret; + } + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + + return result; +} + +static HTTPAPI_RESULT ReceiveContentInfoFromXIO(HTTP_HANDLE_DATA* http_instance, HTTP_HEADERS_HANDLE responseHeadersHandle, size_t* bodyLength, bool* chunked) +{ + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + const char* substr; + char* whereIsColon; + int lengthInMsg; + const char ContentLength[] = "content-length:"; + const size_t ContentLengthSize = sizeof(ContentLength) - 1; + const char TransferEncoding[] = "transfer-encoding:"; + const size_t TransferEncodingSize = sizeof(TransferEncoding) - 1; + const char Chunked[] = "chunked"; + const size_t ChunkedSize = sizeof(Chunked) - 1; + + http_instance->is_io_error = 0; + + //Read HTTP response headers + if (readLine(http_instance, buf, sizeof(buf)) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + + while (*buf && (result == HTTPAPI_OK)) + { + if (InternStrnicmp(buf, ContentLength, ContentLengthSize) == 0) + { + substr = buf + ContentLengthSize; + if (ParseStringToDecimal(substr, &lengthInMsg) != 1) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + (*bodyLength) = (size_t)lengthInMsg; + } + } + else if (InternStrnicmp(buf, TransferEncoding, TransferEncodingSize) == 0) + { + substr = buf + TransferEncodingSize; + + while (isspace(*substr)) substr++; + + if (InternStrnicmp(substr, Chunked, ChunkedSize) == 0) + { + (*chunked) = true; + } + } + + if (result == HTTPAPI_OK) + { + whereIsColon = strchr((char*)buf, ':'); + /*Codes_SRS_HTTPAPI_COMPACT_21_049: [ If responseHeadersHandle is provide, the HTTPAPI_ExecuteRequest shall prepare a Response Header usign the HTTPHeaders_AddHeaderNameValuePair. ]*/ + if (whereIsColon && (responseHeadersHandle != NULL)) + { + *whereIsColon = '\0'; + HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1); + } + + if (readLine(http_instance, buf, sizeof(buf)) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + } + } + } + + return result; +} + +static HTTPAPI_RESULT ReadHTTPResponseBodyFromXIO(HTTP_HANDLE_DATA* http_instance, size_t bodyLength, bool chunked, BUFFER_HANDLE responseContent) +{ + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + const unsigned char* receivedContent; + + http_instance->is_io_error = 0; + + //Read HTTP response body + if (!chunked) + { + if (bodyLength) + { + if (responseContent != NULL) + { + if (BUFFER_pre_build(responseContent, bodyLength) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (BUFFER_content(responseContent, &receivedContent) != 0) + { + (void)BUFFER_unbuild(responseContent); + + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (readChunk(http_instance, (char*)receivedContent, bodyLength) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ + if (skipN(http_instance, bodyLength) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + result = HTTPAPI_OK; + } + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + } + else + { + size_t size = 0; + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + while (result == HTTPAPI_OK) + { + size_t chunkSize; + if (readLine(http_instance, buf, sizeof(buf)) < 0) // read [length in hex]/r/n + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else if (ParseStringToHexadecimal(buf, &chunkSize) != 1) // chunkSize is length of next line (/r/n is not counted) + { + //Cannot match string, error + /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the received message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ + result = HTTPAPI_RECEIVE_RESPONSE_FAILED; + } + else if (chunkSize == 0) + { + // 0 length means next line is just '\r\n' and end of chunks + if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 + || buf[0] != '\r' || buf[1] != '\n') // skip /r/n + { + (void)BUFFER_unbuild(responseContent); + + result = HTTPAPI_READ_DATA_FAILED; + } + break; + } + else + { + if (responseContent != NULL) + { + if (BUFFER_enlarge(responseContent, chunkSize) != 0) + { + (void)BUFFER_unbuild(responseContent); + + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (BUFFER_content(responseContent, &receivedContent) != 0) + { + (void)BUFFER_unbuild(responseContent); + + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (readChunk(http_instance, (char*)receivedContent + size, chunkSize) < 0) + { + result = HTTPAPI_READ_DATA_FAILED; + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ + if (skipN(http_instance, chunkSize) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + } + + if (result == HTTPAPI_OK) + { + if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 + || buf[0] != '\r' || buf[1] != '\n') // skip /r/n + { + result = HTTPAPI_READ_DATA_FAILED; + } + size += chunkSize; + } + } + } + + } + return result; +} + + +/*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ +static bool validRequestType(HTTPAPI_REQUEST_TYPE requestType) +{ + bool result; + + if ((requestType == HTTPAPI_REQUEST_GET) || + (requestType == HTTPAPI_REQUEST_POST) || + (requestType == HTTPAPI_REQUEST_PUT) || + (requestType == HTTPAPI_REQUEST_DELETE) || + (requestType == HTTPAPI_REQUEST_PATCH)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_050: [ If there is a content in the response, the HTTPAPI_ExecuteRequest shall copy it in the responseContent buffer. ]*/ +//Note: This function assumes that "Host:" and "Content-Length:" headers are setup +// by the caller of HTTPAPI_ExecuteRequest() (which is true for httptransport.c). +HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, + HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content, + size_t contentLength, unsigned int* statusCode, + HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent) +{ + HTTPAPI_RESULT result = HTTPAPI_ERROR; + size_t headersCount; + size_t bodyLength = 0; + bool chunked = false; + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; + + /*Codes_SRS_HTTPAPI_COMPACT_21_034: [ If there is no previous connection, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_039: [ If the relativePath is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_041: [ If the httpHeadersHandle is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_053: [ The HTTPAPI_ExecuteRequest shall produce a set of http header to send to the host. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_040: [ The request shall contain the http header provided in httpHeadersHandle parameter. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_054: [ If Http header maker cannot provide the number of headers, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + if (http_instance == NULL || + relativePath == NULL || + httpHeadersHandle == NULL || + !validRequestType(requestType) || + HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK) + { + result = HTTPAPI_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ + else if ((result = OpenXIOConnection(http_instance)) != HTTPAPI_OK) + { + LogError("Open HTTP connection failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ + else if ((result = SendHeadsToXIO(http_instance, requestType, relativePath, httpHeadersHandle, headersCount)) != HTTPAPI_OK) + { + LogError("Send heads to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ + else if ((result = SendContentToXIO(http_instance, content, contentLength)) != HTTPAPI_OK) + { + LogError("Send content to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_073: [ The message received by the HTTPAPI_ExecuteRequest shall starts with a valid header. ]*/ + else if ((result = ReceiveHeaderFromXIO(http_instance, statusCode)) != HTTPAPI_OK) + { + LogError("Receive header from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_074: [ After the header, the message received by the HTTPAPI_ExecuteRequest can contain addition information about the content. ]*/ + else if ((result = ReceiveContentInfoFromXIO(http_instance, responseHeadersHandle, &bodyLength, &chunked)) != HTTPAPI_OK) + { + LogError("Receive content information from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_075: [ The message received by the HTTPAPI_ExecuteRequest can contain a body with the message content. ]*/ + else if ((result = ReadHTTPResponseBodyFromXIO(http_instance, bodyLength, chunked, responseContent)) != HTTPAPI_OK) + { + LogError("Read HTTP response body from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + + conn_receive_discard_buffer(http_instance); + + + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_056: [ The HTTPAPI_SetOption shall change the HTTP options. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_057: [ The HTTPAPI_SetOption shall receive a handle that identiry the HTTP connection. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_058: [ The HTTPAPI_SetOption shall receive the option as a pair optionName/value. ]*/ +HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value) +{ + HTTPAPI_RESULT result; + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; + + if ( + (http_instance == NULL) || + (optionName == NULL) || + (value == NULL) + ) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_059: [ If the handle is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_060: [ If the optionName is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_061: [ If the value is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; + } + else if (strcmp("TrustedCerts", optionName) == 0) + { + int len; + + if (http_instance->certificate) + { + free(http_instance->certificate); + } + + len = (int)strlen((char*)value); + http_instance->certificate = (char*)malloc((len + 1) * sizeof(char)); + if (http_instance->certificate == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + LogInfo("unable to allocate memory for the certificate in HTTPAPI_SetOption"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(http_instance->certificate, (const char*)value); + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) + { + int len; + if (http_instance->x509ClientCertificate) + { + free(http_instance->x509ClientCertificate); + } + + len = (int)strlen((char*)value); + http_instance->x509ClientCertificate = (char*)malloc((len + 1) * sizeof(char)); + if (http_instance->x509ClientCertificate == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + LogInfo("unable to allocate memory for the client certificate in HTTPAPI_SetOption"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(http_instance->x509ClientCertificate, (const char*)value); + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) + { + int len; + if (http_instance->x509ClientPrivateKey) + { + free(http_instance->x509ClientPrivateKey); + } + + len = (int)strlen((char*)value); + http_instance->x509ClientPrivateKey = (char*)malloc((len + 1) * sizeof(char)); + if (http_instance->x509ClientPrivateKey == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + LogInfo("unable to allocate memory for the client private key in HTTPAPI_SetOption"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(http_instance->x509ClientPrivateKey, (const char*)value); + result = HTTPAPI_OK; + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_063: [ If the HTTP do not support the optionName, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; + LogInfo("unknown option %s", optionName); + } + return result; +} + +/*Codes_SRS_HTTPAPI_COMPACT_21_065: [ The HTTPAPI_CloneOption shall provide the means to clone the HTTP option. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_066: [ The HTTPAPI_CloneOption shall return a clone of the value identified by the optionName. ]*/ +HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue) +{ + HTTPAPI_RESULT result; + size_t certLen; + char* tempCert; + + if ( + (optionName == NULL) || + (value == NULL) || + (savedValue == NULL) + ) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_067: [ If the optionName is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_068: [ If the value is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_069: [ If the savedValue is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; + } + else if (strcmp("TrustedCerts", optionName) == 0) + { + certLen = strlen((const char*)value); + tempCert = (char*)malloc((certLen + 1) * sizeof(char)); + if (tempCert == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); + *savedValue = tempCert; + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) + { + certLen = strlen((const char*)value); + tempCert = (char*)malloc((certLen + 1) * sizeof(char)); + if (tempCert == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); + *savedValue = tempCert; + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) + { + certLen = strlen((const char*)value); + tempCert = (char*)malloc((certLen + 1) * sizeof(char)); + if (tempCert == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); + *savedValue = tempCert; + result = HTTPAPI_OK; + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_071: [ If the HTTP do not support the optionName, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; + LogInfo("unknown option %s", optionName); + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/lock_rtx_mbed.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/xlogging.h" +#include "rtos.h" + + +LOCK_HANDLE Lock_Init(void) +{ + /* Codes_SRS_LOCK_10_002: [Lock_Init on success shall return a valid lock handle which should be a non NULL value] */ + /* Codes_SRS_LOCK_10_003: [Lock_Init on error shall return NULL] */ + Mutex* result = new Mutex(); + if (result == NULL) + { + LogError("Failed to instantiate a new Mutex object."); + } + + return (LOCK_HANDLE)result; +} + +LOCK_RESULT Lock(LOCK_HANDLE handle) +{ + LOCK_RESULT result; + if (handle == NULL) + { + /* Codes_SRS_LOCK_10_007: [Lock on NULL handle passed returns LOCK_ERROR] */ + LogError("Invalid argument; handle is NULL."); + result = LOCK_ERROR; + } + else + { + Mutex* lock_mtx = (Mutex*)handle; + if (lock_mtx->lock() == osOK) + { + /* Codes_SRS_LOCK_10_005: [Lock on success shall return LOCK_OK] */ + result = LOCK_OK; + } + else + { + /* Codes_SRS_LOCK_10_006: [Lock on error shall return LOCK_ERROR] */ + LogError("Mutex(%p)->lock() failed.", handle); + result = LOCK_ERROR; + } + } + + return result; +} + +LOCK_RESULT Unlock(LOCK_HANDLE handle) +{ + LOCK_RESULT result; + if (handle == NULL) + { + /* Codes_SRS_LOCK_10_007: [Unlock on NULL handle passed returns LOCK_ERROR] */ + LogError("Invalid argument; handle is NULL."); + result = LOCK_ERROR; + } + else + { + Mutex* lock_mtx = (Mutex*)handle; + if (lock_mtx->unlock() == osOK) + { + /* Codes_SRS_LOCK_10_009: [Unlock on success shall return LOCK_OK] */ + result = LOCK_OK; + } + else + { + /* Codes_SRS_LOCK_10_010: [Unlock on error shall return LOCK_ERROR] */ + LogError("Mutex(%p)->unlock() failed.", handle); + result = LOCK_ERROR; + } + } + + return result; +} + +LOCK_RESULT Lock_Deinit(LOCK_HANDLE handle) +{ + LOCK_RESULT result; + if (NULL == handle) + { + /* Codes_SRS_LOCK_10_007: [Lock_Deinit on NULL handle passed returns LOCK_ERROR] */ + LogError("Invalid argument; handle is NULL."); + result = LOCK_ERROR; + } + else + { + /* Codes_SRS_LOCK_10_012: [Lock_Deinit frees the memory pointed by handle] */ + Mutex* lock_mtx = (Mutex*)handle; + delete lock_mtx; + result = LOCK_OK; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/platform_mbed.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/platform.h" +#include "NTPClient.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/tlsio_wolfssl.h" +#include "azure_c_shared_utility/tlsio_mbedtls.h" +#include "easy-connect.h" + + +extern NetworkInterface *network; + +int setupRealTime(void) +{ + int result; + + NTPClient ntp(network); + if (ntp.setTime("0.pool.ntp.org") != 0) + { + result = __LINE__; + } + else + { + result = 0; + } + + return result; +} + +int platform_init(void) +{ + int result; + + while(true) + { + network = easy_connect(true); /* has 1 argument, enable_logging (pass in true to log to serial port) */ + if (!network) + { + printf("Connecting to the network failed... See serial output.\r\n"); + return 1; + } + else + { + printf("Wifi connected successfully\n"); + break; + } + } + + if(setupRealTime() != 0) + { + result = __LINE__; + } + else + { + result = 0; + } + + return result; +} + +const IO_INTERFACE_DESCRIPTION* platform_get_default_tlsio(void) +{ + return tlsio_mbedtls_get_interface_description(); +} + +void platform_deinit(void) +{ +} + +STRING_HANDLE platform_get_platform_info(void) +{ + return STRING_construct("(mbed)"); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/socketio_mbed.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,460 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include "azure_c_shared_utility/socketio.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/tcpsocketconnection_c.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +#define UNABLE_TO_COMPLETE -2 +#define MBED_RECEIVE_BYTES_VALUE 128 + +typedef enum IO_STATE_TAG +{ + IO_STATE_CLOSED, + IO_STATE_OPENING, + IO_STATE_OPEN, + IO_STATE_CLOSING, + IO_STATE_ERROR +} IO_STATE; + +typedef struct PENDING_SOCKET_IO_TAG +{ + unsigned char* bytes; + size_t size; + ON_SEND_COMPLETE on_send_complete; + void* callback_context; + SINGLYLINKEDLIST_HANDLE pending_io_list; +} PENDING_SOCKET_IO; + +typedef struct SOCKET_IO_INSTANCE_TAG +{ + TCPSOCKETCONNECTION_HANDLE tcp_socket_connection; + ON_BYTES_RECEIVED on_bytes_received; + ON_IO_ERROR on_io_error; + void* on_bytes_received_context; + void* on_io_error_context; + char* hostname; + int port; + IO_STATE io_state; + SINGLYLINKEDLIST_HANDLE pending_io_list; +} SOCKET_IO_INSTANCE; + +/*this function will clone an option given by name and value*/ +static void* socketio_CloneOption(const char* name, const void* value) +{ + (void)name; + (void)value; + return NULL; +} + +/*this function destroys an option previously created*/ +static void socketio_DestroyOption(const char* name, const void* value) +{ + (void)name; + (void)value; +} + +static OPTIONHANDLER_HANDLE socketio_retrieveoptions(CONCRETE_IO_HANDLE socket_io) +{ + OPTIONHANDLER_HANDLE result; + (void)socket_io; + result = OptionHandler_Create(socketio_CloneOption, socketio_DestroyOption, socketio_setoption); + if (result == NULL) + { + /*return as is*/ + } + else + { + /*insert here work to add the options to "result" handle*/ + } + return result; +} + +static const IO_INTERFACE_DESCRIPTION socket_io_interface_description = +{ + socketio_retrieveoptions, + socketio_create, + socketio_destroy, + socketio_open, + socketio_close, + socketio_send, + socketio_dowork, + socketio_setoption +}; + +static void indicate_error(SOCKET_IO_INSTANCE* socket_io_instance) +{ + if (socket_io_instance->on_io_error != NULL) + { + socket_io_instance->on_io_error(socket_io_instance->on_io_error_context); + } +} + +static int add_pending_io(SOCKET_IO_INSTANCE* socket_io_instance, const unsigned char* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + PENDING_SOCKET_IO* pending_socket_io = (PENDING_SOCKET_IO*)malloc(sizeof(PENDING_SOCKET_IO)); + if (pending_socket_io == NULL) + { + result = __FAILURE__; + } + else + { + pending_socket_io->bytes = (unsigned char*)malloc(size); + if (pending_socket_io->bytes == NULL) + { + free(pending_socket_io); + result = __FAILURE__; + } + else + { + pending_socket_io->size = size; + pending_socket_io->on_send_complete = on_send_complete; + pending_socket_io->callback_context = callback_context; + pending_socket_io->pending_io_list = socket_io_instance->pending_io_list; + (void)memcpy(pending_socket_io->bytes, buffer, size); + + if (singlylinkedlist_add(socket_io_instance->pending_io_list, pending_socket_io) == NULL) + { + free(pending_socket_io->bytes); + free(pending_socket_io); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +CONCRETE_IO_HANDLE socketio_create(void* io_create_parameters) +{ + SOCKETIO_CONFIG* socket_io_config = io_create_parameters; + SOCKET_IO_INSTANCE* result; + + if (socket_io_config == NULL) + { + result = NULL; + } + else + { + result = malloc(sizeof(SOCKET_IO_INSTANCE)); + if (result != NULL) + { + result->pending_io_list = singlylinkedlist_create(); + if (result->pending_io_list == NULL) + { + free(result); + result = NULL; + } + else + { + result->hostname = (char*)malloc(strlen(socket_io_config->hostname) + 1); + if (result->hostname == NULL) + { + singlylinkedlist_destroy(result->pending_io_list); + free(result); + result = NULL; + } + else + { + strcpy(result->hostname, socket_io_config->hostname); + result->port = socket_io_config->port; + result->on_bytes_received = NULL; + result->on_io_error = NULL; + result->on_bytes_received_context = NULL; + result->on_io_error_context = NULL; + result->io_state = IO_STATE_CLOSED; + result->tcp_socket_connection = NULL; + } + } + } + } + + return result; +} + +void socketio_destroy(CONCRETE_IO_HANDLE socket_io) +{ + if (socket_io != NULL) + { + SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io; + + tcpsocketconnection_destroy(socket_io_instance->tcp_socket_connection); + + /* clear all pending IOs */ + LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(socket_io_instance->pending_io_list); + while (first_pending_io != NULL) + { + PENDING_SOCKET_IO* pending_socket_io = (PENDING_SOCKET_IO*)singlylinkedlist_item_get_value(first_pending_io); + if (pending_socket_io != NULL) + { + free(pending_socket_io->bytes); + free(pending_socket_io); + } + + (void)singlylinkedlist_remove(socket_io_instance->pending_io_list, first_pending_io); + first_pending_io = singlylinkedlist_get_head_item(socket_io_instance->pending_io_list); + } + + singlylinkedlist_destroy(socket_io_instance->pending_io_list); + free(socket_io_instance->hostname); + free(socket_io); + } +} + +int socketio_open(CONCRETE_IO_HANDLE socket_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result; + + SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io; + if (socket_io == NULL) + { + result = __FAILURE__; + } + else + { + socket_io_instance->tcp_socket_connection = tcpsocketconnection_create(); + if (socket_io_instance->tcp_socket_connection == NULL) + { + result = __FAILURE__; + } + else + { + if (tcpsocketconnection_connect(socket_io_instance->tcp_socket_connection, socket_io_instance->hostname, socket_io_instance->port) != 0) + { + tcpsocketconnection_destroy(socket_io_instance->tcp_socket_connection); + socket_io_instance->tcp_socket_connection = NULL; + result = __FAILURE__; + } + else + { + tcpsocketconnection_set_blocking(socket_io_instance->tcp_socket_connection, false, 0); + + socket_io_instance->on_bytes_received = on_bytes_received; + socket_io_instance->on_bytes_received_context = on_bytes_received_context; + + socket_io_instance->on_io_error = on_io_error; + socket_io_instance->on_io_error_context = on_io_error_context; + + socket_io_instance->io_state = IO_STATE_OPEN; + + result = 0; + } + } + } + + if (on_io_open_complete != NULL) + { + on_io_open_complete(on_io_open_complete_context, result == 0 ? IO_OPEN_OK : IO_OPEN_ERROR); + } + + return result; +} + +int socketio_close(CONCRETE_IO_HANDLE socket_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result = 0; + + if (socket_io == NULL) + { + result = __FAILURE__; + } + else + { + SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io; + + if ((socket_io_instance->io_state == IO_STATE_CLOSED) || + (socket_io_instance->io_state == IO_STATE_CLOSING)) + { + result = __FAILURE__; + } + else + { + tcpsocketconnection_close(socket_io_instance->tcp_socket_connection); + socket_io_instance->tcp_socket_connection = NULL; + socket_io_instance->io_state = IO_STATE_CLOSED; + + if (on_io_close_complete != NULL) + { + on_io_close_complete(callback_context); + } + + result = 0; + } + } + + return result; +} + +int socketio_send(CONCRETE_IO_HANDLE socket_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + if ((socket_io == NULL) || + (buffer == NULL) || + (size == 0)) + { + /* Invalid arguments */ + result = __FAILURE__; + } + else + { + SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io; + if (socket_io_instance->io_state != IO_STATE_OPEN) + { + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(socket_io_instance->pending_io_list); + if (first_pending_io != NULL) + { + if (add_pending_io(socket_io_instance, buffer, size, on_send_complete, callback_context) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + int send_result = tcpsocketconnection_send(socket_io_instance->tcp_socket_connection, buffer, size); + if (send_result != size) + { + if (send_result < 0) + { + send_result = 0; + } + + /* queue data */ + if (add_pending_io(socket_io_instance, (unsigned char*)buffer + send_result, size - send_result, on_send_complete, callback_context) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + if (on_send_complete != NULL) + { + on_send_complete(callback_context, IO_SEND_OK); + } + + result = 0; + } + } + } + } + + return result; +} + +void socketio_dowork(CONCRETE_IO_HANDLE socket_io) +{ + if (socket_io != NULL) + { + SOCKET_IO_INSTANCE* socket_io_instance = (SOCKET_IO_INSTANCE*)socket_io; + if (socket_io_instance->io_state == IO_STATE_OPEN) + { + int received = 1; + + LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(socket_io_instance->pending_io_list); + while (first_pending_io != NULL) + { + PENDING_SOCKET_IO* pending_socket_io = (PENDING_SOCKET_IO*)singlylinkedlist_item_get_value(first_pending_io); + if (pending_socket_io == NULL) + { + socket_io_instance->io_state = IO_STATE_ERROR; + indicate_error(socket_io_instance); + break; + } + + int send_result = tcpsocketconnection_send(socket_io_instance->tcp_socket_connection, (const char*)pending_socket_io->bytes, pending_socket_io->size); + if (send_result != pending_socket_io->size) + { + if (send_result < 0) + { + if (send_result < UNABLE_TO_COMPLETE) + { + // Bad error. Indicate as much. + socket_io_instance->io_state = IO_STATE_ERROR; + indicate_error(socket_io_instance); + } + break; + } + else + { + /* send something, wait for the rest */ + (void)memmove(pending_socket_io->bytes, pending_socket_io->bytes + send_result, pending_socket_io->size - send_result); + } + } + else + { + if (pending_socket_io->on_send_complete != NULL) + { + pending_socket_io->on_send_complete(pending_socket_io->callback_context, IO_SEND_OK); + } + + free(pending_socket_io->bytes); + free(pending_socket_io); + if (singlylinkedlist_remove(socket_io_instance->pending_io_list, first_pending_io) != 0) + { + socket_io_instance->io_state = IO_STATE_ERROR; + indicate_error(socket_io_instance); + } + } + + first_pending_io = singlylinkedlist_get_head_item(socket_io_instance->pending_io_list); + } + + while (received > 0) + { + unsigned char* recv_bytes = malloc(MBED_RECEIVE_BYTES_VALUE); + if (recv_bytes == NULL) + { + LogError("Socketio_Failure: NULL allocating input buffer."); + indicate_error(socket_io_instance); + } + else + { + received = tcpsocketconnection_receive(socket_io_instance->tcp_socket_connection, (char*)recv_bytes, MBED_RECEIVE_BYTES_VALUE); + if (received > 0) + { + if (socket_io_instance->on_bytes_received != NULL) + { + /* explictly ignoring here the result of the callback */ + (void)socket_io_instance->on_bytes_received(socket_io_instance->on_bytes_received_context, recv_bytes, received); + } + } + free(recv_bytes); + } + } + } + } +} + +int socketio_setoption(CONCRETE_IO_HANDLE socket_io, const char* optionName, const void* value) +{ + /* Not implementing any options */ + return 0; +} + +const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void) +{ + return &socket_io_interface_description; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/tcpsocketconnection_c.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "mbed.h" + +#include <stddef.h> +#include "TCPSocket.h" +#include "azure_c_shared_utility/tcpsocketconnection_c.h" + +extern NetworkInterface *network; +bool tcpsocketconnection_isConnected = false; + +TCPSOCKETCONNECTION_HANDLE tcpsocketconnection_create(void) +{ + TCPSocket* tcpSocket = new TCPSocket(); + tcpSocket->open(network); + return tcpSocket; +} + +void tcpsocketconnection_set_blocking(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, bool blocking, unsigned int timeout) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + tsc->set_blocking(blocking); + tsc->set_timeout(timeout); +} + +void tcpsocketconnection_destroy(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle) +{ + delete (TCPSocket*)tcpSocketConnectionHandle; +} + +int tcpsocketconnection_connect(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* host, const int port) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + int ret = tsc->connect(host, port); + tcpsocketconnection_isConnected = true; + return ret; +} + +bool tcpsocketconnection_is_connected(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + return tcpsocketconnection_isConnected; +} + +void tcpsocketconnection_close(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + tcpsocketconnection_isConnected = false; + tsc->close(); +} + +int tcpsocketconnection_send(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* data, int length) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + int ret = tsc->send((char*)data, length); + return ret; +} + +int tcpsocketconnection_send_all(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* data, int length) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + return tsc->send((char*)data, length); +} + +int tcpsocketconnection_receive(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, char* data, int length) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + int ret = tsc->recv(data, length); + return ret; +} + +int tcpsocketconnection_receive_all(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, char* data, int length) +{ + TCPSocket* tsc = (TCPSocket*)tcpSocketConnectionHandle; + return tsc->recv(data, length); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/threadapi_rtx_mbed.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/xlogging.h" +#include "rtos.h" + +DEFINE_ENUM_STRINGS(THREADAPI_RESULT, THREADAPI_RESULT_VALUES); + +#define MAX_THREADS 4 +#define STACK_SIZE 0x4000 + +typedef struct _thread +{ + Thread* thrd; + osThreadId id; + Queue<int, 1> result; +} mbedThread; +static mbedThread threads[MAX_THREADS] = { 0 }; + +typedef struct _create_param +{ + THREAD_START_FUNC func; + const void* arg; + mbedThread *p_thread; +} create_param; + +static void thread_wrapper(const void* createParamArg) +{ + const create_param* p = (const create_param*)createParamArg; + p->p_thread->id = Thread::gettid(); + (*(p->func))((void*)p->arg); + free((void*)p); +} + +THREADAPI_RESULT ThreadAPI_Create(THREAD_HANDLE* threadHandle, THREAD_START_FUNC func, void* arg) +{ + THREADAPI_RESULT result; + if ((threadHandle == NULL) || + (func == NULL)) + { + result = THREADAPI_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(THREADAPI_RESULT, result)); + } + else + { + size_t slot; + for (slot = 0; slot < MAX_THREADS; slot++) + { + if (threads[slot].id == NULL) + break; + } + + if (slot < MAX_THREADS) + { + create_param* param = (create_param*)malloc(sizeof(create_param)); + if (param != NULL) + { + param->func = func; + param->arg = arg; + param->p_thread = threads + slot; + threads[slot].thrd = new Thread(thread_wrapper, param, osPriorityNormal, STACK_SIZE); + *threadHandle = (THREAD_HANDLE)(threads + slot); + result = THREADAPI_OK; + } + else + { + result = THREADAPI_NO_MEMORY; + LogError("(result = %s)", ENUM_TO_STRING(THREADAPI_RESULT, result)); + } + } + else + { + result = THREADAPI_NO_MEMORY; + LogError("(result = %s)", ENUM_TO_STRING(THREADAPI_RESULT, result)); + } + } + + return result; +} + +THREADAPI_RESULT ThreadAPI_Join(THREAD_HANDLE thr, int *res) +{ + THREADAPI_RESULT result = THREADAPI_OK; + mbedThread* p = (mbedThread*)thr; + if (p) + { + osEvent evt = p->result.get(); + if (evt.status == osEventMessage) { + Thread* t = p->thrd; + if (res) + { + *res = (int)evt.value.p; + } + (void)t->terminate(); + } + else + { + result = THREADAPI_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(THREADAPI_RESULT, result)); + } + } + else + { + result = THREADAPI_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(THREADAPI_RESULT, result)); + } + return result; +} + +void ThreadAPI_Exit(int res) +{ + mbedThread* p; + for (p = threads; p < &threads[MAX_THREADS]; p++) + { + if (p->id == Thread::gettid()) + { + p->result.put((int*)res); + break; + } + } +} + +void ThreadAPI_Sleep(unsigned int millisec) +{ + // + // The timer on mbed seems to wrap around 65 seconds. Hmmm. + // So we will do our waits in increments of 30 seconds. + // + const int thirtySeconds = 30000; + int numberOfThirtySecondWaits = millisec / thirtySeconds; + int remainderOfThirtySeconds = millisec % thirtySeconds; + int i; + for (i = 1; i <= numberOfThirtySecondWaits; i++) + { + Thread::wait(thirtySeconds); + } + Thread::wait(remainderOfThirtySeconds); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/tickcounter_mbed.cpp Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "mbed.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/tickcounter.h" + +class TICK_COUNTER_INSTANCE_TAG +{ +public: + clock_t last_clock_value; + tickcounter_ms_t current_ms; +}; + +TICK_COUNTER_HANDLE tickcounter_create(void) +{ + TICK_COUNTER_INSTANCE_TAG* result; + result = new TICK_COUNTER_INSTANCE_TAG(); + result->last_clock_value = clock(); + result->current_ms = result->last_clock_value * 1000 / CLOCKS_PER_SEC; + return result; +} + +void tickcounter_destroy(TICK_COUNTER_HANDLE tick_counter) +{ + if (tick_counter != NULL) + { + delete tick_counter; + } +} + +int tickcounter_get_current_ms(TICK_COUNTER_HANDLE tick_counter, tickcounter_ms_t * current_ms) +{ + int result; + if (tick_counter == NULL || current_ms == NULL) + { + result = __FAILURE__; + } + else + { + TICK_COUNTER_INSTANCE_TAG* tick_counter_instance = (TICK_COUNTER_INSTANCE_TAG*)tick_counter; + + clock_t clock_value = clock(); + + tick_counter_instance->current_ms += (clock_value - tick_counter_instance->last_clock_value) * 1000 / CLOCKS_PER_SEC; + tick_counter_instance->last_clock_value = clock_value; + *current_ms = tick_counter_instance->current_ms; + + result = 0; + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/tlsio_mbedtls.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,756 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include "azure_c_shared_utility/tlsio_mbedtls.h" + +// DEPRECATED: the USE_MBED_TLS #define is deprecated. +#ifdef USE_MBED_TLS + +#include <stdlib.h> + +#ifdef TIZENRT +#include "tls/config.h" +#include "tls/debug.h" +#include "tls/ssl.h" +#include "tls/entropy.h" +#include "tls/ctr_drbg.h" +#include "tls/error.h" +#include "tls/certs.h" +#include "tls/entropy_poll.h" +#else +#include "mbedtls/config.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" +#include "mbedtls/entropy_poll.h" +#endif + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/tlsio_mbedtls.h" +#include "azure_c_shared_utility/socketio.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/shared_util_options.h" + +#define OPTION_UNDERLYING_IO_OPTIONS "underlying_io_options" + +// DEPRECATED: debug functions do not belong in the tree. +#define MBED_TLS_DEBUG_ENABLE + +typedef enum TLSIO_STATE_ENUM_TAG +{ + TLSIO_STATE_NOT_OPEN, + TLSIO_STATE_OPENING_UNDERLYING_IO, + TLSIO_STATE_IN_HANDSHAKE, + TLSIO_STATE_OPEN, + TLSIO_STATE_CLOSING, + TLSIO_STATE_ERROR +} TLSIO_STATE_ENUM; + +typedef struct TLS_IO_INSTANCE_TAG +{ + XIO_HANDLE socket_io; + ON_BYTES_RECEIVED on_bytes_received; + ON_IO_OPEN_COMPLETE on_io_open_complete; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + ON_IO_ERROR on_io_error; + void* on_bytes_received_context; + void* on_io_open_complete_context; + void* on_io_close_complete_context; + void* on_io_error_context; + TLSIO_STATE_ENUM tlsio_state; + unsigned char* socket_io_read_bytes; + size_t socket_io_read_byte_count; + ON_SEND_COMPLETE on_send_complete; + void* on_send_complete_callback_context; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config config; + mbedtls_x509_crt trusted_certificates_parsed; + mbedtls_ssl_session ssn; + char* trusted_certificates; +} TLS_IO_INSTANCE; + +static const IO_INTERFACE_DESCRIPTION tlsio_mbedtls_interface_description = +{ + tlsio_mbedtls_retrieveoptions, + tlsio_mbedtls_create, + tlsio_mbedtls_destroy, + tlsio_mbedtls_open, + tlsio_mbedtls_close, + tlsio_mbedtls_send, + tlsio_mbedtls_dowork, + tlsio_mbedtls_setoption +}; + +// DEPRECATED: debug functions do not belong in the tree. +#if defined (MBED_TLS_DEBUG_ENABLE) +void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) +{ + ((void)level); + printf("%s (%d): %s\r\n", file, line, str); +} +#endif + +static void indicate_error(TLS_IO_INSTANCE* tls_io_instance) +{ + if (tls_io_instance->on_io_error != NULL) + { + tls_io_instance->on_io_error(tls_io_instance->on_io_error_context); + } +} + +static void indicate_open_complete(TLS_IO_INSTANCE* tls_io_instance, IO_OPEN_RESULT open_result) +{ + if (tls_io_instance->on_io_open_complete != NULL) + { + tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, open_result); + } +} + +static int decode_ssl_received_bytes(TLS_IO_INSTANCE* tls_io_instance) +{ + int result = 0; + unsigned char buffer[64]; + int rcv_bytes = 1; + + while (rcv_bytes > 0) + { + rcv_bytes = mbedtls_ssl_read(&tls_io_instance->ssl, buffer, sizeof(buffer)); + if (rcv_bytes > 0) + { + if (tls_io_instance->on_bytes_received != NULL) + { + tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, buffer, rcv_bytes); + } + } + } + + return result; +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + int result = 0; + + if (open_result != IO_OPEN_OK) + { + xio_close(tls_io_instance->socket_io, NULL, NULL); + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + } + else + { + tls_io_instance->tlsio_state = TLSIO_STATE_IN_HANDSHAKE; + + do { + result = mbedtls_ssl_handshake(&tls_io_instance->ssl); + } while (result == MBEDTLS_ERR_SSL_WANT_READ || result == MBEDTLS_ERR_SSL_WANT_WRITE); + + if (result == 0) + { + tls_io_instance->tlsio_state = TLSIO_STATE_OPEN; + indicate_open_complete(tls_io_instance, IO_OPEN_OK); + } + else + { + xio_close(tls_io_instance->socket_io, NULL, NULL); + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + } + } +} + +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + unsigned char* new_socket_io_read_bytes = (unsigned char*)realloc(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_byte_count + size); + + if (new_socket_io_read_bytes == NULL) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + } + else + { + tls_io_instance->socket_io_read_bytes = new_socket_io_read_bytes; + (void)memcpy(tls_io_instance->socket_io_read_bytes + tls_io_instance->socket_io_read_byte_count, buffer, size); + tls_io_instance->socket_io_read_byte_count += size; + } +} + +static void on_underlying_io_error(void* context) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + switch (tls_io_instance->tlsio_state) + { + default: + case TLSIO_STATE_NOT_OPEN: + case TLSIO_STATE_ERROR: + break; + + case TLSIO_STATE_OPENING_UNDERLYING_IO: + case TLSIO_STATE_IN_HANDSHAKE: + // Existing socket impls are all synchronous close, and this + // adapter does not yet support async close. + xio_close(tls_io_instance->socket_io, NULL, NULL); + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + break; + + case TLSIO_STATE_OPEN: + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + break; + } +} + +static void on_underlying_io_close_complete_during_close(void* context) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + + if (tls_io_instance->on_io_close_complete != NULL) + { + tls_io_instance->on_io_close_complete(tls_io_instance->on_io_close_complete_context); + } +} + +static int on_io_recv(void *context, unsigned char *buf, size_t sz) +{ + int result; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + unsigned char* new_socket_io_read_bytes; + + while (tls_io_instance->socket_io_read_byte_count == 0) + { + xio_dowork(tls_io_instance->socket_io); + if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) + { + break; + } + } + + result = tls_io_instance->socket_io_read_byte_count; + if (result > sz) + { + result = sz; + } + + if (result > 0) + { + (void)memcpy((void *)buf, tls_io_instance->socket_io_read_bytes, result); + (void)memmove(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_bytes + result, tls_io_instance->socket_io_read_byte_count - result); + tls_io_instance->socket_io_read_byte_count -= result; + if (tls_io_instance->socket_io_read_byte_count > 0) + { + new_socket_io_read_bytes = (unsigned char*)realloc(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_byte_count); + if (new_socket_io_read_bytes != NULL) + { + tls_io_instance->socket_io_read_bytes = new_socket_io_read_bytes; + } + } + else + { + free(tls_io_instance->socket_io_read_bytes); + tls_io_instance->socket_io_read_bytes = NULL; + } + } + + + if ((result == 0) && (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN)) + { + result = MBEDTLS_ERR_SSL_WANT_READ; + } + + return result; +} + +static int on_io_send(void *context, const unsigned char *buf, size_t sz) +{ + int result; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + if (xio_send(tls_io_instance->socket_io, buf, sz, tls_io_instance->on_send_complete, tls_io_instance->on_send_complete_callback_context) != 0) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + result = 0; + } + else + { + result = sz; + } + + return result; +} + +static int tlsio_entropy_poll(void *v, unsigned char *output, size_t len, size_t *olen) +{ + srand(time(NULL)); + char *c = (char*)malloc(len); + memset(c, 0, len); + for (uint16_t i = 0; i < len; i++) { + c[i] = rand() % 256; + } + memmove(output, c, len); + *olen = len; + + free(c); + return(0); +} + +static void mbedtls_init(void *instance, const char *host) { + TLS_IO_INSTANCE *result = (TLS_IO_INSTANCE *)instance; + char *pers = "azure_iot_client"; + + // mbedTLS initialize... + mbedtls_entropy_init(&result->entropy); + mbedtls_ctr_drbg_init(&result->ctr_drbg); + mbedtls_ssl_init(&result->ssl); + mbedtls_ssl_session_init(&result->ssn); + mbedtls_ssl_config_init(&result->config); + mbedtls_x509_crt_init(&result->trusted_certificates_parsed); + mbedtls_entropy_add_source(&result->entropy, tlsio_entropy_poll, NULL, 128, 0); + mbedtls_ctr_drbg_seed(&result->ctr_drbg, mbedtls_entropy_func, &result->entropy, (const unsigned char *)pers, strlen(pers)); + mbedtls_ssl_config_defaults(&result->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + mbedtls_ssl_conf_rng(&result->config, mbedtls_ctr_drbg_random, &result->ctr_drbg); + mbedtls_ssl_conf_authmode(&result->config, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_min_version(&result->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); // v1.2 + mbedtls_ssl_set_bio(&result->ssl, instance, on_io_send, on_io_recv, NULL); + mbedtls_ssl_set_hostname(&result->ssl, host); + mbedtls_ssl_set_session(&result->ssl, &result->ssn); + + // DEPRECATED: debug functions do not belong in the tree. +#if defined (MBED_TLS_DEBUG_ENABLE) + mbedtls_ssl_conf_dbg(&result->config, mbedtls_debug, stdout); + mbedtls_debug_set_threshold(1); +#endif + + mbedtls_ssl_setup(&result->ssl, &result->config); +} + +CONCRETE_IO_HANDLE tlsio_mbedtls_create(void* io_create_parameters) +{ + TLSIO_CONFIG* tls_io_config = io_create_parameters; + TLS_IO_INSTANCE* result; + + if (tls_io_config == NULL) + { + LogError("NULL tls_io_config"); + result = NULL; + } + else + { + result = malloc(sizeof(TLS_IO_INSTANCE)); + if (result != NULL) + { + SOCKETIO_CONFIG socketio_config; + const IO_INTERFACE_DESCRIPTION* underlying_io_interface; + void* io_interface_parameters; + + if (tls_io_config->underlying_io_interface != NULL) + { + underlying_io_interface = tls_io_config->underlying_io_interface; + io_interface_parameters = tls_io_config->underlying_io_parameters; + } + else + { + socketio_config.hostname = tls_io_config->hostname; + socketio_config.port = tls_io_config->port; + socketio_config.accepted_socket = NULL; + + underlying_io_interface = socketio_get_interface_description(); + io_interface_parameters = &socketio_config; + } + + if (underlying_io_interface == NULL) + { + free(result); + result = NULL; + LogError("Failed getting socket IO interface description."); + } + else + { + result->on_bytes_received = NULL; + result->on_bytes_received_context = NULL; + + result->on_io_open_complete = NULL; + result->on_io_open_complete_context = NULL; + + result->on_io_close_complete = NULL; + result->on_io_close_complete_context = NULL; + + result->on_io_error = NULL; + result->on_io_error_context = NULL; + + result->trusted_certificates = NULL; + + result->socket_io = xio_create(underlying_io_interface, io_interface_parameters); + if (result->socket_io == NULL) + { + LogError("socket xio create failed"); + free(result); + result = NULL; + } + else + { + result->socket_io_read_bytes = NULL; + result->socket_io_read_byte_count = 0; + result->on_send_complete = NULL; + result->on_send_complete_callback_context = NULL; + + // mbeTLS initialize + mbedtls_init((void *)result, tls_io_config->hostname); + result->tlsio_state = TLSIO_STATE_NOT_OPEN; + } + } + } + } + + return result; +} + +void tlsio_mbedtls_destroy(CONCRETE_IO_HANDLE tls_io) +{ + if (tls_io != NULL) + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + // mbedTLS cleanup... + mbedtls_ssl_close_notify(&tls_io_instance->ssl); + mbedtls_ssl_free(&tls_io_instance->ssl); + mbedtls_ssl_config_free(&tls_io_instance->config); + mbedtls_x509_crt_free(&tls_io_instance->trusted_certificates_parsed); + mbedtls_ctr_drbg_free(&tls_io_instance->ctr_drbg); + mbedtls_entropy_free(&tls_io_instance->entropy); + + xio_close(tls_io_instance->socket_io, NULL, NULL); + + if (tls_io_instance->socket_io_read_bytes != NULL) + { + free(tls_io_instance->socket_io_read_bytes); + } + + xio_destroy(tls_io_instance->socket_io); + if (tls_io_instance->trusted_certificates != NULL) + { + free(tls_io_instance->trusted_certificates); + } + free(tls_io); + } +} + +int tlsio_mbedtls_open(CONCRETE_IO_HANDLE tls_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result = 0; + + if (tls_io == NULL) + { + LogError("NULL tls_io"); + result = __FAILURE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_NOT_OPEN) + { + LogError("IO should not be open: %d\n", tls_io_instance->tlsio_state); + result = __FAILURE__; + } + else + { + tls_io_instance->on_bytes_received = on_bytes_received; + tls_io_instance->on_bytes_received_context = on_bytes_received_context; + + tls_io_instance->on_io_open_complete = on_io_open_complete; + tls_io_instance->on_io_open_complete_context = on_io_open_complete_context; + + tls_io_instance->on_io_error = on_io_error; + tls_io_instance->on_io_error_context = on_io_error_context; + + tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_UNDERLYING_IO; + + if (xio_open(tls_io_instance->socket_io, on_underlying_io_open_complete, tls_io_instance, on_underlying_io_bytes_received, tls_io_instance, on_underlying_io_error, tls_io_instance) != 0) + { + LogError("Underlying IO open failed"); + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +int tlsio_mbedtls_close(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result = 0; + + if (tls_io == NULL) + { + result = __FAILURE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if ((tls_io_instance->tlsio_state == TLSIO_STATE_NOT_OPEN) || + (tls_io_instance->tlsio_state == TLSIO_STATE_CLOSING)) + { + result = __FAILURE__; + } + else + { + tls_io_instance->tlsio_state = TLSIO_STATE_CLOSING; + tls_io_instance->on_io_close_complete = on_io_close_complete; + tls_io_instance->on_io_close_complete_context = callback_context; + + if (xio_close(tls_io_instance->socket_io, + on_underlying_io_close_complete_during_close, tls_io_instance) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +int tlsio_mbedtls_send(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + if (tls_io == NULL) + { + result = __FAILURE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN) + { + result = __FAILURE__; + } + else + { + tls_io_instance->on_send_complete = on_send_complete; + tls_io_instance->on_send_complete_callback_context = callback_context; + + int res = mbedtls_ssl_write(&tls_io_instance->ssl, buffer, size); + if (res != size) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +void tlsio_mbedtls_dowork(CONCRETE_IO_HANDLE tls_io) +{ + if (tls_io != NULL) + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if ((tls_io_instance->tlsio_state != TLSIO_STATE_NOT_OPEN) && + (tls_io_instance->tlsio_state != TLSIO_STATE_ERROR)) + { + decode_ssl_received_bytes(tls_io_instance); + xio_dowork(tls_io_instance->socket_io); + } + } +} + +const IO_INTERFACE_DESCRIPTION* tlsio_mbedtls_get_interface_description(void) +{ + return &tlsio_mbedtls_interface_description; +} + +/*this function will clone an option given by name and value*/ +static void* tlsio_mbedtls_CloneOption(const char* name, const void* value) +{ + void* result; + if ( + (name == NULL) || (value == NULL) + ) + { + LogError("invalid parameter detected: const char* name=%p, const void* value=%p", name, value); + result = NULL; + } + else + { + if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0) + { + result = (void*)value; + } + else if (strcmp(name, OPTION_TRUSTED_CERT) == 0) + { + if (mallocAndStrcpy_s((char**)&result, value) != 0) + { + LogError("unable to mallocAndStrcpy_s TrustedCerts value"); + result = NULL; + } + else + { + /*return as is*/ + } + } + else + { + LogError("not handled option : %s", name); + result = NULL; + } + } + return result; +} + +/*this function destroys an option previously created*/ +static void tlsio_mbedtls_DestroyOption(const char* name, const void* value) +{ + /*since all options for this layer are actually string copies., disposing of one is just calling free*/ + if (name == NULL || value == NULL) + { + LogError("invalid parameter detected: const char* name=%p, const void* value=%p", name, value); + } + else + { + if (strcmp(name, OPTION_TRUSTED_CERT) == 0) + { + free((void*)value); + } + else if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0) + { + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + LogError("not handled option : %s", name); + } + } +} + +OPTIONHANDLER_HANDLE tlsio_mbedtls_retrieveoptions(CONCRETE_IO_HANDLE handle) +{ + OPTIONHANDLER_HANDLE result; + if (handle == NULL) + { + LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle=%p", handle); + result = NULL; + } + else + { + result = OptionHandler_Create(tlsio_mbedtls_CloneOption, tlsio_mbedtls_DestroyOption, tlsio_mbedtls_setoption); + if (result == NULL) + { + LogError("unable to OptionHandler_Create"); + /*return as is*/ + } + else + { + /*this layer cares about the certificates*/ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)handle; + OPTIONHANDLER_HANDLE underlying_io_options; + + if ((underlying_io_options = xio_retrieveoptions(tls_io_instance->socket_io)) == NULL || + OptionHandler_AddOption(result, OPTION_UNDERLYING_IO_OPTIONS, underlying_io_options) != OPTIONHANDLER_OK) + { + LogError("unable to save underlying_io options"); + OptionHandler_Destroy(underlying_io_options); + OptionHandler_Destroy(result); + result = NULL; + } + else if (tls_io_instance->trusted_certificates != NULL && + OptionHandler_AddOption(result, OPTION_TRUSTED_CERT, tls_io_instance->trusted_certificates) != OPTIONHANDLER_OK) + { + LogError("unable to save TrustedCerts option"); + OptionHandler_Destroy(result); + result = NULL; + } + else + { + /*all is fine, all interesting options have been saved*/ + /*return as is*/ + } + } + } + return result; +} + +int tlsio_mbedtls_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value) +{ + int result; + + if (tls_io == NULL || optionName == NULL) + { + result = __FAILURE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) + { + if (tls_io_instance->trusted_certificates != NULL) + { + // Free the memory if it has been previously allocated + free(tls_io_instance->trusted_certificates); + tls_io_instance->trusted_certificates = NULL; + } + if (mallocAndStrcpy_s(&tls_io_instance->trusted_certificates, (const char*)value) != 0) + { + LogError("unable to mallocAndStrcpy_s"); + result = __FAILURE__; + } + else + { + int parse_result = mbedtls_x509_crt_parse(&tls_io_instance->trusted_certificates_parsed, (const unsigned char *)value, (int)(strlen(value) + 1)); + if (parse_result != 0) + { + LogInfo("Malformed pem certificate"); + result = __FAILURE__; + } + else + { + mbedtls_ssl_conf_ca_chain(&tls_io_instance->config, &tls_io_instance->trusted_certificates_parsed, NULL); + result = 0; + } + } + } + else + { + // tls_io_instance->socket_io is never NULL + result = xio_setoption(tls_io_instance->socket_io, optionName, value); + } + } + + return result; +} + +// DEPRECATED: the USE_MBED_TLS #define is deprecated. +#endif // USE_MBED_TLS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/adapters/uniqueid_stub.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/xlogging.h" +#include <time.h> + +DEFINE_ENUM_STRINGS(UNIQUEID_RESULT, UNIQUEID_RESULT_VALUES); + +static const char tochar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; +static void generate128BitUUID(unsigned char* arrayOfByte) +{ + size_t arrayIndex; + + for (arrayIndex = 0; arrayIndex < 16; arrayIndex++) + { + arrayOfByte[arrayIndex] = (unsigned char)rand(); + } + + // + // Stick in the version field for random uuid. + // + arrayOfByte[7] &= 0x0f; //clear the bit field + arrayOfByte[7] |= 0x40; //set the ones we care about + + // + // Stick in the variant field for the random uuid. + // + arrayOfByte[8] &= 0xf3; // Clear + arrayOfByte[8] |= 0x08; // Set + +} + +// TODO: The User will need to call srand before calling this function +UNIQUEID_RESULT UniqueId_Generate(char* uid, size_t len) +{ + UNIQUEID_RESULT result; + unsigned char arrayOfChar[16]; + + /* Codes_SRS_UNIQUEID_07_002: [If uid is NULL then UniqueId_Generate shall return UNIQUEID_INVALID_ARG] */ + /* Codes_SRS_UNIQUEID_07_003: [If len is less then 37 then UniqueId_Generate shall return UNIQUEID_INVALID_ARG] */ + if (uid == NULL || len < 37) + { + result = UNIQUEID_INVALID_ARG; + LogError("Buffer Size is Null or length is less then 37 bytes"); + } + else + { + size_t arrayIndex; + size_t shiftCount; + size_t characterPosition = 0; + + /* Codes_SRS_UNIQUEID_07_001: [UniqueId_Generate shall create a unique Id 36 character long string.] */ + generate128BitUUID(arrayOfChar); + for (arrayIndex = 0; arrayIndex < 16; arrayIndex++) + { + for (shiftCount = 0; shiftCount <= 1; shiftCount++) + { + char hexChar = tochar[arrayOfChar[arrayIndex] & 0xf]; + if ((characterPosition == 8) || (characterPosition == 13) || (characterPosition == 18) || (characterPosition == 23)) + { + uid[characterPosition] = '-'; + characterPosition++; + } + uid[characterPosition] = hexChar; + characterPosition++; + arrayOfChar[arrayIndex] = arrayOfChar[arrayIndex] >> 4; + } + } + uid[characterPosition] = 0; + result = UNIQUEID_OK; + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/agenttime.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file agenttime.h +* @brief Function prototypes for time related functions. +* +* @details These functions are implemented with C standard functions, +* and therefore they are platform independent. But then a platform +* can replace these functions with its own implementation as necessary. +*/ + +#ifndef AGENTTIME_H +#define AGENTTIME_H + +#include <time.h> +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @brief Get current calendar time. +* +* @details This function provides the same functionality as the +* standard C @c time() function. +*/ +MOCKABLE_FUNCTION(, time_t, get_time, time_t*, currentTime); + +/** @brief Get UTC in @c tm struct. +* +* @details This function provides the same functionality as the +* standard C @c gmtime() function. +*/ +MOCKABLE_FUNCTION(, struct tm*, get_gmtime, time_t*, currentTime); + +/** @brief Get current time representation of the given calendar time. +* +* @details This function provides the same functionality as the +* standard C @c mktime() function. +*/ +MOCKABLE_FUNCTION(, time_t, get_mktime, struct tm*, cal_time); + +/** @brief Gets a C-string representation of the given time. +* +* @details This function provides the same functionality as the +* standard C @c ctime() function. +*/ +MOCKABLE_FUNCTION(, char*, get_ctime, time_t*, timeToGet); + +/** @brief Gets the difference in seconds between @c stopTime and +* @c startTime. +* +* @details This function provides the same functionality as the +* standard C @c difftime() function. +*/ +MOCKABLE_FUNCTION(, double, get_difftime, time_t, stopTime, time_t, startTime); + +#ifdef __cplusplus +} +#endif + +#endif // AGENTTIME_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/base32.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BASE32_H +#define BASE32_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +/** +* @brief Encodes the BUFFER_HANDLE to a base 32 STRING_HANDLE +* +* @param input BUFFER_HANDLE of the unsigned char* to be encoded +* +* @return A base32 encoded STRING_HANDLE that will need to be deallocated +*/ +MOCKABLE_FUNCTION(, STRING_HANDLE, Base32_Encode, BUFFER_HANDLE, input); + +/** +* @brief Encodes the BUFFER_HANDLE to a base 32 char* +* +* @param source An unsigned char* to be encoded +* @param size The lenght in bytes of the source variable +* +* @return A base32 encoded string that will need to be deallocated +*/ +MOCKABLE_FUNCTION(, char*, Base32_Encode_Bytes, const unsigned char*, source, size_t, size); + +/** +* @brief Decodes a base32 encoded STRING_HANDLE to a BUFFER_HANDLE +* +* @param handle STRING_HANDLE of a base32 encode string +* +* @return A BUFFER_HANDLE of the result of decoding the handle +*/ +MOCKABLE_FUNCTION(, BUFFER_HANDLE, Base32_Decode, STRING_HANDLE, handle); + +/** +* @brief Decodes a base32 encoded char* to a BUFFER_HANDLE +* +* @param source char* of a base32 encode string +* +* @return A BUFFER_HANDLE of the result of decoding the source +*/ +MOCKABLE_FUNCTION(, BUFFER_HANDLE, Base32_Decode_String, const char*, source); + +#ifdef __cplusplus +} +#endif + +#endif /* BASE64_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/base64.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file base64.h +* @brief Prototypes for functions related to encoding/decoding +* a @c buffer using standard base64 encoding. +*/ + +#ifndef BASE64_H +#define BASE64_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + + +/** + * @brief Base64 encodes a buffer and returns the resulting string. + * + * @param input The buffer that needs to be base64 encoded. + * + * Base64_Encoder takes as a parameter a pointer to a BUFFER. If @p input is @c NULL then + * @c Base64_Encoder returns @c NULL. The size of the BUFFER pointed to by @p input may + * be zero. If when allocating memory to produce the encoding a failure occurs, then @c + * Base64_Encoder returns @c NULL. Otherwise + * @c Base64_Encoder returns a pointer to a STRING. That string contains the + * base 64 encoding of the @p input. This encoding of @p input will not contain embedded + * line feeds. + * + * @return A @c STRING_HANDLE containing the base64 encoding of @p input. + */ +MOCKABLE_FUNCTION(, STRING_HANDLE, Base64_Encoder, BUFFER_HANDLE, input); + +/** + * @brief Base64 encodes the buffer pointed to by @p source and returns the resulting string. + * + * @param source The buffer that needs to be base64 encoded. + * @param size The size. + * + * This function produces a @c STRING_HANDLE containing the base64 encoding of the + * buffer pointed to by @p source, having the size as given by + * @p size. If @p source is @c NULL then @c Base64_Encode_Bytes returns @c NULL + * If @p source is not @c NULL and @p size is zero, then @c Base64_Encode_Bytes produces + * an empty @c STRING_HANDLE. Otherwise, @c Base64_Encode_Bytes produces a + * @c STRING_HANDLE containing the Base64 representation of the buffer. In case of + * any errors, @c Base64_Encode_Bytes returns @c NULL.]. + * + * @return @c NULL in case an error occurs or a @c STRING_HANDLE containing the base64 encoding + * of @p input. + * + */ +MOCKABLE_FUNCTION(, STRING_HANDLE, Base64_Encode_Bytes, const unsigned char*, source, size_t, size); + +/** + * @brief Base64 decodes the buffer pointed to by @p source and returns the resulting buffer. + * + * @param source A base64 encoded string buffer. + * + * This function decodes the string pointed at by @p source using base64 decoding and + * returns the resulting buffer. If @p source is @c NULL then + * @c Base64_Decoder returns NULL. If the string pointed to by @p source is zero + * length then the handle returned refers to a zero length buffer. If there is any + * memory allocation failure during the decode or if the source string has an invalid + * length for a base 64 encoded string then @c Base64_Decoder returns @c NULL. + * + * @return A @c BUFFER_HANDLE pointing to a buffer containing the result of base64 decoding @p + * source. + */ +MOCKABLE_FUNCTION(, BUFFER_HANDLE, Base64_Decoder, const char*, source); + +#ifdef __cplusplus +} +#endif + +#endif /* BASE64_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/buffer_.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BUFFER_H +#define BUFFER_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#include <stdbool.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct BUFFER_TAG* BUFFER_HANDLE; + +MOCKABLE_FUNCTION(, BUFFER_HANDLE, BUFFER_new); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, BUFFER_create, const unsigned char*, source, size_t, size); +MOCKABLE_FUNCTION(, void, BUFFER_delete, BUFFER_HANDLE, handle); +MOCKABLE_FUNCTION(, int, BUFFER_pre_build, BUFFER_HANDLE, handle, size_t, size); +MOCKABLE_FUNCTION(, int, BUFFER_build, BUFFER_HANDLE, handle, const unsigned char*, source, size_t, size); +MOCKABLE_FUNCTION(, int, BUFFER_append_build, BUFFER_HANDLE, handle, const unsigned char*, source, size_t, size); +MOCKABLE_FUNCTION(, int, BUFFER_unbuild, BUFFER_HANDLE, handle); +MOCKABLE_FUNCTION(, int, BUFFER_enlarge, BUFFER_HANDLE, handle, size_t, enlargeSize); +MOCKABLE_FUNCTION(, int, BUFFER_shrink, BUFFER_HANDLE, handle, size_t, decreaseSize, bool, fromEnd); +MOCKABLE_FUNCTION(, int, BUFFER_content, BUFFER_HANDLE, handle, const unsigned char**, content); +MOCKABLE_FUNCTION(, int, BUFFER_size, BUFFER_HANDLE, handle, size_t*, size); +MOCKABLE_FUNCTION(, int, BUFFER_append, BUFFER_HANDLE, handle1, BUFFER_HANDLE, handle2); +MOCKABLE_FUNCTION(, int, BUFFER_prepend, BUFFER_HANDLE, handle1, BUFFER_HANDLE, handle2); +MOCKABLE_FUNCTION(, int, BUFFER_fill, BUFFER_HANDLE, handle, unsigned char, fill_char); +MOCKABLE_FUNCTION(, unsigned char*, BUFFER_u_char, BUFFER_HANDLE, handle); +MOCKABLE_FUNCTION(, size_t, BUFFER_length, BUFFER_HANDLE, handle); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, BUFFER_clone, BUFFER_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + + +#endif /* BUFFER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/condition.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONDITION_H +#define CONDITION_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* COND_HANDLE; + +#define COND_RESULT_VALUES \ + COND_OK, \ + COND_INVALID_ARG, \ + COND_ERROR, \ + COND_TIMEOUT \ + +/** +* @brief Enumeration specifying the lock status. +*/ +DEFINE_ENUM(COND_RESULT, COND_RESULT_VALUES); + +/** +* @brief This API creates and returns a valid condition handle. +* +* @return A valid @c COND_HANDLE when successful or @c NULL otherwise. +*/ +MOCKABLE_FUNCTION(, COND_HANDLE, Condition_Init); + +/** +* @brief unblock all currently working condition. +* +* @param handle A valid handle to the lock. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs +* and @c COND_TIMEOUT when the handle times out. +*/ +MOCKABLE_FUNCTION(, COND_RESULT, Condition_Post, COND_HANDLE, handle); + +/** +* @brief block on the condition handle unti the thread is signalled +* or until the timeout_milliseconds is reached. +* +* @param handle A valid handle to the lock. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs +* and @c COND_TIMEOUT when the handle times out. +*/ +MOCKABLE_FUNCTION(, COND_RESULT, Condition_Wait, COND_HANDLE, handle, LOCK_HANDLE, lock, int, timeout_milliseconds); + +/** +* @brief The condition instance is deinitialized. +* +* @param handle A valid handle to the condition. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs. +*/ +MOCKABLE_FUNCTION(, void, Condition_Deinit, COND_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* CONDITION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/connection_string_parser.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONNECTION_STRING_PARSER_H +#define CONNECTION_STRING_PARSER_H + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/strings.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + MOCKABLE_FUNCTION(, MAP_HANDLE, connectionstringparser_parse_from_char, const char*, connection_string); + MOCKABLE_FUNCTION(, MAP_HANDLE, connectionstringparser_parse, STRING_HANDLE, connection_string); + MOCKABLE_FUNCTION(, int, connectionstringparser_splitHostName_from_char, const char*, hostName, STRING_HANDLE, nameString, STRING_HANDLE, suffixString); + MOCKABLE_FUNCTION(, int, connectionstringparser_splitHostName, STRING_HANDLE, hostNameString, STRING_HANDLE, nameString, STRING_HANDLE, suffixString); + +#ifdef __cplusplus +} +#endif + +#endif /* CONNECTION_STRING_PARSER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/consolelogger.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONSOLELOGGER_H +#define CONSOLELOGGER_H + +#include "azure_c_shared_utility/xlogging.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + extern void consolelogger_log(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...); + +#if (defined(_MSC_VER)) && (!(defined WINCE)) + extern void consolelogger_log_with_GetLastError(const char* file, const char* func, int line, const char* format, ...); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CONSOLELOGGER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/const_defines.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONST_DEFINES_H +#define CONST_DEFINES_H + +// Used to remove GCC warning unused +#ifdef __GNUC__ + #define STATIC_VAR_UNUSED __attribute__ ((unused)) +#else + #define STATIC_VAR_UNUSED +#endif + +#ifndef AZURE_UNREFERENCED_PARAMETER +#define AZURE_UNREFERENCED_PARAMETER(param) (void)(param) +#endif + +#ifdef _MSC_VER +#define AZURE_DEPRECATED __declspec(deprecated) +#elif defined(__GNUC__) | defined(__clang__) +#define AZURE_DEPRECATED __attribute__((__deprecated__)) +#else +#define AZURE_DEPRECATED +#endif + +#endif // CONST_DEFINES
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/constbuffer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONSTBUFFER_H +#define CONSTBUFFER_H + +#include "azure_c_shared_utility/buffer_.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +/*this is the handle*/ +typedef struct CONSTBUFFER_HANDLE_DATA_TAG* CONSTBUFFER_HANDLE; + +/*this is what is returned when the content of the buffer needs access*/ +typedef struct CONSTBUFFER_TAG +{ + const unsigned char* buffer; + size_t size; +} CONSTBUFFER; + +/*this creates a new constbuffer from a memory area*/ +MOCKABLE_FUNCTION(, CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size); + +/*this creates a new constbuffer from an existing BUFFER_HANDLE*/ +MOCKABLE_FUNCTION(, CONSTBUFFER_HANDLE, CONSTBUFFER_CreateFromBuffer, BUFFER_HANDLE, buffer); + +MOCKABLE_FUNCTION(, CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle); + +MOCKABLE_FUNCTION(, const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle); + +MOCKABLE_FUNCTION(, void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* CONSTBUFFER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/constmap.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file constmap.h +* @brief ConstMap is a module that implements a read-only dictionary +* of @c const char* keys to @c const char* values. +*/ + +#ifndef CONSTMAP_H +#define CONSTMAP_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + + +#define CONSTMAP_RESULT_VALUES \ + CONSTMAP_OK, \ + CONSTMAP_ERROR, \ + CONSTMAP_INVALIDARG, \ + CONSTMAP_KEYNOTFOUND + +/** @brief Enumeration specifying the status of calls to various APIs in this + * module. + */ +DEFINE_ENUM(CONSTMAP_RESULT, CONSTMAP_RESULT_VALUES); + +typedef struct CONSTMAP_HANDLE_DATA_TAG* CONSTMAP_HANDLE; + + +/** + * @brief Creates a new read-only map from a map handle. + * + * @param sourceMap The map from which we will populate key,value + * into the read-only map. + * + * @return A valid @c CONSTMAP_HANDLE or @c NULL in case an error occurs. + */ +MOCKABLE_FUNCTION(, CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap); + + /** + * @brief Destroy a read-only map. Deallocate memory associated with handle. + * @param handle Handle to a read-only map. + */ +MOCKABLE_FUNCTION(, void, ConstMap_Destroy, CONSTMAP_HANDLE, handle); + + /** + * @brief Clone a read-only map from another read-only map. + * @param handle Handle to a read-only map. + * @return A valid @c CONSTMAP_HANDLE or @c NULL in case an error occurs. + */ +MOCKABLE_FUNCTION(, CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle); + + /** + * @brief Create a map handle populated from the read-only map. + * @param handle Handle to a read-only map. + * @return A valid @c MAP_HANDLE or @c NULL in case an error occurs. + * + * The new MAP_HANDLE needs to be destroyed when it is no longer needed. + */ +MOCKABLE_FUNCTION(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); + +/** + * @brief This function returns a true if the map contains a key + * with the same value the parameter @p key. + * + * @param handle The handle to an existing map. + * @param key The key that the caller wants checked. + * + * @return The function returns @c true if the key exists + * in the map and @c false if key is not found or + * parameters are invalid. + */ +MOCKABLE_FUNCTION(, bool, ConstMap_ContainsKey, CONSTMAP_HANDLE, handle, const char*, key); + +/** + * @brief This function returns @c true if at least one <key,value> pair + * exists in the map where the entry's value is equal to the + * parameter @c value. + * + * @param handle The handle to an existing map. + * @param value The value that the caller wants checked. + * + * @return The function returns @c true if the value exists + * in the map and @c false if value is not found or + * parameters are invalid. + */ +MOCKABLE_FUNCTION(, bool, ConstMap_ContainsValue, CONSTMAP_HANDLE, handle, const char*, value); + +/** + * @brief Retrieves the value of a stored key. + * + * @param handle The handle to an existing map. + * @param key The key to be looked up in the map. + * + * @return Returns @c NULL in case the input arguments are @c NULL or if the + * requested key is not found in the map. Returns a pointer to the + * key's value otherwise. + */ +MOCKABLE_FUNCTION(, const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key); + + /** + * @brief Retrieves the complete list of keys and values from the map + * in @p values and @p keys. Also writes the size of the list + * in @p count. + * + * @param handle The handle to an existing map. + * @param keys The location where the list of keys is to be written. + * @param values The location where the list of values is to be written. + * @param count The number of stored keys and values is written at the + * location indicated by this pointer. + * + * @return Returns @c CONSTMAP_OK if the keys and values are retrieved + * and written successfully or an error code otherwise. + */ +MOCKABLE_FUNCTION(, CONSTMAP_RESULT, ConstMap_GetInternals, CONSTMAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count); + + +#ifdef __cplusplus +} +#endif + +#endif /* CONSTMAP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/crt_abstractions.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CRT_ABSTRACTIONS_H +#define CRT_ABSTRACTIONS_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstdio> +#include <cstring> +#include <cerrno> +#include <cmath> +extern "C" { +#else // __cplusplus +#include <stdio.h> +#include <string.h> +#include <errno.h> +#endif // __cplusplus + +#ifdef _MSC_VER + +#ifdef QUARKGALILEO +#define HAS_STDBOOL +#ifdef __cplusplus +typedef bool _Bool; +#else +/*galileo apparently has _Bool and bool as built in types*/ +#endif +#endif // QUARKGALILEO + +#ifndef _WIN32_WCE +#define HAS_STDBOOL +#ifdef __cplusplus +/*because C++ doesn't do anything about ... */ +#define _Bool bool +#else // __cplusplus +#include <stdbool.h> +#endif // __cplusplus +#else // _WIN32_WCE +/* WINCE does not support bool as C datatype */ +#define __bool_true_false_are_defined 1 + +#define HAS_STDBOOL + +#define _Bool bool + +#ifdef __cplusplus +#define _CSTDBOOL_ +#else // __cplusplus +typedef unsigned char bool; + +#define false 0 +#define true 1 +#endif // __cplusplus +#endif // _WIN32_WCE + +#else // _MSC_VER + +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define HAS_STDBOOL +#include <stdbool.h> +#endif // ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) +#endif // __STDC_VERSION__ +#endif // _MSC_VER + +#ifndef HAS_STDBOOL +#ifdef __cplusplus +#define _Bool bool +#else // __cplusplus +typedef unsigned char _Bool; +typedef unsigned char bool; +#define false 0 +#define true 1 +#endif // __cplusplus +#endif // HAS_STDBOOL + + +/* Codes_SRS_CRT_ABSTRACTIONS_99_001:[The module shall not redefine the secure functions implemented by Microsoft CRT.] */ +/* Codes_SRS_CRT_ABSTRACTIONS_99_040 : [The module shall still compile when building on a Microsoft platform.] */ +/* Codes_SRS_CRT_ABSTRACTIONS_99_002: [CRTAbstractions module shall expose the following API]*/ +#ifdef _MSC_VER +#else // _MSC_VER + +/* Adding definitions from errno.h & crtdefs.h */ +#if !defined (_TRUNCATE) +#define _TRUNCATE ((size_t)-1) +#endif /* !defined (_TRUNCATE) */ + +#if !defined STRUNCATE +#define STRUNCATE 80 +#endif /* !defined (STRUNCATE) */ + +extern int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src); +extern int strcat_s(char* dst, size_t dstSizeInBytes, const char* src); +extern int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t maxCount); +extern int sprintf_s(char* dst, size_t dstSizeInBytes, const char* format, ...); +#endif // _MSC_VER + +extern unsigned long long strtoull_s(const char* nptr, char** endPtr, int base); +extern float strtof_s(const char* nptr, char** endPtr); +extern long double strtold_s(const char* nptr, char** endPtr); + +#ifdef _MSC_VER +#define stricmp _stricmp +#endif // _MSC_VER + +MOCKABLE_FUNCTION(, int, mallocAndStrcpy_s, char**, destination, const char*, source); +MOCKABLE_FUNCTION(, int, unsignedIntToString, char*, destination, size_t, destinationSize, unsigned int, value); +MOCKABLE_FUNCTION(, int, size_tToString, char*, destination, size_t, destinationSize, size_t, value); + +/*following logic shall define the TOUPPER and ISDIGIT, we do that because the SDK is not happy with some Arduino implementation of it.*/ +#define TOUPPER(c) ((((c)>='a') && ((c)<='z'))?(c)-'a'+'A':c) +#define ISDIGIT(c) ((((c)>='0') && ((c)<='9'))?1:0) + +/*following logic shall define the ISNAN macro*/ +/*if runing on Microsoft Visual C compiler, than ISNAN shall be _isnan*/ +/*else if running on C99 or C11, ISNAN shall be isnan*/ +/*else if running on C89 ... #error and inform user*/ + +#ifdef _MSC_VER +#define ISNAN _isnan +#else // _MSC_VER +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define ISNAN isnan +#else // ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) +#error update this file to contain the latest C standard. +#endif // ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) +#else // __STDC_VERSION__ +#ifdef __cplusplus +/*C++ defines isnan... in C11*/ +extern "C++" { +#define ISNAN std::isnan +} +#else // __cplusplus +#error unknown (or C89) compiler, provide ISNAN with the same meaning as isnan in C99 standard +#endif // __cplusplus + +#endif // __STDC_VERSION__ +#endif // _MSC_VER + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif /* CRT_ABSTRACTIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/doublylinkedlist.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef DOUBLYLINKEDLIST_H +#define DOUBLYLINKEDLIST_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#include <stdint.h> +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct DLIST_ENTRY_TAG +{ + struct DLIST_ENTRY_TAG *Flink; + struct DLIST_ENTRY_TAG *Blink; +} DLIST_ENTRY, *PDLIST_ENTRY; + +MOCKABLE_FUNCTION(, void, DList_InitializeListHead, PDLIST_ENTRY, listHead); +MOCKABLE_FUNCTION(, int, DList_IsListEmpty, const PDLIST_ENTRY, listHead); +MOCKABLE_FUNCTION(, void, DList_InsertTailList, PDLIST_ENTRY, listHead, PDLIST_ENTRY, listEntry); +MOCKABLE_FUNCTION(, void, DList_InsertHeadList, PDLIST_ENTRY, listHead, PDLIST_ENTRY, listEntry); +MOCKABLE_FUNCTION(, void, DList_AppendTailList, PDLIST_ENTRY, listHead, PDLIST_ENTRY, ListToAppend); +MOCKABLE_FUNCTION(, int, DList_RemoveEntryList, PDLIST_ENTRY, listEntry); +MOCKABLE_FUNCTION(, PDLIST_ENTRY, DList_RemoveHeadList, PDLIST_ENTRY, listHead); + +// +// Calculate the address of the base of the structure given its type, and an +// address of a field within the structure. +// +#define containingRecord(address, type, field) ((type *)((uintptr_t)(address) - offsetof(type,field))) + +#ifdef __cplusplus +} +#else +#endif + +#endif /* DOUBLYLINKEDLIST_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/envvariable.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ENV_VARIABLE_H +#define ENV_VARIABLE_H + +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + + +MOCKABLE_FUNCTION(, const char*, environment_get_variable, const char*, variable_name); + +#ifdef __cplusplus +} +#endif + +#endif /* ENV_VARIABLE_H */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/etwlogger_driver.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ETWLOGGER_DRIVER_H +#define ETWLOGGER_DRIVER_H + +#include "azure_c_shared_utility/xlogging.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + extern void etwlogger_log(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...); + +#if (defined(_MSC_VER)) && (!(defined WINCE)) + extern void etwlogger_log_with_GetLastError(const char* file, const char* func, int line, const char* format, ...); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ETWLOGGER_DRIVER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/gb_rand.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GB_RAND_H +#define GB_RAND_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +MOCKABLE_FUNCTION(, int, gb_rand); + +#ifdef __cplusplus +} +#endif + +#endif /* GB_RAND_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/gb_stdio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GB_STDIO_H +#define GB_STDIO_H + +/*this file, if included instead of <stdio.h> has the following functionality: +1) if GB_STDIO_INTERCEPT is defined then + a) some of the stdio.h symbols shall be redefined, for example: fopen => gb_fopen + b) all "code" using the fopen will actually (because of the preprocessor) call to gb_fopen + c) gb_fopen shall blindly call into fopen, thus realizing a passthrough + + reason is: unittesting. fopen is comes with the C Run Time and cannot be mocked (that is, in the global namespace cannot exist a function called fopen + +2) if GB_STDIO_INTERCEPT is not defined then + a) it shall include <stdio.h> => no passthrough, just direct linking. +*/ + +#ifndef GB_STDIO_INTERCEPT +#include <stdio.h> +#else + +/*source level intercepting of function calls*/ +#define fopen fopen_never_called_never_implemented_always_forgotten +#define fclose fclose_never_called_never_implemented_always_forgotten +#define fseek fseek_never_called_never_implemented_always_forgotten +#define ftell ftell_never_called_never_implemented_always_forgotten +#define fprintf fprintf_never_called_never_implemented_always_forgotten + +#include "azure_c_shared_utility/umock_c_prod.h" + + +#ifdef __cplusplus +#include <cstdio.h> +extern "C" +{ +#else +#include <stdio.h> +#endif + +#undef fopen +#define fopen gb_fopen +MOCKABLE_FUNCTION(, FILE*, gb_fopen, const char*, filename, const char*, mode); + + +#undef fclose +#define fclose gb_fclose +MOCKABLE_FUNCTION(, int, fclose, FILE *, stream); + +#undef fseek +#define fseek gb_fseek +MOCKABLE_FUNCTION(, int, fseek, FILE *,stream, long int, offset, int, whence); + +#undef ftell +#define ftell gb_ftell +MOCKABLE_FUNCTION(, long int, ftell, FILE *, stream); + +#undef fprintf +#define fprintf gb_fprintf +extern int fprintf(FILE * stream, const char * format, ...); + + +#ifdef __cplusplus +} +#endif + +#endif /*GB_STDIO_INTERCEPT*/ + +#endif /* GB_STDIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/gb_time.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GB_TIME_H +#define GB_TIME_H + +/*this file, if included instead of <stdio.h> has the following functionality: +1) if GB_TIME_INTERCEPT is defined then + a) some of the time.h symbols shall be redefined, for example: time => gb_time + b) all "code" using the time will actually (because of the preprocessor) call to gb_time + c) gb_time shall blindly call into time, thus realizing a passthrough + + reason is: unittesting. time comes with the C Run Time and cannot be mocked (that is, in the global namespace cannot exist a function called time + +2) if GB_TIME_INTERCEPT is not defined then + a) it shall include <time.h> => no passthrough, just direct linking. +*/ + +#ifndef GB_TIME_INTERCEPT +#include <time.h> +#else + +/*source level intercepting of function calls*/ +#define time time_never_called_never_implemented_always_forgotten +#define localtime localtime_never_called_never_implemented_always_forgotten +#define strftime strftime_never_called_never_implemented_always_forgotten + +#ifdef __cplusplus +#include <ctime.h> +extern "C" +{ +#else +#include <time.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +#undef time +#define time gb_time +MOCKABLE_FUNCTION(, time_t, time, time_t *, timer); + +#undef localtime +#define localtime gb_localtime +MOCKABLE_FUNCTION(, struct tm *, localtime, const time_t *, timer); + +#undef strftime +#define strftime gb_strftime +MOCKABLE_FUNCTION(, size_t, strftime, char *, s, size_t, maxsize, const char *, format, const struct tm *, timeptr); + + +#ifdef __cplusplus +} +#endif + +#endif /*GB_TIME_INTERCEPT*/ + +#endif /* GB_TIME_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/gballoc.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GBALLOC_H +#define GBALLOC_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdlib> +extern "C" +{ +#else +#include <stddef.h> +#include <stdlib.h> +#endif + +// GB_USE_CUSTOM_HEAP disables the implementations in gballoc.c and +// requires that an external library implement the gballoc_malloc family +// declared here. +#if defined(GB_USE_CUSTOM_HEAP) +void* gballoc_malloc(size_t size); +void* gballoc_calloc(size_t nmemb, size_t size); +void* gballoc_realloc(void* ptr, size_t size); +void gballoc_free(void* ptr); + +#define malloc gballoc_malloc +#define calloc gballoc_calloc +#define realloc gballoc_realloc +#define free gballoc_free + +/* all translation units that need memory measurement need to have GB_MEASURE_MEMORY_FOR_THIS defined */ +/* GB_DEBUG_ALLOC is the switch that turns the measurement on/off, so that it is not on always */ +#elif defined(GB_DEBUG_ALLOC) + +MOCKABLE_FUNCTION(, int, gballoc_init); +MOCKABLE_FUNCTION(, void, gballoc_deinit); +MOCKABLE_FUNCTION(, void*, gballoc_malloc, size_t, size); +MOCKABLE_FUNCTION(, void*, gballoc_calloc, size_t, nmemb, size_t, size); +MOCKABLE_FUNCTION(, void*, gballoc_realloc, void*, ptr, size_t, size); +MOCKABLE_FUNCTION(, void, gballoc_free, void*, ptr); + +MOCKABLE_FUNCTION(, size_t, gballoc_getMaximumMemoryUsed); +MOCKABLE_FUNCTION(, size_t, gballoc_getCurrentMemoryUsed); +MOCKABLE_FUNCTION(, size_t, gballoc_getAllocationCount); +MOCKABLE_FUNCTION(, void, gballoc_resetMetrics); + +/* if GB_MEASURE_MEMORY_FOR_THIS is defined then we want to redirect memory allocation functions to gballoc_xxx functions */ +#ifdef GB_MEASURE_MEMORY_FOR_THIS +/* Unfortunately this is still needed here for things to still compile when using _CRTDBG_MAP_ALLOC. +That is because there is a rogue component (most likely CppUnitTest) including crtdbg. */ +#if defined(_CRTDBG_MAP_ALLOC) && defined(_DEBUG) +#undef _malloc_dbg +#undef _calloc_dbg +#undef _realloc_dbg +#undef _free_dbg +#define _malloc_dbg(size, ...) gballoc_malloc(size) +#define _calloc_dbg(nmemb, size, ...) gballoc_calloc(nmemb, size) +#define _realloc_dbg(ptr, size, ...) gballoc_realloc(ptr, size) +#define _free_dbg(ptr, ...) gballoc_free(ptr) +#else +#define malloc gballoc_malloc +#define calloc gballoc_calloc +#define realloc gballoc_realloc +#define free gballoc_free +#endif +#endif + +#else /* GB_DEBUG_ALLOC */ + +#define gballoc_init() 0 +#define gballoc_deinit() ((void)0) + +#define gballoc_getMaximumMemoryUsed() SIZE_MAX +#define gballoc_getCurrentMemoryUsed() SIZE_MAX +#define gballoc_getAllocationCount() SIZE_MAX +#define gballoc_resetMetrics() ((void)0) + +#endif /* GB_DEBUG_ALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* GBALLOC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/gbnetwork.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GBNETWORK_H +#define GBNETWORK_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdlib> +#include <cstdint> +extern "C" +{ +#else +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#endif + +#ifdef WIN32 + #include <winsock2.h> +#else + #include <sys/socket.h> +#endif + +/* all translation units that need network measurement need to have GB_MEASURE_NETWORK_FOR_THIS defined */ +/* GB_DEBUG_NETWORK is the switch that turns the measurement on/off, so that it is not on always */ +#if defined(GB_DEBUG_NETWORK) + +MOCKABLE_FUNCTION(, int, gbnetwork_init); +MOCKABLE_FUNCTION(, void, gbnetwork_deinit); + +#ifdef WIN32 +MOCKABLE_FUNCTION(, int, gbnetwork_send, SOCKET, sock, const char*, buf, int, len, int, flags); +#else +MOCKABLE_FUNCTION(, ssize_t, gbnetwork_send, int, sock, const void*, buf, size_t, len, int, flags); +#endif + +#ifdef WIN32 +MOCKABLE_FUNCTION(, int, gbnetwork_recv, SOCKET, sock, char*, buf, int, len, int, flags); +#else +MOCKABLE_FUNCTION(, ssize_t, gbnetwork_recv, int, sock, void*, buf, size_t, len, int, flags); +#endif + +MOCKABLE_FUNCTION(, uint64_t, gbnetwork_getBytesSent); +MOCKABLE_FUNCTION(, uint64_t, gbnetwork_getNumSends); +MOCKABLE_FUNCTION(, uint64_t, gbnetwork_getBytesRecv); +MOCKABLE_FUNCTION(, uint64_t, gbnetwork_getNumRecv); +MOCKABLE_FUNCTION(, void, gbnetwork_resetMetrics); + +/* if GB_MEASURE_NETWORK_FOR_THIS is defined then we want to redirect network send functions to gbnetwork_xxx functions */ +#ifdef GB_MEASURE_NETWORK_FOR_THIS +#define send gbnetwork_send +#define recv gbnetwork_recv +#endif + +#else /* GB_DEBUG_NETWORK */ + +#define gbnetwork_init() 0 +#define gbnetwork_deinit() ((void)0) +#define gbnetwork_getBytesSent() 0 +#define gbnetwork_getNumSends() 0 + +#define gbnetwork_getBytesRecv() 0 +#define gbnetwork_getNumRecv() 0 + +#endif /* GB_DEBUG_NETWORK */ + +#ifdef __cplusplus +} +#endif + +#endif /* GBNETWORK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/hmac.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HMAC_H +#define HMAC_H + +#include "azure_c_shared_utility/sha.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + + MOCKABLE_FUNCTION(, int, hmac, SHAversion, whichSha, const unsigned char *, text, int, text_len, + const unsigned char *, key, int, key_len, + uint8_t, digest[USHAMaxHashSize]); + +#ifdef __cplusplus +} +#endif + +#endif /* HMAC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/hmacsha256.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HMACSHA256_H +#define HMACSHA256_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HMACSHA256_RESULT_VALUES \ + HMACSHA256_OK, \ + HMACSHA256_INVALID_ARG, \ + HMACSHA256_ERROR + +DEFINE_ENUM(HMACSHA256_RESULT, HMACSHA256_RESULT_VALUES) + +MOCKABLE_FUNCTION(, HMACSHA256_RESULT, HMACSHA256_ComputeHash, const unsigned char*, key, size_t, keyLen, const unsigned char*, payload, size_t, payloadLen, BUFFER_HANDLE, hash); + +#ifdef __cplusplus +} +#endif + +#endif /* HMACSHA256_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/http_proxy_io.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HTTP_PROXY_IO_H +#define HTTP_PROXY_IO_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct HTTP_PROXY_IO_CONFIG_TAG +{ + const char* hostname; + int port; + const char* proxy_hostname; + int proxy_port; + const char* username; + const char* password; +} HTTP_PROXY_IO_CONFIG; + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, http_proxy_io_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* HTTP_PROXY_IO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/httpapi.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file httpapi.h + * @brief This module implements the standard HTTP API used by the C IoT client + * library. + * + * @details For example, on the Windows platform the HTTP API code uses + * WinHTTP and for Linux it uses curl and so forth. HTTPAPI must support + * HTTPs (HTTP+SSL). + */ + +#ifndef HTTPAPI_H +#define HTTPAPI_H + +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +typedef struct HTTP_HANDLE_DATA_TAG* HTTP_HANDLE; + +#define AMBIGUOUS_STATUS_CODE (300) + +#define HTTPAPI_RESULT_VALUES \ +HTTPAPI_OK, \ +HTTPAPI_INVALID_ARG, \ +HTTPAPI_ERROR, \ +HTTPAPI_OPEN_REQUEST_FAILED, \ +HTTPAPI_SET_OPTION_FAILED, \ +HTTPAPI_SEND_REQUEST_FAILED, \ +HTTPAPI_RECEIVE_RESPONSE_FAILED, \ +HTTPAPI_QUERY_HEADERS_FAILED, \ +HTTPAPI_QUERY_DATA_AVAILABLE_FAILED, \ +HTTPAPI_READ_DATA_FAILED, \ +HTTPAPI_ALREADY_INIT, \ +HTTPAPI_NOT_INIT, \ +HTTPAPI_HTTP_HEADERS_FAILED, \ +HTTPAPI_STRING_PROCESSING_ERROR, \ +HTTPAPI_ALLOC_FAILED, \ +HTTPAPI_INIT_FAILED, \ +HTTPAPI_INSUFFICIENT_RESPONSE_BUFFER, \ +HTTPAPI_SET_X509_FAILURE, \ +HTTPAPI_SET_TIMEOUTS_FAILED \ + +/** @brief Enumeration specifying the possible return values for the APIs in + * this module. + */ +DEFINE_ENUM(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES); + +#define HTTPAPI_REQUEST_TYPE_VALUES\ + HTTPAPI_REQUEST_GET, \ + HTTPAPI_REQUEST_POST, \ + HTTPAPI_REQUEST_PUT, \ + HTTPAPI_REQUEST_DELETE, \ + HTTPAPI_REQUEST_PATCH \ + +/** @brief Enumeration specifying the HTTP request verbs accepted by + * the HTTPAPI module. + */ +DEFINE_ENUM(HTTPAPI_REQUEST_TYPE, HTTPAPI_REQUEST_TYPE_VALUES); + +#define MAX_HOSTNAME_LEN 65 +#define MAX_USERNAME_LEN 65 +#define MAX_PASSWORD_LEN 65 + +/** + * @brief Global initialization for the HTTP API component. + * + * Platform specific implementations are expected to initialize + * the underlying HTTP API stacks. + * + * @return @c HTTPAPI_OK if initialization is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, HTTPAPI_RESULT, HTTPAPI_Init); + +/** @brief Free resources allocated in ::HTTPAPI_Init. */ +MOCKABLE_FUNCTION(, void, HTTPAPI_Deinit); + +/** + * @brief Creates an HTTPS connection to the host specified by the @p + * hostName parameter. + * + * @param hostName Name of the host. + * + * This function returns a handle to the newly created connection. + * You can use the handle in subsequent calls to execute specific + * HTTP calls using ::HTTPAPI_ExecuteRequest. + * + * @return A @c HTTP_HANDLE to the newly created connection or @c NULL in + * case an error occurs. + */ +MOCKABLE_FUNCTION(, HTTP_HANDLE, HTTPAPI_CreateConnection, const char*, hostName); + +/** + * @brief Closes a connection created with ::HTTPAPI_CreateConnection. + * + * @param handle The handle to the HTTP connection created via ::HTTPAPI_CreateConnection. + * + * All resources allocated by ::HTTPAPI_CreateConnection should be + * freed in ::HTTPAPI_CloseConnection. + */ +MOCKABLE_FUNCTION(, void, HTTPAPI_CloseConnection, HTTP_HANDLE, handle); + +/** + * @brief Sends the HTTP request to the host and handles the response for + * the HTTP call. + * + * @param handle The handle to the HTTP connection created + * via ::HTTPAPI_CreateConnection. + * @param requestType Specifies which HTTP method is used (GET, + * POST, DELETE, PUT, PATCH). + * @param relativePath Specifies the relative path of the URL + * excluding the host name. + * @param httpHeadersHandle Specifies a set of HTTP headers (name-value + * pairs) to be added to the + * HTTP request. The @p httpHeadersHandle + * handle can be created and setup with + * the proper name-value pairs by using the + * HTTPHeaders APIs available in @c + * HTTPHeaders.h. + * @param content Specifies a pointer to the request body. + * This value is optional and can be @c NULL. + * @param contentLength Specifies the request body size (this is + * typically added into the HTTP headers as + * the Content-Length header). This value is + * optional and can be 0. + * @param statusCode This is an out parameter, where + * ::HTTPAPI_ExecuteRequest returns the status + * code from the HTTP response (200, 201, 400, + * 401, etc.) + * @param responseHeadersHandle This is an HTTP headers handle to which + * ::HTTPAPI_ExecuteRequest must add all the + * HTTP response headers so that the caller of + * ::HTTPAPI_ExecuteRequest can inspect them. + * You can manipulate @p responseHeadersHandle + * by using the HTTPHeaders APIs available in + * @c HTTPHeaders.h + * @param responseContent This is a buffer that must be filled by + * ::HTTPAPI_ExecuteRequest with the contents + * of the HTTP response body. The buffer size + * must be increased by the + * ::HTTPAPI_ExecuteRequest implementation in + * order to fit the response body. + * ::HTTPAPI_ExecuteRequest must also handle + * chunked transfer encoding for HTTP responses. + * To manipulate the @p responseContent buffer, + * use the APIs available in @c Strings.h. + * + * @return @c HTTPAPI_OK if the API call is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, HTTPAPI_RESULT, HTTPAPI_ExecuteRequest, HTTP_HANDLE, handle, HTTPAPI_REQUEST_TYPE, requestType, const char*, relativePath, + HTTP_HEADERS_HANDLE, httpHeadersHandle, const unsigned char*, content, + size_t, contentLength, unsigned int*, statusCode, + HTTP_HEADERS_HANDLE, responseHeadersHandle, BUFFER_HANDLE, responseContent); + +/** + * @brief Sets the option named @p optionName bearing the value + * @p value for the HTTP_HANDLE @p handle. + * + * @param handle The handle to the HTTP connection created via + * ::HTTPAPI_CreateConnection. + * @param optionName A @c NULL terminated string representing the name + * of the option. + * @param value A pointer to the value for the option. + * + * @return @c HTTPAPI_OK if initialization is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, HTTPAPI_RESULT, HTTPAPI_SetOption, HTTP_HANDLE, handle, const char*, optionName, const void*, value); + +/** + * @brief Clones the option named @p optionName bearing the value @p value + * into the pointer @p savedValue. + * + * @param optionName A @c NULL terminated string representing the name of + * the option + * @param value A pointer to the value of the option. + * @param savedValue This pointer receives the copy of the value of the + * option. The copy needs to be free-able. + * + * @return @c HTTPAPI_OK if initialization is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, HTTPAPI_RESULT, HTTPAPI_CloneOption, const char*, optionName, const void*, value, const void**, savedValue); + +#ifdef __cplusplus +} +#endif + +#endif /* HTTPAPI_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/httpapiex.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file httpapiex.h +* @brief This is a utility module that provides HTTP requests with +* build-in retry capabilities. +* +* @details HTTAPIEX is a utility module that provides HTTP requests with build-in +* retry capability to an HTTP server. Features over "regular" HTTPAPI include: +* - Optional parameters +* - Implementation independent +* - Retry mechanism +* - Persistent options +*/ + +#ifndef HTTPAPIEX_H +#define HTTPAPIEX_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/httpapi.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +typedef struct HTTPAPIEX_HANDLE_DATA_TAG* HTTPAPIEX_HANDLE; + +#define HTTPAPIEX_RESULT_VALUES \ + HTTPAPIEX_OK, \ + HTTPAPIEX_ERROR, \ + HTTPAPIEX_INVALID_ARG, \ + HTTPAPIEX_RECOVERYFAILED +/*to be continued*/ + +/** @brief Enumeration specifying the status of calls to various APIs in this module. +*/ +DEFINE_ENUM(HTTPAPIEX_RESULT, HTTPAPIEX_RESULT_VALUES); + +/** + * @brief Creates an @c HTTPAPIEX_HANDLE that can be used in further calls. + * + * @param hostName Pointer to a null-terminated string that contains the host name + * of an HTTP server. + * + * If @p hostName is @c NULL then @c HTTPAPIEX_Create returns @c NULL. The @p + * hostName value is saved and associated with the returned handle. If creating + * the handle fails for any reason, then @c HTTAPIEX_Create returns @c NULL. + * Otherwise, @c HTTPAPIEX_Create returns an @c HTTAPIEX_HANDLE suitable for + * further calls to the module. + * + * @return An @c HTTAPIEX_HANDLE suitable for further calls to the module. + */ +MOCKABLE_FUNCTION(, HTTPAPIEX_HANDLE, HTTPAPIEX_Create, const char*, hostName); + +/** + * @brief Tries to execute an HTTP request. + * + * @param handle A valid @c HTTPAPIEX_HANDLE value. + * @param requestType A value from the ::HTTPAPI_REQUEST_TYPE enum. + * @param relativePath Relative path to send the request to on the server. + * @param requestHttpHeadersHandle Handle to the request HTTP headers. + * @param requestContent The request content. + * @param statusCode If non-null, the HTTP status code is written to this + * pointer. + * @param responseHttpHeadersHandle Handle to the response HTTP headers. + * @param responseContent The response content. + * + * @c HTTPAPIEX_ExecuteRequest tries to execute an HTTP request of type @p + * requestType, on the server's @p relativePath, pushing the request HTTP + * headers @p requestHttpHeadersHandle, having the content of the request + * as pointed to by @p requestContent. If successful, @c HTTAPIEX_ExecuteRequest + * writes in the out @p parameter statusCode the HTTP status, populates the @p + * responseHeadersHandle with the response headers and copies the response body + * to @p responseContent. + * + * @return An @c HTTAPIEX_HANDLE suitable for further calls to the module. + */ +MOCKABLE_FUNCTION(, HTTPAPIEX_RESULT, HTTPAPIEX_ExecuteRequest, HTTPAPIEX_HANDLE, handle, HTTPAPI_REQUEST_TYPE, requestType, const char*, relativePath, HTTP_HEADERS_HANDLE, requestHttpHeadersHandle, BUFFER_HANDLE, requestContent, unsigned int*, statusCode, HTTP_HEADERS_HANDLE, responseHttpHeadersHandle, BUFFER_HANDLE, responseContent); + +/** + * @brief Frees all resources used by the @c HTTPAPIEX_HANDLE object. + * + * @param handle The @c HTTPAPIEX_HANDLE object to be freed. + */ +MOCKABLE_FUNCTION(, void, HTTPAPIEX_Destroy, HTTPAPIEX_HANDLE, handle); + +/** + * @brief Sets the option @p optionName to the value pointed to by @p value. + * + * @param handle The @c HTTPAPIEX_HANDLE representing this session. + * @param optionName Name of the option. + * @param value The value to be set for the option. + * + * @return An @c HTTPAPIEX_RESULT indicating the status of the call. + */ +MOCKABLE_FUNCTION(, HTTPAPIEX_RESULT, HTTPAPIEX_SetOption, HTTPAPIEX_HANDLE, handle, const char*, optionName, const void*, value); + +#ifdef __cplusplus +} +#endif + +#endif /* HTTPAPIEX_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/httpapiexsas.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HTTPAPIEX_SAS_H +#define HTTPAPIEX_SAS_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct HTTPAPIEX_SAS_STATE_TAG* HTTPAPIEX_SAS_HANDLE; + +MOCKABLE_FUNCTION(, HTTPAPIEX_SAS_HANDLE, HTTPAPIEX_SAS_Create, STRING_HANDLE, key, STRING_HANDLE, uriResource, STRING_HANDLE, keyName); + +MOCKABLE_FUNCTION(, HTTPAPIEX_SAS_HANDLE, HTTPAPIEX_SAS_Create_From_String, const char*, key, const char*, uriResource, const char*, keyName); + +MOCKABLE_FUNCTION(, void, HTTPAPIEX_SAS_Destroy, HTTPAPIEX_SAS_HANDLE, handle); + +MOCKABLE_FUNCTION(, HTTPAPIEX_RESULT, HTTPAPIEX_SAS_ExecuteRequest, HTTPAPIEX_SAS_HANDLE, sasHandle, HTTPAPIEX_HANDLE, handle, HTTPAPI_REQUEST_TYPE, requestType, const char*, relativePath, HTTP_HEADERS_HANDLE, requestHttpHeadersHandle, BUFFER_HANDLE, requestContent, unsigned int*, statusCode, HTTP_HEADERS_HANDLE, responseHeadersHandle, BUFFER_HANDLE, responseContent); + +#ifdef __cplusplus +} +#endif + +#endif /* HTTPAPIEX_SAS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/httpheaders.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file httpheaders.h +* @brief This is a utility module that handles HTTP message-headers. +* +* @details An application would use ::HTTPHeaders_Alloc to create a new set of HTTP headers. +* After getting the handle, the application would build in several headers by +* consecutive calls to ::HTTPHeaders_AddHeaderNameValuePair. When the headers are +* constructed, the application can retrieve the stored data by calling one of the +* following functions: +* - ::HTTPHeaders_FindHeaderValue - when the name of the header is known and it +* wants to know the value of that header +* - ::HTTPHeaders_GetHeaderCount - when the application needs to know the count +* of all the headers +* - ::HTTPHeaders_GetHeader - when the application needs to retrieve the +* <code>name + ": " + value</code> string based on an index. +*/ + +#ifndef HTTPHEADERS_H +#define HTTPHEADERS_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +/*Codes_SRS_HTTP_HEADERS_99_001:[ HttpHeaders shall have the following interface]*/ + +#define HTTP_HEADERS_RESULT_VALUES \ +HTTP_HEADERS_OK, \ +HTTP_HEADERS_INVALID_ARG, \ +HTTP_HEADERS_ALLOC_FAILED, \ +HTTP_HEADERS_INSUFFICIENT_BUFFER, \ +HTTP_HEADERS_ERROR \ + +/** @brief Enumeration specifying the status of calls to various APIs in this module. +*/ +DEFINE_ENUM(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES); +typedef struct HTTP_HEADERS_HANDLE_DATA_TAG* HTTP_HEADERS_HANDLE; + +/** + * @brief Produces a @c HTTP_HANDLE that can later be used in subsequent calls to the module. + * + * This function returns @c NULL in case an error occurs. After successful execution + * ::HTTPHeaders_GetHeaderCount will report @c 0 existing headers. + * + * @return A HTTP_HEADERS_HANDLE representing the newly created collection of HTTP headers. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_HANDLE, HTTPHeaders_Alloc); + +/** + * @brief De-allocates the data structures allocated by previous API calls to the same handle. + * + * @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value. + */ +MOCKABLE_FUNCTION(, void, HTTPHeaders_Free, HTTP_HEADERS_HANDLE, httpHeadersHandle); + +/** + * @brief Adds a header record from the @p name and @p value parameters. + * + * @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value. + * @param name The name of the HTTP header to add. It is invalid for + * the name to include the ':' character or character codes + * outside the range 33-126. + * @param value The value to be assigned to the header. + * + * The function stores the @c name:value pair in such a way that when later + * retrieved by a call to ::HTTPHeaders_GetHeader it will return a string + * that is @c strcmp equal to @c name+": "+value. If the name already exists + * in the collection of headers, the function concatenates the new value + * after the existing value, separated by a comma and a space as in: + * <code>old-value+", "+new-value</code>. + * + * @return Returns @c HTTP_HEADERS_OK when execution is successful or an error code from + * the ::HTTPAPIEX_RESULT enum. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_RESULT, HTTPHeaders_AddHeaderNameValuePair, HTTP_HEADERS_HANDLE, httpHeadersHandle, const char*, name, const char*, value); + +/** + * @brief This API performs exactly the same as ::HTTPHeaders_AddHeaderNameValuePair + * except that if the header name already exists then the already existing value + * will be replaced as opposed to being concatenated to. + * + * @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value. + * @param name The name of the HTTP header to add/replace. It is invalid for + * the name to include the ':' character or character codes + * outside the range 33-126. + * @param value The value to be assigned to the header. + * + * @return Returns @c HTTP_HEADERS_OK when execution is successful or an error code from + * the ::HTTPAPIEX_RESULT enum. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_RESULT, HTTPHeaders_ReplaceHeaderNameValuePair, HTTP_HEADERS_HANDLE, httpHeadersHandle, const char*, name, const char*, value); + +/** + * @brief Retrieves the value for a previously stored name. + * + * @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value. + * @param name The name of the HTTP header to find. + * + * @return The return value points to a string that shall be @c strcmp equal + * to the original stored string. + */ +MOCKABLE_FUNCTION(, const char*, HTTPHeaders_FindHeaderValue, HTTP_HEADERS_HANDLE, httpHeadersHandle, const char*, name); + +/** + * @brief This API retrieves the number of stored headers. + * + * @param httpHeadersHandle A valid @c HTTP_HEADERS_HANDLE value. + * @param headersCount If non-null, the API writes the number of + * into the memory pointed at by this parameter. + * + * @return Returns @c HTTP_HEADERS_OK when execution is successful or + * @c HTTP_HEADERS_ERROR when an error occurs. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_RESULT, HTTPHeaders_GetHeaderCount, HTTP_HEADERS_HANDLE, httpHeadersHandle, size_t*, headersCount); + +/** + * @brief This API retrieves the string name+": "+value for the header + * element at the given @p index. + * + * @param handle A valid @c HTTP_HEADERS_HANDLE value. + * @param index Zero-based index of the item in the + * headers collection. + * @param destination If non-null, the header value is written into a + * new string a pointer to which is written into this + * parameters. It is the caller's responsibility to free + * this memory. + * + * @return Returns @c HTTP_HEADERS_OK when execution is successful or + * @c HTTP_HEADERS_ERROR when an error occurs. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_RESULT, HTTPHeaders_GetHeader, HTTP_HEADERS_HANDLE, handle, size_t, index, char**, destination); + +/** + * @brief This API produces a clone of the @p handle parameter. + * + * @param handle A valid @c HTTP_HEADERS_HANDLE value. + * + * If @p handle is not @c NULL this function clones the content + * of the handle to a new handle and returns it. + * + * @return A @c HTTP_HEADERS_HANDLE containing a cloned copy of the + * contents of @p handle. + */ +MOCKABLE_FUNCTION(, HTTP_HEADERS_HANDLE, HTTPHeaders_Clone, HTTP_HEADERS_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* HTTPHEADERS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/lock.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file lock.h +* @brief A minimalistic platform agnostic lock abstraction for thread +* synchronization. +* @details The Lock component is implemented in order to achieve thread +* synchronization, as we may have a requirement to consume locks +* across different platforms. This component exposes some generic +* APIs so that it can be extended for platform specific +* implementations. +*/ + +#ifndef LOCK_H +#define LOCK_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* LOCK_HANDLE; + +#define LOCK_RESULT_VALUES \ + LOCK_OK, \ + LOCK_ERROR \ + +/** @brief Enumeration specifying the lock status. +*/ +DEFINE_ENUM(LOCK_RESULT, LOCK_RESULT_VALUES); + +/** + * @brief This API creates and returns a valid lock handle. + * + * @return A valid @c LOCK_HANDLE when successful or @c NULL otherwise. + */ +MOCKABLE_FUNCTION(, LOCK_HANDLE, Lock_Init); + +/** + * @brief Acquires a lock on the given lock handle. Uses platform + * specific mutex primitives in its implementation. + * + * @param handle A valid handle to the lock. + * + * @return Returns @c LOCK_OK when a lock has been acquired and + * @c LOCK_ERROR when an error occurs. + */ +MOCKABLE_FUNCTION(, LOCK_RESULT, Lock, LOCK_HANDLE, handle); + +/** + * @brief Releases the lock on the given lock handle. Uses platform + * specific mutex primitives in its implementation. + * + * @param handle A valid handle to the lock. + * + * @return Returns @c LOCK_OK when the lock has been released and + * @c LOCK_ERROR when an error occurs. + */ +MOCKABLE_FUNCTION(, LOCK_RESULT, Unlock, LOCK_HANDLE, handle); + +/** + * @brief The lock instance is destroyed. + * + * @param handle A valid handle to the lock. + * + * @return Returns @c LOCK_OK when the lock object has been + * destroyed and @c LOCK_ERROR when an error occurs. + */ +MOCKABLE_FUNCTION(, LOCK_RESULT, Lock_Deinit, LOCK_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* LOCK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/macro_utils.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,12673 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*THIS FILE IS GENERATED*/ +/*DO NOT EDIT BY HAND!!!*/ +/*instead edit macro_utils.tt here: http://www.github.com/azure/azure-macro-utils-c.git */ +/*and then propagate the generated file to all the repos*/ +/* !!! CAUTION!!! This file is copied to multiple places */ +/* in https://github.com/Azure/azure-c-shared-utility.git, */ +/* and all of these copies must be located and replaced. */ + + + +#ifndef MACRO_UTILS_H +#define MACRO_UTILS_H + +#include <string.h> + +#if (defined OPTIMIZE_RETURN_CODES) + #define __FAILURE__ 1 +#else + #define __FAILURE__ __LINE__ +#endif + +/*"pointer or NULL" macro - because when printf-ing arguments NULL is not valid for %s (section 7.1.4 of C11 standard) */ +#define P_OR_NULL(p) (((p)!=NULL)?(p):"NULL") + +#define TOSTRING_(x) #x +#define TOSTRING(x) TOSTRING_(x) + +#define IFCOMMA(N) C2(IFCOMMA_, N) +#define IFCOMMA_0 +#define IFCOMMA_2 +#define IFCOMMA_4 , +#define IFCOMMA_6 , +#define IFCOMMA_8 , +#define IFCOMMA_10 , +#define IFCOMMA_12 , +#define IFCOMMA_14 , +#define IFCOMMA_16 , +#define IFCOMMA_18 , +#define IFCOMMA_20 , +#define IFCOMMA_22 , +#define IFCOMMA_24 , +#define IFCOMMA_26 , +#define IFCOMMA_28 , +#define IFCOMMA_30 , +#define IFCOMMA_32 , +#define IFCOMMA_34 , +#define IFCOMMA_36 , +#define IFCOMMA_38 , +#define IFCOMMA_40 , +#define IFCOMMA_42 , +#define IFCOMMA_44 , +#define IFCOMMA_46 , +#define IFCOMMA_48 , +#define IFCOMMA_50 , +#define IFCOMMA_52 , +#define IFCOMMA_54 , +#define IFCOMMA_56 , +#define IFCOMMA_58 , +#define IFCOMMA_60 , +#define IFCOMMA_62 , +#define IFCOMMA_64 , +#define IFCOMMA_66 , +#define IFCOMMA_68 , +#define IFCOMMA_70 , +#define IFCOMMA_72 , +#define IFCOMMA_74 , +#define IFCOMMA_76 , +#define IFCOMMA_78 , +#define IFCOMMA_80 , +#define IFCOMMA_82 , +#define IFCOMMA_84 , +#define IFCOMMA_86 , +#define IFCOMMA_88 , +#define IFCOMMA_90 , +#define IFCOMMA_92 , +#define IFCOMMA_94 , +#define IFCOMMA_96 , +#define IFCOMMA_98 , +#define IFCOMMA_100 , +#define IFCOMMA_102 , +#define IFCOMMA_104 , +#define IFCOMMA_106 , +#define IFCOMMA_108 , +#define IFCOMMA_110 , +#define IFCOMMA_112 , +#define IFCOMMA_114 , +#define IFCOMMA_116 , +#define IFCOMMA_118 , +#define IFCOMMA_120 , +#define IFCOMMA_122 , +#define IFCOMMA_124 , + +#define IFCOMMA_NOFIRST(N) C2(IFCOMMA_NOFIRST, N) +#define IFCOMMA_NOFIRST1 +#define IFCOMMA_NOFIRST2 , +#define IFCOMMA_NOFIRST3 , +#define IFCOMMA_NOFIRST4 , +#define IFCOMMA_NOFIRST5 , +#define IFCOMMA_NOFIRST6 , +#define IFCOMMA_NOFIRST7 , +#define IFCOMMA_NOFIRST8 , +#define IFCOMMA_NOFIRST9 , +#define IFCOMMA_NOFIRST10 , +#define IFCOMMA_NOFIRST11 , +#define IFCOMMA_NOFIRST12 , +#define IFCOMMA_NOFIRST13 , +#define IFCOMMA_NOFIRST14 , +#define IFCOMMA_NOFIRST15 , +#define IFCOMMA_NOFIRST16 , +#define IFCOMMA_NOFIRST17 , +#define IFCOMMA_NOFIRST18 , +#define IFCOMMA_NOFIRST19 , +#define IFCOMMA_NOFIRST20 , +#define IFCOMMA_NOFIRST21 , +#define IFCOMMA_NOFIRST22 , +#define IFCOMMA_NOFIRST23 , +#define IFCOMMA_NOFIRST24 , +#define IFCOMMA_NOFIRST25 , +#define IFCOMMA_NOFIRST26 , +#define IFCOMMA_NOFIRST27 , +#define IFCOMMA_NOFIRST28 , +#define IFCOMMA_NOFIRST29 , +#define IFCOMMA_NOFIRST30 , +#define IFCOMMA_NOFIRST31 , +#define IFCOMMA_NOFIRST32 , +#define IFCOMMA_NOFIRST33 , +#define IFCOMMA_NOFIRST34 , +#define IFCOMMA_NOFIRST35 , +#define IFCOMMA_NOFIRST36 , +#define IFCOMMA_NOFIRST37 , +#define IFCOMMA_NOFIRST38 , +#define IFCOMMA_NOFIRST39 , +#define IFCOMMA_NOFIRST40 , +#define IFCOMMA_NOFIRST41 , +#define IFCOMMA_NOFIRST42 , +#define IFCOMMA_NOFIRST43 , +#define IFCOMMA_NOFIRST44 , +#define IFCOMMA_NOFIRST45 , +#define IFCOMMA_NOFIRST46 , +#define IFCOMMA_NOFIRST47 , +#define IFCOMMA_NOFIRST48 , +#define IFCOMMA_NOFIRST49 , +#define IFCOMMA_NOFIRST50 , +#define IFCOMMA_NOFIRST51 , +#define IFCOMMA_NOFIRST52 , +#define IFCOMMA_NOFIRST53 , +#define IFCOMMA_NOFIRST54 , +#define IFCOMMA_NOFIRST55 , +#define IFCOMMA_NOFIRST56 , +#define IFCOMMA_NOFIRST57 , +#define IFCOMMA_NOFIRST58 , +#define IFCOMMA_NOFIRST59 , +#define IFCOMMA_NOFIRST60 , +#define IFCOMMA_NOFIRST61 , +#define IFCOMMA_NOFIRST62 , +#define IFCOMMA_NOFIRST63 , +#define IFCOMMA_NOFIRST64 , +#define IFCOMMA_NOFIRST65 , +#define IFCOMMA_NOFIRST66 , +#define IFCOMMA_NOFIRST67 , +#define IFCOMMA_NOFIRST68 , +#define IFCOMMA_NOFIRST69 , +#define IFCOMMA_NOFIRST70 , +#define IFCOMMA_NOFIRST71 , +#define IFCOMMA_NOFIRST72 , +#define IFCOMMA_NOFIRST73 , +#define IFCOMMA_NOFIRST74 , +#define IFCOMMA_NOFIRST75 , +#define IFCOMMA_NOFIRST76 , +#define IFCOMMA_NOFIRST77 , +#define IFCOMMA_NOFIRST78 , +#define IFCOMMA_NOFIRST79 , +#define IFCOMMA_NOFIRST80 , +#define IFCOMMA_NOFIRST81 , +#define IFCOMMA_NOFIRST82 , +#define IFCOMMA_NOFIRST83 , +#define IFCOMMA_NOFIRST84 , +#define IFCOMMA_NOFIRST85 , +#define IFCOMMA_NOFIRST86 , +#define IFCOMMA_NOFIRST87 , +#define IFCOMMA_NOFIRST88 , +#define IFCOMMA_NOFIRST89 , +#define IFCOMMA_NOFIRST90 , +#define IFCOMMA_NOFIRST91 , +#define IFCOMMA_NOFIRST92 , +#define IFCOMMA_NOFIRST93 , +#define IFCOMMA_NOFIRST94 , +#define IFCOMMA_NOFIRST95 , +#define IFCOMMA_NOFIRST96 , +#define IFCOMMA_NOFIRST97 , +#define IFCOMMA_NOFIRST98 , +#define IFCOMMA_NOFIRST99 , +#define IFCOMMA_NOFIRST100 , +#define IFCOMMA_NOFIRST101 , +#define IFCOMMA_NOFIRST102 , +#define IFCOMMA_NOFIRST103 , +#define IFCOMMA_NOFIRST104 , +#define IFCOMMA_NOFIRST105 , +#define IFCOMMA_NOFIRST106 , +#define IFCOMMA_NOFIRST107 , +#define IFCOMMA_NOFIRST108 , +#define IFCOMMA_NOFIRST109 , +#define IFCOMMA_NOFIRST110 , +#define IFCOMMA_NOFIRST111 , +#define IFCOMMA_NOFIRST112 , +#define IFCOMMA_NOFIRST113 , +#define IFCOMMA_NOFIRST114 , +#define IFCOMMA_NOFIRST115 , +#define IFCOMMA_NOFIRST116 , +#define IFCOMMA_NOFIRST117 , +#define IFCOMMA_NOFIRST118 , +#define IFCOMMA_NOFIRST119 , +#define IFCOMMA_NOFIRST120 , +#define IFCOMMA_NOFIRST121 , +#define IFCOMMA_NOFIRST122 , +#define IFCOMMA_NOFIRST123 , +#define IFCOMMA_NOFIRST124 , + +#define DEC(x) C2(DEC,x) +#define DEC1024 1023 +#define DEC1023 1022 +#define DEC1022 1021 +#define DEC1021 1020 +#define DEC1020 1019 +#define DEC1019 1018 +#define DEC1018 1017 +#define DEC1017 1016 +#define DEC1016 1015 +#define DEC1015 1014 +#define DEC1014 1013 +#define DEC1013 1012 +#define DEC1012 1011 +#define DEC1011 1010 +#define DEC1010 1009 +#define DEC1009 1008 +#define DEC1008 1007 +#define DEC1007 1006 +#define DEC1006 1005 +#define DEC1005 1004 +#define DEC1004 1003 +#define DEC1003 1002 +#define DEC1002 1001 +#define DEC1001 1000 +#define DEC1000 999 +#define DEC999 998 +#define DEC998 997 +#define DEC997 996 +#define DEC996 995 +#define DEC995 994 +#define DEC994 993 +#define DEC993 992 +#define DEC992 991 +#define DEC991 990 +#define DEC990 989 +#define DEC989 988 +#define DEC988 987 +#define DEC987 986 +#define DEC986 985 +#define DEC985 984 +#define DEC984 983 +#define DEC983 982 +#define DEC982 981 +#define DEC981 980 +#define DEC980 979 +#define DEC979 978 +#define DEC978 977 +#define DEC977 976 +#define DEC976 975 +#define DEC975 974 +#define DEC974 973 +#define DEC973 972 +#define DEC972 971 +#define DEC971 970 +#define DEC970 969 +#define DEC969 968 +#define DEC968 967 +#define DEC967 966 +#define DEC966 965 +#define DEC965 964 +#define DEC964 963 +#define DEC963 962 +#define DEC962 961 +#define DEC961 960 +#define DEC960 959 +#define DEC959 958 +#define DEC958 957 +#define DEC957 956 +#define DEC956 955 +#define DEC955 954 +#define DEC954 953 +#define DEC953 952 +#define DEC952 951 +#define DEC951 950 +#define DEC950 949 +#define DEC949 948 +#define DEC948 947 +#define DEC947 946 +#define DEC946 945 +#define DEC945 944 +#define DEC944 943 +#define DEC943 942 +#define DEC942 941 +#define DEC941 940 +#define DEC940 939 +#define DEC939 938 +#define DEC938 937 +#define DEC937 936 +#define DEC936 935 +#define DEC935 934 +#define DEC934 933 +#define DEC933 932 +#define DEC932 931 +#define DEC931 930 +#define DEC930 929 +#define DEC929 928 +#define DEC928 927 +#define DEC927 926 +#define DEC926 925 +#define DEC925 924 +#define DEC924 923 +#define DEC923 922 +#define DEC922 921 +#define DEC921 920 +#define DEC920 919 +#define DEC919 918 +#define DEC918 917 +#define DEC917 916 +#define DEC916 915 +#define DEC915 914 +#define DEC914 913 +#define DEC913 912 +#define DEC912 911 +#define DEC911 910 +#define DEC910 909 +#define DEC909 908 +#define DEC908 907 +#define DEC907 906 +#define DEC906 905 +#define DEC905 904 +#define DEC904 903 +#define DEC903 902 +#define DEC902 901 +#define DEC901 900 +#define DEC900 899 +#define DEC899 898 +#define DEC898 897 +#define DEC897 896 +#define DEC896 895 +#define DEC895 894 +#define DEC894 893 +#define DEC893 892 +#define DEC892 891 +#define DEC891 890 +#define DEC890 889 +#define DEC889 888 +#define DEC888 887 +#define DEC887 886 +#define DEC886 885 +#define DEC885 884 +#define DEC884 883 +#define DEC883 882 +#define DEC882 881 +#define DEC881 880 +#define DEC880 879 +#define DEC879 878 +#define DEC878 877 +#define DEC877 876 +#define DEC876 875 +#define DEC875 874 +#define DEC874 873 +#define DEC873 872 +#define DEC872 871 +#define DEC871 870 +#define DEC870 869 +#define DEC869 868 +#define DEC868 867 +#define DEC867 866 +#define DEC866 865 +#define DEC865 864 +#define DEC864 863 +#define DEC863 862 +#define DEC862 861 +#define DEC861 860 +#define DEC860 859 +#define DEC859 858 +#define DEC858 857 +#define DEC857 856 +#define DEC856 855 +#define DEC855 854 +#define DEC854 853 +#define DEC853 852 +#define DEC852 851 +#define DEC851 850 +#define DEC850 849 +#define DEC849 848 +#define DEC848 847 +#define DEC847 846 +#define DEC846 845 +#define DEC845 844 +#define DEC844 843 +#define DEC843 842 +#define DEC842 841 +#define DEC841 840 +#define DEC840 839 +#define DEC839 838 +#define DEC838 837 +#define DEC837 836 +#define DEC836 835 +#define DEC835 834 +#define DEC834 833 +#define DEC833 832 +#define DEC832 831 +#define DEC831 830 +#define DEC830 829 +#define DEC829 828 +#define DEC828 827 +#define DEC827 826 +#define DEC826 825 +#define DEC825 824 +#define DEC824 823 +#define DEC823 822 +#define DEC822 821 +#define DEC821 820 +#define DEC820 819 +#define DEC819 818 +#define DEC818 817 +#define DEC817 816 +#define DEC816 815 +#define DEC815 814 +#define DEC814 813 +#define DEC813 812 +#define DEC812 811 +#define DEC811 810 +#define DEC810 809 +#define DEC809 808 +#define DEC808 807 +#define DEC807 806 +#define DEC806 805 +#define DEC805 804 +#define DEC804 803 +#define DEC803 802 +#define DEC802 801 +#define DEC801 800 +#define DEC800 799 +#define DEC799 798 +#define DEC798 797 +#define DEC797 796 +#define DEC796 795 +#define DEC795 794 +#define DEC794 793 +#define DEC793 792 +#define DEC792 791 +#define DEC791 790 +#define DEC790 789 +#define DEC789 788 +#define DEC788 787 +#define DEC787 786 +#define DEC786 785 +#define DEC785 784 +#define DEC784 783 +#define DEC783 782 +#define DEC782 781 +#define DEC781 780 +#define DEC780 779 +#define DEC779 778 +#define DEC778 777 +#define DEC777 776 +#define DEC776 775 +#define DEC775 774 +#define DEC774 773 +#define DEC773 772 +#define DEC772 771 +#define DEC771 770 +#define DEC770 769 +#define DEC769 768 +#define DEC768 767 +#define DEC767 766 +#define DEC766 765 +#define DEC765 764 +#define DEC764 763 +#define DEC763 762 +#define DEC762 761 +#define DEC761 760 +#define DEC760 759 +#define DEC759 758 +#define DEC758 757 +#define DEC757 756 +#define DEC756 755 +#define DEC755 754 +#define DEC754 753 +#define DEC753 752 +#define DEC752 751 +#define DEC751 750 +#define DEC750 749 +#define DEC749 748 +#define DEC748 747 +#define DEC747 746 +#define DEC746 745 +#define DEC745 744 +#define DEC744 743 +#define DEC743 742 +#define DEC742 741 +#define DEC741 740 +#define DEC740 739 +#define DEC739 738 +#define DEC738 737 +#define DEC737 736 +#define DEC736 735 +#define DEC735 734 +#define DEC734 733 +#define DEC733 732 +#define DEC732 731 +#define DEC731 730 +#define DEC730 729 +#define DEC729 728 +#define DEC728 727 +#define DEC727 726 +#define DEC726 725 +#define DEC725 724 +#define DEC724 723 +#define DEC723 722 +#define DEC722 721 +#define DEC721 720 +#define DEC720 719 +#define DEC719 718 +#define DEC718 717 +#define DEC717 716 +#define DEC716 715 +#define DEC715 714 +#define DEC714 713 +#define DEC713 712 +#define DEC712 711 +#define DEC711 710 +#define DEC710 709 +#define DEC709 708 +#define DEC708 707 +#define DEC707 706 +#define DEC706 705 +#define DEC705 704 +#define DEC704 703 +#define DEC703 702 +#define DEC702 701 +#define DEC701 700 +#define DEC700 699 +#define DEC699 698 +#define DEC698 697 +#define DEC697 696 +#define DEC696 695 +#define DEC695 694 +#define DEC694 693 +#define DEC693 692 +#define DEC692 691 +#define DEC691 690 +#define DEC690 689 +#define DEC689 688 +#define DEC688 687 +#define DEC687 686 +#define DEC686 685 +#define DEC685 684 +#define DEC684 683 +#define DEC683 682 +#define DEC682 681 +#define DEC681 680 +#define DEC680 679 +#define DEC679 678 +#define DEC678 677 +#define DEC677 676 +#define DEC676 675 +#define DEC675 674 +#define DEC674 673 +#define DEC673 672 +#define DEC672 671 +#define DEC671 670 +#define DEC670 669 +#define DEC669 668 +#define DEC668 667 +#define DEC667 666 +#define DEC666 665 +#define DEC665 664 +#define DEC664 663 +#define DEC663 662 +#define DEC662 661 +#define DEC661 660 +#define DEC660 659 +#define DEC659 658 +#define DEC658 657 +#define DEC657 656 +#define DEC656 655 +#define DEC655 654 +#define DEC654 653 +#define DEC653 652 +#define DEC652 651 +#define DEC651 650 +#define DEC650 649 +#define DEC649 648 +#define DEC648 647 +#define DEC647 646 +#define DEC646 645 +#define DEC645 644 +#define DEC644 643 +#define DEC643 642 +#define DEC642 641 +#define DEC641 640 +#define DEC640 639 +#define DEC639 638 +#define DEC638 637 +#define DEC637 636 +#define DEC636 635 +#define DEC635 634 +#define DEC634 633 +#define DEC633 632 +#define DEC632 631 +#define DEC631 630 +#define DEC630 629 +#define DEC629 628 +#define DEC628 627 +#define DEC627 626 +#define DEC626 625 +#define DEC625 624 +#define DEC624 623 +#define DEC623 622 +#define DEC622 621 +#define DEC621 620 +#define DEC620 619 +#define DEC619 618 +#define DEC618 617 +#define DEC617 616 +#define DEC616 615 +#define DEC615 614 +#define DEC614 613 +#define DEC613 612 +#define DEC612 611 +#define DEC611 610 +#define DEC610 609 +#define DEC609 608 +#define DEC608 607 +#define DEC607 606 +#define DEC606 605 +#define DEC605 604 +#define DEC604 603 +#define DEC603 602 +#define DEC602 601 +#define DEC601 600 +#define DEC600 599 +#define DEC599 598 +#define DEC598 597 +#define DEC597 596 +#define DEC596 595 +#define DEC595 594 +#define DEC594 593 +#define DEC593 592 +#define DEC592 591 +#define DEC591 590 +#define DEC590 589 +#define DEC589 588 +#define DEC588 587 +#define DEC587 586 +#define DEC586 585 +#define DEC585 584 +#define DEC584 583 +#define DEC583 582 +#define DEC582 581 +#define DEC581 580 +#define DEC580 579 +#define DEC579 578 +#define DEC578 577 +#define DEC577 576 +#define DEC576 575 +#define DEC575 574 +#define DEC574 573 +#define DEC573 572 +#define DEC572 571 +#define DEC571 570 +#define DEC570 569 +#define DEC569 568 +#define DEC568 567 +#define DEC567 566 +#define DEC566 565 +#define DEC565 564 +#define DEC564 563 +#define DEC563 562 +#define DEC562 561 +#define DEC561 560 +#define DEC560 559 +#define DEC559 558 +#define DEC558 557 +#define DEC557 556 +#define DEC556 555 +#define DEC555 554 +#define DEC554 553 +#define DEC553 552 +#define DEC552 551 +#define DEC551 550 +#define DEC550 549 +#define DEC549 548 +#define DEC548 547 +#define DEC547 546 +#define DEC546 545 +#define DEC545 544 +#define DEC544 543 +#define DEC543 542 +#define DEC542 541 +#define DEC541 540 +#define DEC540 539 +#define DEC539 538 +#define DEC538 537 +#define DEC537 536 +#define DEC536 535 +#define DEC535 534 +#define DEC534 533 +#define DEC533 532 +#define DEC532 531 +#define DEC531 530 +#define DEC530 529 +#define DEC529 528 +#define DEC528 527 +#define DEC527 526 +#define DEC526 525 +#define DEC525 524 +#define DEC524 523 +#define DEC523 522 +#define DEC522 521 +#define DEC521 520 +#define DEC520 519 +#define DEC519 518 +#define DEC518 517 +#define DEC517 516 +#define DEC516 515 +#define DEC515 514 +#define DEC514 513 +#define DEC513 512 +#define DEC512 511 +#define DEC511 510 +#define DEC510 509 +#define DEC509 508 +#define DEC508 507 +#define DEC507 506 +#define DEC506 505 +#define DEC505 504 +#define DEC504 503 +#define DEC503 502 +#define DEC502 501 +#define DEC501 500 +#define DEC500 499 +#define DEC499 498 +#define DEC498 497 +#define DEC497 496 +#define DEC496 495 +#define DEC495 494 +#define DEC494 493 +#define DEC493 492 +#define DEC492 491 +#define DEC491 490 +#define DEC490 489 +#define DEC489 488 +#define DEC488 487 +#define DEC487 486 +#define DEC486 485 +#define DEC485 484 +#define DEC484 483 +#define DEC483 482 +#define DEC482 481 +#define DEC481 480 +#define DEC480 479 +#define DEC479 478 +#define DEC478 477 +#define DEC477 476 +#define DEC476 475 +#define DEC475 474 +#define DEC474 473 +#define DEC473 472 +#define DEC472 471 +#define DEC471 470 +#define DEC470 469 +#define DEC469 468 +#define DEC468 467 +#define DEC467 466 +#define DEC466 465 +#define DEC465 464 +#define DEC464 463 +#define DEC463 462 +#define DEC462 461 +#define DEC461 460 +#define DEC460 459 +#define DEC459 458 +#define DEC458 457 +#define DEC457 456 +#define DEC456 455 +#define DEC455 454 +#define DEC454 453 +#define DEC453 452 +#define DEC452 451 +#define DEC451 450 +#define DEC450 449 +#define DEC449 448 +#define DEC448 447 +#define DEC447 446 +#define DEC446 445 +#define DEC445 444 +#define DEC444 443 +#define DEC443 442 +#define DEC442 441 +#define DEC441 440 +#define DEC440 439 +#define DEC439 438 +#define DEC438 437 +#define DEC437 436 +#define DEC436 435 +#define DEC435 434 +#define DEC434 433 +#define DEC433 432 +#define DEC432 431 +#define DEC431 430 +#define DEC430 429 +#define DEC429 428 +#define DEC428 427 +#define DEC427 426 +#define DEC426 425 +#define DEC425 424 +#define DEC424 423 +#define DEC423 422 +#define DEC422 421 +#define DEC421 420 +#define DEC420 419 +#define DEC419 418 +#define DEC418 417 +#define DEC417 416 +#define DEC416 415 +#define DEC415 414 +#define DEC414 413 +#define DEC413 412 +#define DEC412 411 +#define DEC411 410 +#define DEC410 409 +#define DEC409 408 +#define DEC408 407 +#define DEC407 406 +#define DEC406 405 +#define DEC405 404 +#define DEC404 403 +#define DEC403 402 +#define DEC402 401 +#define DEC401 400 +#define DEC400 399 +#define DEC399 398 +#define DEC398 397 +#define DEC397 396 +#define DEC396 395 +#define DEC395 394 +#define DEC394 393 +#define DEC393 392 +#define DEC392 391 +#define DEC391 390 +#define DEC390 389 +#define DEC389 388 +#define DEC388 387 +#define DEC387 386 +#define DEC386 385 +#define DEC385 384 +#define DEC384 383 +#define DEC383 382 +#define DEC382 381 +#define DEC381 380 +#define DEC380 379 +#define DEC379 378 +#define DEC378 377 +#define DEC377 376 +#define DEC376 375 +#define DEC375 374 +#define DEC374 373 +#define DEC373 372 +#define DEC372 371 +#define DEC371 370 +#define DEC370 369 +#define DEC369 368 +#define DEC368 367 +#define DEC367 366 +#define DEC366 365 +#define DEC365 364 +#define DEC364 363 +#define DEC363 362 +#define DEC362 361 +#define DEC361 360 +#define DEC360 359 +#define DEC359 358 +#define DEC358 357 +#define DEC357 356 +#define DEC356 355 +#define DEC355 354 +#define DEC354 353 +#define DEC353 352 +#define DEC352 351 +#define DEC351 350 +#define DEC350 349 +#define DEC349 348 +#define DEC348 347 +#define DEC347 346 +#define DEC346 345 +#define DEC345 344 +#define DEC344 343 +#define DEC343 342 +#define DEC342 341 +#define DEC341 340 +#define DEC340 339 +#define DEC339 338 +#define DEC338 337 +#define DEC337 336 +#define DEC336 335 +#define DEC335 334 +#define DEC334 333 +#define DEC333 332 +#define DEC332 331 +#define DEC331 330 +#define DEC330 329 +#define DEC329 328 +#define DEC328 327 +#define DEC327 326 +#define DEC326 325 +#define DEC325 324 +#define DEC324 323 +#define DEC323 322 +#define DEC322 321 +#define DEC321 320 +#define DEC320 319 +#define DEC319 318 +#define DEC318 317 +#define DEC317 316 +#define DEC316 315 +#define DEC315 314 +#define DEC314 313 +#define DEC313 312 +#define DEC312 311 +#define DEC311 310 +#define DEC310 309 +#define DEC309 308 +#define DEC308 307 +#define DEC307 306 +#define DEC306 305 +#define DEC305 304 +#define DEC304 303 +#define DEC303 302 +#define DEC302 301 +#define DEC301 300 +#define DEC300 299 +#define DEC299 298 +#define DEC298 297 +#define DEC297 296 +#define DEC296 295 +#define DEC295 294 +#define DEC294 293 +#define DEC293 292 +#define DEC292 291 +#define DEC291 290 +#define DEC290 289 +#define DEC289 288 +#define DEC288 287 +#define DEC287 286 +#define DEC286 285 +#define DEC285 284 +#define DEC284 283 +#define DEC283 282 +#define DEC282 281 +#define DEC281 280 +#define DEC280 279 +#define DEC279 278 +#define DEC278 277 +#define DEC277 276 +#define DEC276 275 +#define DEC275 274 +#define DEC274 273 +#define DEC273 272 +#define DEC272 271 +#define DEC271 270 +#define DEC270 269 +#define DEC269 268 +#define DEC268 267 +#define DEC267 266 +#define DEC266 265 +#define DEC265 264 +#define DEC264 263 +#define DEC263 262 +#define DEC262 261 +#define DEC261 260 +#define DEC260 259 +#define DEC259 258 +#define DEC258 257 +#define DEC257 256 +#define DEC256 255 +#define DEC255 254 +#define DEC254 253 +#define DEC253 252 +#define DEC252 251 +#define DEC251 250 +#define DEC250 249 +#define DEC249 248 +#define DEC248 247 +#define DEC247 246 +#define DEC246 245 +#define DEC245 244 +#define DEC244 243 +#define DEC243 242 +#define DEC242 241 +#define DEC241 240 +#define DEC240 239 +#define DEC239 238 +#define DEC238 237 +#define DEC237 236 +#define DEC236 235 +#define DEC235 234 +#define DEC234 233 +#define DEC233 232 +#define DEC232 231 +#define DEC231 230 +#define DEC230 229 +#define DEC229 228 +#define DEC228 227 +#define DEC227 226 +#define DEC226 225 +#define DEC225 224 +#define DEC224 223 +#define DEC223 222 +#define DEC222 221 +#define DEC221 220 +#define DEC220 219 +#define DEC219 218 +#define DEC218 217 +#define DEC217 216 +#define DEC216 215 +#define DEC215 214 +#define DEC214 213 +#define DEC213 212 +#define DEC212 211 +#define DEC211 210 +#define DEC210 209 +#define DEC209 208 +#define DEC208 207 +#define DEC207 206 +#define DEC206 205 +#define DEC205 204 +#define DEC204 203 +#define DEC203 202 +#define DEC202 201 +#define DEC201 200 +#define DEC200 199 +#define DEC199 198 +#define DEC198 197 +#define DEC197 196 +#define DEC196 195 +#define DEC195 194 +#define DEC194 193 +#define DEC193 192 +#define DEC192 191 +#define DEC191 190 +#define DEC190 189 +#define DEC189 188 +#define DEC188 187 +#define DEC187 186 +#define DEC186 185 +#define DEC185 184 +#define DEC184 183 +#define DEC183 182 +#define DEC182 181 +#define DEC181 180 +#define DEC180 179 +#define DEC179 178 +#define DEC178 177 +#define DEC177 176 +#define DEC176 175 +#define DEC175 174 +#define DEC174 173 +#define DEC173 172 +#define DEC172 171 +#define DEC171 170 +#define DEC170 169 +#define DEC169 168 +#define DEC168 167 +#define DEC167 166 +#define DEC166 165 +#define DEC165 164 +#define DEC164 163 +#define DEC163 162 +#define DEC162 161 +#define DEC161 160 +#define DEC160 159 +#define DEC159 158 +#define DEC158 157 +#define DEC157 156 +#define DEC156 155 +#define DEC155 154 +#define DEC154 153 +#define DEC153 152 +#define DEC152 151 +#define DEC151 150 +#define DEC150 149 +#define DEC149 148 +#define DEC148 147 +#define DEC147 146 +#define DEC146 145 +#define DEC145 144 +#define DEC144 143 +#define DEC143 142 +#define DEC142 141 +#define DEC141 140 +#define DEC140 139 +#define DEC139 138 +#define DEC138 137 +#define DEC137 136 +#define DEC136 135 +#define DEC135 134 +#define DEC134 133 +#define DEC133 132 +#define DEC132 131 +#define DEC131 130 +#define DEC130 129 +#define DEC129 128 +#define DEC128 127 +#define DEC127 126 +#define DEC126 125 +#define DEC125 124 +#define DEC124 123 +#define DEC123 122 +#define DEC122 121 +#define DEC121 120 +#define DEC120 119 +#define DEC119 118 +#define DEC118 117 +#define DEC117 116 +#define DEC116 115 +#define DEC115 114 +#define DEC114 113 +#define DEC113 112 +#define DEC112 111 +#define DEC111 110 +#define DEC110 109 +#define DEC109 108 +#define DEC108 107 +#define DEC107 106 +#define DEC106 105 +#define DEC105 104 +#define DEC104 103 +#define DEC103 102 +#define DEC102 101 +#define DEC101 100 +#define DEC100 99 +#define DEC99 98 +#define DEC98 97 +#define DEC97 96 +#define DEC96 95 +#define DEC95 94 +#define DEC94 93 +#define DEC93 92 +#define DEC92 91 +#define DEC91 90 +#define DEC90 89 +#define DEC89 88 +#define DEC88 87 +#define DEC87 86 +#define DEC86 85 +#define DEC85 84 +#define DEC84 83 +#define DEC83 82 +#define DEC82 81 +#define DEC81 80 +#define DEC80 79 +#define DEC79 78 +#define DEC78 77 +#define DEC77 76 +#define DEC76 75 +#define DEC75 74 +#define DEC74 73 +#define DEC73 72 +#define DEC72 71 +#define DEC71 70 +#define DEC70 69 +#define DEC69 68 +#define DEC68 67 +#define DEC67 66 +#define DEC66 65 +#define DEC65 64 +#define DEC64 63 +#define DEC63 62 +#define DEC62 61 +#define DEC61 60 +#define DEC60 59 +#define DEC59 58 +#define DEC58 57 +#define DEC57 56 +#define DEC56 55 +#define DEC55 54 +#define DEC54 53 +#define DEC53 52 +#define DEC52 51 +#define DEC51 50 +#define DEC50 49 +#define DEC49 48 +#define DEC48 47 +#define DEC47 46 +#define DEC46 45 +#define DEC45 44 +#define DEC44 43 +#define DEC43 42 +#define DEC42 41 +#define DEC41 40 +#define DEC40 39 +#define DEC39 38 +#define DEC38 37 +#define DEC37 36 +#define DEC36 35 +#define DEC35 34 +#define DEC34 33 +#define DEC33 32 +#define DEC32 31 +#define DEC31 30 +#define DEC30 29 +#define DEC29 28 +#define DEC28 27 +#define DEC27 26 +#define DEC26 25 +#define DEC25 24 +#define DEC24 23 +#define DEC23 22 +#define DEC22 21 +#define DEC21 20 +#define DEC20 19 +#define DEC19 18 +#define DEC18 17 +#define DEC17 16 +#define DEC16 15 +#define DEC15 14 +#define DEC14 13 +#define DEC13 12 +#define DEC12 11 +#define DEC11 10 +#define DEC10 9 +#define DEC9 8 +#define DEC8 7 +#define DEC7 6 +#define DEC6 5 +#define DEC5 4 +#define DEC4 3 +#define DEC3 2 +#define DEC2 1 +#define DEC1 0 + +#define INC(x) C2(INC,x) +#define INC1024 1025 +#define INC1023 1024 +#define INC1022 1023 +#define INC1021 1022 +#define INC1020 1021 +#define INC1019 1020 +#define INC1018 1019 +#define INC1017 1018 +#define INC1016 1017 +#define INC1015 1016 +#define INC1014 1015 +#define INC1013 1014 +#define INC1012 1013 +#define INC1011 1012 +#define INC1010 1011 +#define INC1009 1010 +#define INC1008 1009 +#define INC1007 1008 +#define INC1006 1007 +#define INC1005 1006 +#define INC1004 1005 +#define INC1003 1004 +#define INC1002 1003 +#define INC1001 1002 +#define INC1000 1001 +#define INC999 1000 +#define INC998 999 +#define INC997 998 +#define INC996 997 +#define INC995 996 +#define INC994 995 +#define INC993 994 +#define INC992 993 +#define INC991 992 +#define INC990 991 +#define INC989 990 +#define INC988 989 +#define INC987 988 +#define INC986 987 +#define INC985 986 +#define INC984 985 +#define INC983 984 +#define INC982 983 +#define INC981 982 +#define INC980 981 +#define INC979 980 +#define INC978 979 +#define INC977 978 +#define INC976 977 +#define INC975 976 +#define INC974 975 +#define INC973 974 +#define INC972 973 +#define INC971 972 +#define INC970 971 +#define INC969 970 +#define INC968 969 +#define INC967 968 +#define INC966 967 +#define INC965 966 +#define INC964 965 +#define INC963 964 +#define INC962 963 +#define INC961 962 +#define INC960 961 +#define INC959 960 +#define INC958 959 +#define INC957 958 +#define INC956 957 +#define INC955 956 +#define INC954 955 +#define INC953 954 +#define INC952 953 +#define INC951 952 +#define INC950 951 +#define INC949 950 +#define INC948 949 +#define INC947 948 +#define INC946 947 +#define INC945 946 +#define INC944 945 +#define INC943 944 +#define INC942 943 +#define INC941 942 +#define INC940 941 +#define INC939 940 +#define INC938 939 +#define INC937 938 +#define INC936 937 +#define INC935 936 +#define INC934 935 +#define INC933 934 +#define INC932 933 +#define INC931 932 +#define INC930 931 +#define INC929 930 +#define INC928 929 +#define INC927 928 +#define INC926 927 +#define INC925 926 +#define INC924 925 +#define INC923 924 +#define INC922 923 +#define INC921 922 +#define INC920 921 +#define INC919 920 +#define INC918 919 +#define INC917 918 +#define INC916 917 +#define INC915 916 +#define INC914 915 +#define INC913 914 +#define INC912 913 +#define INC911 912 +#define INC910 911 +#define INC909 910 +#define INC908 909 +#define INC907 908 +#define INC906 907 +#define INC905 906 +#define INC904 905 +#define INC903 904 +#define INC902 903 +#define INC901 902 +#define INC900 901 +#define INC899 900 +#define INC898 899 +#define INC897 898 +#define INC896 897 +#define INC895 896 +#define INC894 895 +#define INC893 894 +#define INC892 893 +#define INC891 892 +#define INC890 891 +#define INC889 890 +#define INC888 889 +#define INC887 888 +#define INC886 887 +#define INC885 886 +#define INC884 885 +#define INC883 884 +#define INC882 883 +#define INC881 882 +#define INC880 881 +#define INC879 880 +#define INC878 879 +#define INC877 878 +#define INC876 877 +#define INC875 876 +#define INC874 875 +#define INC873 874 +#define INC872 873 +#define INC871 872 +#define INC870 871 +#define INC869 870 +#define INC868 869 +#define INC867 868 +#define INC866 867 +#define INC865 866 +#define INC864 865 +#define INC863 864 +#define INC862 863 +#define INC861 862 +#define INC860 861 +#define INC859 860 +#define INC858 859 +#define INC857 858 +#define INC856 857 +#define INC855 856 +#define INC854 855 +#define INC853 854 +#define INC852 853 +#define INC851 852 +#define INC850 851 +#define INC849 850 +#define INC848 849 +#define INC847 848 +#define INC846 847 +#define INC845 846 +#define INC844 845 +#define INC843 844 +#define INC842 843 +#define INC841 842 +#define INC840 841 +#define INC839 840 +#define INC838 839 +#define INC837 838 +#define INC836 837 +#define INC835 836 +#define INC834 835 +#define INC833 834 +#define INC832 833 +#define INC831 832 +#define INC830 831 +#define INC829 830 +#define INC828 829 +#define INC827 828 +#define INC826 827 +#define INC825 826 +#define INC824 825 +#define INC823 824 +#define INC822 823 +#define INC821 822 +#define INC820 821 +#define INC819 820 +#define INC818 819 +#define INC817 818 +#define INC816 817 +#define INC815 816 +#define INC814 815 +#define INC813 814 +#define INC812 813 +#define INC811 812 +#define INC810 811 +#define INC809 810 +#define INC808 809 +#define INC807 808 +#define INC806 807 +#define INC805 806 +#define INC804 805 +#define INC803 804 +#define INC802 803 +#define INC801 802 +#define INC800 801 +#define INC799 800 +#define INC798 799 +#define INC797 798 +#define INC796 797 +#define INC795 796 +#define INC794 795 +#define INC793 794 +#define INC792 793 +#define INC791 792 +#define INC790 791 +#define INC789 790 +#define INC788 789 +#define INC787 788 +#define INC786 787 +#define INC785 786 +#define INC784 785 +#define INC783 784 +#define INC782 783 +#define INC781 782 +#define INC780 781 +#define INC779 780 +#define INC778 779 +#define INC777 778 +#define INC776 777 +#define INC775 776 +#define INC774 775 +#define INC773 774 +#define INC772 773 +#define INC771 772 +#define INC770 771 +#define INC769 770 +#define INC768 769 +#define INC767 768 +#define INC766 767 +#define INC765 766 +#define INC764 765 +#define INC763 764 +#define INC762 763 +#define INC761 762 +#define INC760 761 +#define INC759 760 +#define INC758 759 +#define INC757 758 +#define INC756 757 +#define INC755 756 +#define INC754 755 +#define INC753 754 +#define INC752 753 +#define INC751 752 +#define INC750 751 +#define INC749 750 +#define INC748 749 +#define INC747 748 +#define INC746 747 +#define INC745 746 +#define INC744 745 +#define INC743 744 +#define INC742 743 +#define INC741 742 +#define INC740 741 +#define INC739 740 +#define INC738 739 +#define INC737 738 +#define INC736 737 +#define INC735 736 +#define INC734 735 +#define INC733 734 +#define INC732 733 +#define INC731 732 +#define INC730 731 +#define INC729 730 +#define INC728 729 +#define INC727 728 +#define INC726 727 +#define INC725 726 +#define INC724 725 +#define INC723 724 +#define INC722 723 +#define INC721 722 +#define INC720 721 +#define INC719 720 +#define INC718 719 +#define INC717 718 +#define INC716 717 +#define INC715 716 +#define INC714 715 +#define INC713 714 +#define INC712 713 +#define INC711 712 +#define INC710 711 +#define INC709 710 +#define INC708 709 +#define INC707 708 +#define INC706 707 +#define INC705 706 +#define INC704 705 +#define INC703 704 +#define INC702 703 +#define INC701 702 +#define INC700 701 +#define INC699 700 +#define INC698 699 +#define INC697 698 +#define INC696 697 +#define INC695 696 +#define INC694 695 +#define INC693 694 +#define INC692 693 +#define INC691 692 +#define INC690 691 +#define INC689 690 +#define INC688 689 +#define INC687 688 +#define INC686 687 +#define INC685 686 +#define INC684 685 +#define INC683 684 +#define INC682 683 +#define INC681 682 +#define INC680 681 +#define INC679 680 +#define INC678 679 +#define INC677 678 +#define INC676 677 +#define INC675 676 +#define INC674 675 +#define INC673 674 +#define INC672 673 +#define INC671 672 +#define INC670 671 +#define INC669 670 +#define INC668 669 +#define INC667 668 +#define INC666 667 +#define INC665 666 +#define INC664 665 +#define INC663 664 +#define INC662 663 +#define INC661 662 +#define INC660 661 +#define INC659 660 +#define INC658 659 +#define INC657 658 +#define INC656 657 +#define INC655 656 +#define INC654 655 +#define INC653 654 +#define INC652 653 +#define INC651 652 +#define INC650 651 +#define INC649 650 +#define INC648 649 +#define INC647 648 +#define INC646 647 +#define INC645 646 +#define INC644 645 +#define INC643 644 +#define INC642 643 +#define INC641 642 +#define INC640 641 +#define INC639 640 +#define INC638 639 +#define INC637 638 +#define INC636 637 +#define INC635 636 +#define INC634 635 +#define INC633 634 +#define INC632 633 +#define INC631 632 +#define INC630 631 +#define INC629 630 +#define INC628 629 +#define INC627 628 +#define INC626 627 +#define INC625 626 +#define INC624 625 +#define INC623 624 +#define INC622 623 +#define INC621 622 +#define INC620 621 +#define INC619 620 +#define INC618 619 +#define INC617 618 +#define INC616 617 +#define INC615 616 +#define INC614 615 +#define INC613 614 +#define INC612 613 +#define INC611 612 +#define INC610 611 +#define INC609 610 +#define INC608 609 +#define INC607 608 +#define INC606 607 +#define INC605 606 +#define INC604 605 +#define INC603 604 +#define INC602 603 +#define INC601 602 +#define INC600 601 +#define INC599 600 +#define INC598 599 +#define INC597 598 +#define INC596 597 +#define INC595 596 +#define INC594 595 +#define INC593 594 +#define INC592 593 +#define INC591 592 +#define INC590 591 +#define INC589 590 +#define INC588 589 +#define INC587 588 +#define INC586 587 +#define INC585 586 +#define INC584 585 +#define INC583 584 +#define INC582 583 +#define INC581 582 +#define INC580 581 +#define INC579 580 +#define INC578 579 +#define INC577 578 +#define INC576 577 +#define INC575 576 +#define INC574 575 +#define INC573 574 +#define INC572 573 +#define INC571 572 +#define INC570 571 +#define INC569 570 +#define INC568 569 +#define INC567 568 +#define INC566 567 +#define INC565 566 +#define INC564 565 +#define INC563 564 +#define INC562 563 +#define INC561 562 +#define INC560 561 +#define INC559 560 +#define INC558 559 +#define INC557 558 +#define INC556 557 +#define INC555 556 +#define INC554 555 +#define INC553 554 +#define INC552 553 +#define INC551 552 +#define INC550 551 +#define INC549 550 +#define INC548 549 +#define INC547 548 +#define INC546 547 +#define INC545 546 +#define INC544 545 +#define INC543 544 +#define INC542 543 +#define INC541 542 +#define INC540 541 +#define INC539 540 +#define INC538 539 +#define INC537 538 +#define INC536 537 +#define INC535 536 +#define INC534 535 +#define INC533 534 +#define INC532 533 +#define INC531 532 +#define INC530 531 +#define INC529 530 +#define INC528 529 +#define INC527 528 +#define INC526 527 +#define INC525 526 +#define INC524 525 +#define INC523 524 +#define INC522 523 +#define INC521 522 +#define INC520 521 +#define INC519 520 +#define INC518 519 +#define INC517 518 +#define INC516 517 +#define INC515 516 +#define INC514 515 +#define INC513 514 +#define INC512 513 +#define INC511 512 +#define INC510 511 +#define INC509 510 +#define INC508 509 +#define INC507 508 +#define INC506 507 +#define INC505 506 +#define INC504 505 +#define INC503 504 +#define INC502 503 +#define INC501 502 +#define INC500 501 +#define INC499 500 +#define INC498 499 +#define INC497 498 +#define INC496 497 +#define INC495 496 +#define INC494 495 +#define INC493 494 +#define INC492 493 +#define INC491 492 +#define INC490 491 +#define INC489 490 +#define INC488 489 +#define INC487 488 +#define INC486 487 +#define INC485 486 +#define INC484 485 +#define INC483 484 +#define INC482 483 +#define INC481 482 +#define INC480 481 +#define INC479 480 +#define INC478 479 +#define INC477 478 +#define INC476 477 +#define INC475 476 +#define INC474 475 +#define INC473 474 +#define INC472 473 +#define INC471 472 +#define INC470 471 +#define INC469 470 +#define INC468 469 +#define INC467 468 +#define INC466 467 +#define INC465 466 +#define INC464 465 +#define INC463 464 +#define INC462 463 +#define INC461 462 +#define INC460 461 +#define INC459 460 +#define INC458 459 +#define INC457 458 +#define INC456 457 +#define INC455 456 +#define INC454 455 +#define INC453 454 +#define INC452 453 +#define INC451 452 +#define INC450 451 +#define INC449 450 +#define INC448 449 +#define INC447 448 +#define INC446 447 +#define INC445 446 +#define INC444 445 +#define INC443 444 +#define INC442 443 +#define INC441 442 +#define INC440 441 +#define INC439 440 +#define INC438 439 +#define INC437 438 +#define INC436 437 +#define INC435 436 +#define INC434 435 +#define INC433 434 +#define INC432 433 +#define INC431 432 +#define INC430 431 +#define INC429 430 +#define INC428 429 +#define INC427 428 +#define INC426 427 +#define INC425 426 +#define INC424 425 +#define INC423 424 +#define INC422 423 +#define INC421 422 +#define INC420 421 +#define INC419 420 +#define INC418 419 +#define INC417 418 +#define INC416 417 +#define INC415 416 +#define INC414 415 +#define INC413 414 +#define INC412 413 +#define INC411 412 +#define INC410 411 +#define INC409 410 +#define INC408 409 +#define INC407 408 +#define INC406 407 +#define INC405 406 +#define INC404 405 +#define INC403 404 +#define INC402 403 +#define INC401 402 +#define INC400 401 +#define INC399 400 +#define INC398 399 +#define INC397 398 +#define INC396 397 +#define INC395 396 +#define INC394 395 +#define INC393 394 +#define INC392 393 +#define INC391 392 +#define INC390 391 +#define INC389 390 +#define INC388 389 +#define INC387 388 +#define INC386 387 +#define INC385 386 +#define INC384 385 +#define INC383 384 +#define INC382 383 +#define INC381 382 +#define INC380 381 +#define INC379 380 +#define INC378 379 +#define INC377 378 +#define INC376 377 +#define INC375 376 +#define INC374 375 +#define INC373 374 +#define INC372 373 +#define INC371 372 +#define INC370 371 +#define INC369 370 +#define INC368 369 +#define INC367 368 +#define INC366 367 +#define INC365 366 +#define INC364 365 +#define INC363 364 +#define INC362 363 +#define INC361 362 +#define INC360 361 +#define INC359 360 +#define INC358 359 +#define INC357 358 +#define INC356 357 +#define INC355 356 +#define INC354 355 +#define INC353 354 +#define INC352 353 +#define INC351 352 +#define INC350 351 +#define INC349 350 +#define INC348 349 +#define INC347 348 +#define INC346 347 +#define INC345 346 +#define INC344 345 +#define INC343 344 +#define INC342 343 +#define INC341 342 +#define INC340 341 +#define INC339 340 +#define INC338 339 +#define INC337 338 +#define INC336 337 +#define INC335 336 +#define INC334 335 +#define INC333 334 +#define INC332 333 +#define INC331 332 +#define INC330 331 +#define INC329 330 +#define INC328 329 +#define INC327 328 +#define INC326 327 +#define INC325 326 +#define INC324 325 +#define INC323 324 +#define INC322 323 +#define INC321 322 +#define INC320 321 +#define INC319 320 +#define INC318 319 +#define INC317 318 +#define INC316 317 +#define INC315 316 +#define INC314 315 +#define INC313 314 +#define INC312 313 +#define INC311 312 +#define INC310 311 +#define INC309 310 +#define INC308 309 +#define INC307 308 +#define INC306 307 +#define INC305 306 +#define INC304 305 +#define INC303 304 +#define INC302 303 +#define INC301 302 +#define INC300 301 +#define INC299 300 +#define INC298 299 +#define INC297 298 +#define INC296 297 +#define INC295 296 +#define INC294 295 +#define INC293 294 +#define INC292 293 +#define INC291 292 +#define INC290 291 +#define INC289 290 +#define INC288 289 +#define INC287 288 +#define INC286 287 +#define INC285 286 +#define INC284 285 +#define INC283 284 +#define INC282 283 +#define INC281 282 +#define INC280 281 +#define INC279 280 +#define INC278 279 +#define INC277 278 +#define INC276 277 +#define INC275 276 +#define INC274 275 +#define INC273 274 +#define INC272 273 +#define INC271 272 +#define INC270 271 +#define INC269 270 +#define INC268 269 +#define INC267 268 +#define INC266 267 +#define INC265 266 +#define INC264 265 +#define INC263 264 +#define INC262 263 +#define INC261 262 +#define INC260 261 +#define INC259 260 +#define INC258 259 +#define INC257 258 +#define INC256 257 +#define INC255 256 +#define INC254 255 +#define INC253 254 +#define INC252 253 +#define INC251 252 +#define INC250 251 +#define INC249 250 +#define INC248 249 +#define INC247 248 +#define INC246 247 +#define INC245 246 +#define INC244 245 +#define INC243 244 +#define INC242 243 +#define INC241 242 +#define INC240 241 +#define INC239 240 +#define INC238 239 +#define INC237 238 +#define INC236 237 +#define INC235 236 +#define INC234 235 +#define INC233 234 +#define INC232 233 +#define INC231 232 +#define INC230 231 +#define INC229 230 +#define INC228 229 +#define INC227 228 +#define INC226 227 +#define INC225 226 +#define INC224 225 +#define INC223 224 +#define INC222 223 +#define INC221 222 +#define INC220 221 +#define INC219 220 +#define INC218 219 +#define INC217 218 +#define INC216 217 +#define INC215 216 +#define INC214 215 +#define INC213 214 +#define INC212 213 +#define INC211 212 +#define INC210 211 +#define INC209 210 +#define INC208 209 +#define INC207 208 +#define INC206 207 +#define INC205 206 +#define INC204 205 +#define INC203 204 +#define INC202 203 +#define INC201 202 +#define INC200 201 +#define INC199 200 +#define INC198 199 +#define INC197 198 +#define INC196 197 +#define INC195 196 +#define INC194 195 +#define INC193 194 +#define INC192 193 +#define INC191 192 +#define INC190 191 +#define INC189 190 +#define INC188 189 +#define INC187 188 +#define INC186 187 +#define INC185 186 +#define INC184 185 +#define INC183 184 +#define INC182 183 +#define INC181 182 +#define INC180 181 +#define INC179 180 +#define INC178 179 +#define INC177 178 +#define INC176 177 +#define INC175 176 +#define INC174 175 +#define INC173 174 +#define INC172 173 +#define INC171 172 +#define INC170 171 +#define INC169 170 +#define INC168 169 +#define INC167 168 +#define INC166 167 +#define INC165 166 +#define INC164 165 +#define INC163 164 +#define INC162 163 +#define INC161 162 +#define INC160 161 +#define INC159 160 +#define INC158 159 +#define INC157 158 +#define INC156 157 +#define INC155 156 +#define INC154 155 +#define INC153 154 +#define INC152 153 +#define INC151 152 +#define INC150 151 +#define INC149 150 +#define INC148 149 +#define INC147 148 +#define INC146 147 +#define INC145 146 +#define INC144 145 +#define INC143 144 +#define INC142 143 +#define INC141 142 +#define INC140 141 +#define INC139 140 +#define INC138 139 +#define INC137 138 +#define INC136 137 +#define INC135 136 +#define INC134 135 +#define INC133 134 +#define INC132 133 +#define INC131 132 +#define INC130 131 +#define INC129 130 +#define INC128 129 +#define INC127 128 +#define INC126 127 +#define INC125 126 +#define INC124 125 +#define INC123 124 +#define INC122 123 +#define INC121 122 +#define INC120 121 +#define INC119 120 +#define INC118 119 +#define INC117 118 +#define INC116 117 +#define INC115 116 +#define INC114 115 +#define INC113 114 +#define INC112 113 +#define INC111 112 +#define INC110 111 +#define INC109 110 +#define INC108 109 +#define INC107 108 +#define INC106 107 +#define INC105 106 +#define INC104 105 +#define INC103 104 +#define INC102 103 +#define INC101 102 +#define INC100 101 +#define INC99 100 +#define INC98 99 +#define INC97 98 +#define INC96 97 +#define INC95 96 +#define INC94 95 +#define INC93 94 +#define INC92 93 +#define INC91 92 +#define INC90 91 +#define INC89 90 +#define INC88 89 +#define INC87 88 +#define INC86 87 +#define INC85 86 +#define INC84 85 +#define INC83 84 +#define INC82 83 +#define INC81 82 +#define INC80 81 +#define INC79 80 +#define INC78 79 +#define INC77 78 +#define INC76 77 +#define INC75 76 +#define INC74 75 +#define INC73 74 +#define INC72 73 +#define INC71 72 +#define INC70 71 +#define INC69 70 +#define INC68 69 +#define INC67 68 +#define INC66 67 +#define INC65 66 +#define INC64 65 +#define INC63 64 +#define INC62 63 +#define INC61 62 +#define INC60 61 +#define INC59 60 +#define INC58 59 +#define INC57 58 +#define INC56 57 +#define INC55 56 +#define INC54 55 +#define INC53 54 +#define INC52 53 +#define INC51 52 +#define INC50 51 +#define INC49 50 +#define INC48 49 +#define INC47 48 +#define INC46 47 +#define INC45 46 +#define INC44 45 +#define INC43 44 +#define INC42 43 +#define INC41 42 +#define INC40 41 +#define INC39 40 +#define INC38 39 +#define INC37 38 +#define INC36 37 +#define INC35 36 +#define INC34 35 +#define INC33 34 +#define INC32 33 +#define INC31 32 +#define INC30 31 +#define INC29 30 +#define INC28 29 +#define INC27 28 +#define INC26 27 +#define INC25 26 +#define INC24 25 +#define INC23 24 +#define INC22 23 +#define INC21 22 +#define INC20 21 +#define INC19 20 +#define INC18 19 +#define INC17 18 +#define INC16 17 +#define INC15 16 +#define INC14 15 +#define INC13 14 +#define INC12 13 +#define INC11 12 +#define INC10 11 +#define INC9 10 +#define INC8 9 +#define INC7 8 +#define INC6 7 +#define INC5 6 +#define INC4 5 +#define INC3 4 +#define INC2 3 +#define INC1 2 +#define INC0 1 + +#define DIV2(x) C2(DIV2_,x) + +#define DIV2_1024 512 +#define DIV2_1023 511 +#define DIV2_1022 511 +#define DIV2_1021 510 +#define DIV2_1020 510 +#define DIV2_1019 509 +#define DIV2_1018 509 +#define DIV2_1017 508 +#define DIV2_1016 508 +#define DIV2_1015 507 +#define DIV2_1014 507 +#define DIV2_1013 506 +#define DIV2_1012 506 +#define DIV2_1011 505 +#define DIV2_1010 505 +#define DIV2_1009 504 +#define DIV2_1008 504 +#define DIV2_1007 503 +#define DIV2_1006 503 +#define DIV2_1005 502 +#define DIV2_1004 502 +#define DIV2_1003 501 +#define DIV2_1002 501 +#define DIV2_1001 500 +#define DIV2_1000 500 +#define DIV2_999 499 +#define DIV2_998 499 +#define DIV2_997 498 +#define DIV2_996 498 +#define DIV2_995 497 +#define DIV2_994 497 +#define DIV2_993 496 +#define DIV2_992 496 +#define DIV2_991 495 +#define DIV2_990 495 +#define DIV2_989 494 +#define DIV2_988 494 +#define DIV2_987 493 +#define DIV2_986 493 +#define DIV2_985 492 +#define DIV2_984 492 +#define DIV2_983 491 +#define DIV2_982 491 +#define DIV2_981 490 +#define DIV2_980 490 +#define DIV2_979 489 +#define DIV2_978 489 +#define DIV2_977 488 +#define DIV2_976 488 +#define DIV2_975 487 +#define DIV2_974 487 +#define DIV2_973 486 +#define DIV2_972 486 +#define DIV2_971 485 +#define DIV2_970 485 +#define DIV2_969 484 +#define DIV2_968 484 +#define DIV2_967 483 +#define DIV2_966 483 +#define DIV2_965 482 +#define DIV2_964 482 +#define DIV2_963 481 +#define DIV2_962 481 +#define DIV2_961 480 +#define DIV2_960 480 +#define DIV2_959 479 +#define DIV2_958 479 +#define DIV2_957 478 +#define DIV2_956 478 +#define DIV2_955 477 +#define DIV2_954 477 +#define DIV2_953 476 +#define DIV2_952 476 +#define DIV2_951 475 +#define DIV2_950 475 +#define DIV2_949 474 +#define DIV2_948 474 +#define DIV2_947 473 +#define DIV2_946 473 +#define DIV2_945 472 +#define DIV2_944 472 +#define DIV2_943 471 +#define DIV2_942 471 +#define DIV2_941 470 +#define DIV2_940 470 +#define DIV2_939 469 +#define DIV2_938 469 +#define DIV2_937 468 +#define DIV2_936 468 +#define DIV2_935 467 +#define DIV2_934 467 +#define DIV2_933 466 +#define DIV2_932 466 +#define DIV2_931 465 +#define DIV2_930 465 +#define DIV2_929 464 +#define DIV2_928 464 +#define DIV2_927 463 +#define DIV2_926 463 +#define DIV2_925 462 +#define DIV2_924 462 +#define DIV2_923 461 +#define DIV2_922 461 +#define DIV2_921 460 +#define DIV2_920 460 +#define DIV2_919 459 +#define DIV2_918 459 +#define DIV2_917 458 +#define DIV2_916 458 +#define DIV2_915 457 +#define DIV2_914 457 +#define DIV2_913 456 +#define DIV2_912 456 +#define DIV2_911 455 +#define DIV2_910 455 +#define DIV2_909 454 +#define DIV2_908 454 +#define DIV2_907 453 +#define DIV2_906 453 +#define DIV2_905 452 +#define DIV2_904 452 +#define DIV2_903 451 +#define DIV2_902 451 +#define DIV2_901 450 +#define DIV2_900 450 +#define DIV2_899 449 +#define DIV2_898 449 +#define DIV2_897 448 +#define DIV2_896 448 +#define DIV2_895 447 +#define DIV2_894 447 +#define DIV2_893 446 +#define DIV2_892 446 +#define DIV2_891 445 +#define DIV2_890 445 +#define DIV2_889 444 +#define DIV2_888 444 +#define DIV2_887 443 +#define DIV2_886 443 +#define DIV2_885 442 +#define DIV2_884 442 +#define DIV2_883 441 +#define DIV2_882 441 +#define DIV2_881 440 +#define DIV2_880 440 +#define DIV2_879 439 +#define DIV2_878 439 +#define DIV2_877 438 +#define DIV2_876 438 +#define DIV2_875 437 +#define DIV2_874 437 +#define DIV2_873 436 +#define DIV2_872 436 +#define DIV2_871 435 +#define DIV2_870 435 +#define DIV2_869 434 +#define DIV2_868 434 +#define DIV2_867 433 +#define DIV2_866 433 +#define DIV2_865 432 +#define DIV2_864 432 +#define DIV2_863 431 +#define DIV2_862 431 +#define DIV2_861 430 +#define DIV2_860 430 +#define DIV2_859 429 +#define DIV2_858 429 +#define DIV2_857 428 +#define DIV2_856 428 +#define DIV2_855 427 +#define DIV2_854 427 +#define DIV2_853 426 +#define DIV2_852 426 +#define DIV2_851 425 +#define DIV2_850 425 +#define DIV2_849 424 +#define DIV2_848 424 +#define DIV2_847 423 +#define DIV2_846 423 +#define DIV2_845 422 +#define DIV2_844 422 +#define DIV2_843 421 +#define DIV2_842 421 +#define DIV2_841 420 +#define DIV2_840 420 +#define DIV2_839 419 +#define DIV2_838 419 +#define DIV2_837 418 +#define DIV2_836 418 +#define DIV2_835 417 +#define DIV2_834 417 +#define DIV2_833 416 +#define DIV2_832 416 +#define DIV2_831 415 +#define DIV2_830 415 +#define DIV2_829 414 +#define DIV2_828 414 +#define DIV2_827 413 +#define DIV2_826 413 +#define DIV2_825 412 +#define DIV2_824 412 +#define DIV2_823 411 +#define DIV2_822 411 +#define DIV2_821 410 +#define DIV2_820 410 +#define DIV2_819 409 +#define DIV2_818 409 +#define DIV2_817 408 +#define DIV2_816 408 +#define DIV2_815 407 +#define DIV2_814 407 +#define DIV2_813 406 +#define DIV2_812 406 +#define DIV2_811 405 +#define DIV2_810 405 +#define DIV2_809 404 +#define DIV2_808 404 +#define DIV2_807 403 +#define DIV2_806 403 +#define DIV2_805 402 +#define DIV2_804 402 +#define DIV2_803 401 +#define DIV2_802 401 +#define DIV2_801 400 +#define DIV2_800 400 +#define DIV2_799 399 +#define DIV2_798 399 +#define DIV2_797 398 +#define DIV2_796 398 +#define DIV2_795 397 +#define DIV2_794 397 +#define DIV2_793 396 +#define DIV2_792 396 +#define DIV2_791 395 +#define DIV2_790 395 +#define DIV2_789 394 +#define DIV2_788 394 +#define DIV2_787 393 +#define DIV2_786 393 +#define DIV2_785 392 +#define DIV2_784 392 +#define DIV2_783 391 +#define DIV2_782 391 +#define DIV2_781 390 +#define DIV2_780 390 +#define DIV2_779 389 +#define DIV2_778 389 +#define DIV2_777 388 +#define DIV2_776 388 +#define DIV2_775 387 +#define DIV2_774 387 +#define DIV2_773 386 +#define DIV2_772 386 +#define DIV2_771 385 +#define DIV2_770 385 +#define DIV2_769 384 +#define DIV2_768 384 +#define DIV2_767 383 +#define DIV2_766 383 +#define DIV2_765 382 +#define DIV2_764 382 +#define DIV2_763 381 +#define DIV2_762 381 +#define DIV2_761 380 +#define DIV2_760 380 +#define DIV2_759 379 +#define DIV2_758 379 +#define DIV2_757 378 +#define DIV2_756 378 +#define DIV2_755 377 +#define DIV2_754 377 +#define DIV2_753 376 +#define DIV2_752 376 +#define DIV2_751 375 +#define DIV2_750 375 +#define DIV2_749 374 +#define DIV2_748 374 +#define DIV2_747 373 +#define DIV2_746 373 +#define DIV2_745 372 +#define DIV2_744 372 +#define DIV2_743 371 +#define DIV2_742 371 +#define DIV2_741 370 +#define DIV2_740 370 +#define DIV2_739 369 +#define DIV2_738 369 +#define DIV2_737 368 +#define DIV2_736 368 +#define DIV2_735 367 +#define DIV2_734 367 +#define DIV2_733 366 +#define DIV2_732 366 +#define DIV2_731 365 +#define DIV2_730 365 +#define DIV2_729 364 +#define DIV2_728 364 +#define DIV2_727 363 +#define DIV2_726 363 +#define DIV2_725 362 +#define DIV2_724 362 +#define DIV2_723 361 +#define DIV2_722 361 +#define DIV2_721 360 +#define DIV2_720 360 +#define DIV2_719 359 +#define DIV2_718 359 +#define DIV2_717 358 +#define DIV2_716 358 +#define DIV2_715 357 +#define DIV2_714 357 +#define DIV2_713 356 +#define DIV2_712 356 +#define DIV2_711 355 +#define DIV2_710 355 +#define DIV2_709 354 +#define DIV2_708 354 +#define DIV2_707 353 +#define DIV2_706 353 +#define DIV2_705 352 +#define DIV2_704 352 +#define DIV2_703 351 +#define DIV2_702 351 +#define DIV2_701 350 +#define DIV2_700 350 +#define DIV2_699 349 +#define DIV2_698 349 +#define DIV2_697 348 +#define DIV2_696 348 +#define DIV2_695 347 +#define DIV2_694 347 +#define DIV2_693 346 +#define DIV2_692 346 +#define DIV2_691 345 +#define DIV2_690 345 +#define DIV2_689 344 +#define DIV2_688 344 +#define DIV2_687 343 +#define DIV2_686 343 +#define DIV2_685 342 +#define DIV2_684 342 +#define DIV2_683 341 +#define DIV2_682 341 +#define DIV2_681 340 +#define DIV2_680 340 +#define DIV2_679 339 +#define DIV2_678 339 +#define DIV2_677 338 +#define DIV2_676 338 +#define DIV2_675 337 +#define DIV2_674 337 +#define DIV2_673 336 +#define DIV2_672 336 +#define DIV2_671 335 +#define DIV2_670 335 +#define DIV2_669 334 +#define DIV2_668 334 +#define DIV2_667 333 +#define DIV2_666 333 +#define DIV2_665 332 +#define DIV2_664 332 +#define DIV2_663 331 +#define DIV2_662 331 +#define DIV2_661 330 +#define DIV2_660 330 +#define DIV2_659 329 +#define DIV2_658 329 +#define DIV2_657 328 +#define DIV2_656 328 +#define DIV2_655 327 +#define DIV2_654 327 +#define DIV2_653 326 +#define DIV2_652 326 +#define DIV2_651 325 +#define DIV2_650 325 +#define DIV2_649 324 +#define DIV2_648 324 +#define DIV2_647 323 +#define DIV2_646 323 +#define DIV2_645 322 +#define DIV2_644 322 +#define DIV2_643 321 +#define DIV2_642 321 +#define DIV2_641 320 +#define DIV2_640 320 +#define DIV2_639 319 +#define DIV2_638 319 +#define DIV2_637 318 +#define DIV2_636 318 +#define DIV2_635 317 +#define DIV2_634 317 +#define DIV2_633 316 +#define DIV2_632 316 +#define DIV2_631 315 +#define DIV2_630 315 +#define DIV2_629 314 +#define DIV2_628 314 +#define DIV2_627 313 +#define DIV2_626 313 +#define DIV2_625 312 +#define DIV2_624 312 +#define DIV2_623 311 +#define DIV2_622 311 +#define DIV2_621 310 +#define DIV2_620 310 +#define DIV2_619 309 +#define DIV2_618 309 +#define DIV2_617 308 +#define DIV2_616 308 +#define DIV2_615 307 +#define DIV2_614 307 +#define DIV2_613 306 +#define DIV2_612 306 +#define DIV2_611 305 +#define DIV2_610 305 +#define DIV2_609 304 +#define DIV2_608 304 +#define DIV2_607 303 +#define DIV2_606 303 +#define DIV2_605 302 +#define DIV2_604 302 +#define DIV2_603 301 +#define DIV2_602 301 +#define DIV2_601 300 +#define DIV2_600 300 +#define DIV2_599 299 +#define DIV2_598 299 +#define DIV2_597 298 +#define DIV2_596 298 +#define DIV2_595 297 +#define DIV2_594 297 +#define DIV2_593 296 +#define DIV2_592 296 +#define DIV2_591 295 +#define DIV2_590 295 +#define DIV2_589 294 +#define DIV2_588 294 +#define DIV2_587 293 +#define DIV2_586 293 +#define DIV2_585 292 +#define DIV2_584 292 +#define DIV2_583 291 +#define DIV2_582 291 +#define DIV2_581 290 +#define DIV2_580 290 +#define DIV2_579 289 +#define DIV2_578 289 +#define DIV2_577 288 +#define DIV2_576 288 +#define DIV2_575 287 +#define DIV2_574 287 +#define DIV2_573 286 +#define DIV2_572 286 +#define DIV2_571 285 +#define DIV2_570 285 +#define DIV2_569 284 +#define DIV2_568 284 +#define DIV2_567 283 +#define DIV2_566 283 +#define DIV2_565 282 +#define DIV2_564 282 +#define DIV2_563 281 +#define DIV2_562 281 +#define DIV2_561 280 +#define DIV2_560 280 +#define DIV2_559 279 +#define DIV2_558 279 +#define DIV2_557 278 +#define DIV2_556 278 +#define DIV2_555 277 +#define DIV2_554 277 +#define DIV2_553 276 +#define DIV2_552 276 +#define DIV2_551 275 +#define DIV2_550 275 +#define DIV2_549 274 +#define DIV2_548 274 +#define DIV2_547 273 +#define DIV2_546 273 +#define DIV2_545 272 +#define DIV2_544 272 +#define DIV2_543 271 +#define DIV2_542 271 +#define DIV2_541 270 +#define DIV2_540 270 +#define DIV2_539 269 +#define DIV2_538 269 +#define DIV2_537 268 +#define DIV2_536 268 +#define DIV2_535 267 +#define DIV2_534 267 +#define DIV2_533 266 +#define DIV2_532 266 +#define DIV2_531 265 +#define DIV2_530 265 +#define DIV2_529 264 +#define DIV2_528 264 +#define DIV2_527 263 +#define DIV2_526 263 +#define DIV2_525 262 +#define DIV2_524 262 +#define DIV2_523 261 +#define DIV2_522 261 +#define DIV2_521 260 +#define DIV2_520 260 +#define DIV2_519 259 +#define DIV2_518 259 +#define DIV2_517 258 +#define DIV2_516 258 +#define DIV2_515 257 +#define DIV2_514 257 +#define DIV2_513 256 +#define DIV2_512 256 +#define DIV2_511 255 +#define DIV2_510 255 +#define DIV2_509 254 +#define DIV2_508 254 +#define DIV2_507 253 +#define DIV2_506 253 +#define DIV2_505 252 +#define DIV2_504 252 +#define DIV2_503 251 +#define DIV2_502 251 +#define DIV2_501 250 +#define DIV2_500 250 +#define DIV2_499 249 +#define DIV2_498 249 +#define DIV2_497 248 +#define DIV2_496 248 +#define DIV2_495 247 +#define DIV2_494 247 +#define DIV2_493 246 +#define DIV2_492 246 +#define DIV2_491 245 +#define DIV2_490 245 +#define DIV2_489 244 +#define DIV2_488 244 +#define DIV2_487 243 +#define DIV2_486 243 +#define DIV2_485 242 +#define DIV2_484 242 +#define DIV2_483 241 +#define DIV2_482 241 +#define DIV2_481 240 +#define DIV2_480 240 +#define DIV2_479 239 +#define DIV2_478 239 +#define DIV2_477 238 +#define DIV2_476 238 +#define DIV2_475 237 +#define DIV2_474 237 +#define DIV2_473 236 +#define DIV2_472 236 +#define DIV2_471 235 +#define DIV2_470 235 +#define DIV2_469 234 +#define DIV2_468 234 +#define DIV2_467 233 +#define DIV2_466 233 +#define DIV2_465 232 +#define DIV2_464 232 +#define DIV2_463 231 +#define DIV2_462 231 +#define DIV2_461 230 +#define DIV2_460 230 +#define DIV2_459 229 +#define DIV2_458 229 +#define DIV2_457 228 +#define DIV2_456 228 +#define DIV2_455 227 +#define DIV2_454 227 +#define DIV2_453 226 +#define DIV2_452 226 +#define DIV2_451 225 +#define DIV2_450 225 +#define DIV2_449 224 +#define DIV2_448 224 +#define DIV2_447 223 +#define DIV2_446 223 +#define DIV2_445 222 +#define DIV2_444 222 +#define DIV2_443 221 +#define DIV2_442 221 +#define DIV2_441 220 +#define DIV2_440 220 +#define DIV2_439 219 +#define DIV2_438 219 +#define DIV2_437 218 +#define DIV2_436 218 +#define DIV2_435 217 +#define DIV2_434 217 +#define DIV2_433 216 +#define DIV2_432 216 +#define DIV2_431 215 +#define DIV2_430 215 +#define DIV2_429 214 +#define DIV2_428 214 +#define DIV2_427 213 +#define DIV2_426 213 +#define DIV2_425 212 +#define DIV2_424 212 +#define DIV2_423 211 +#define DIV2_422 211 +#define DIV2_421 210 +#define DIV2_420 210 +#define DIV2_419 209 +#define DIV2_418 209 +#define DIV2_417 208 +#define DIV2_416 208 +#define DIV2_415 207 +#define DIV2_414 207 +#define DIV2_413 206 +#define DIV2_412 206 +#define DIV2_411 205 +#define DIV2_410 205 +#define DIV2_409 204 +#define DIV2_408 204 +#define DIV2_407 203 +#define DIV2_406 203 +#define DIV2_405 202 +#define DIV2_404 202 +#define DIV2_403 201 +#define DIV2_402 201 +#define DIV2_401 200 +#define DIV2_400 200 +#define DIV2_399 199 +#define DIV2_398 199 +#define DIV2_397 198 +#define DIV2_396 198 +#define DIV2_395 197 +#define DIV2_394 197 +#define DIV2_393 196 +#define DIV2_392 196 +#define DIV2_391 195 +#define DIV2_390 195 +#define DIV2_389 194 +#define DIV2_388 194 +#define DIV2_387 193 +#define DIV2_386 193 +#define DIV2_385 192 +#define DIV2_384 192 +#define DIV2_383 191 +#define DIV2_382 191 +#define DIV2_381 190 +#define DIV2_380 190 +#define DIV2_379 189 +#define DIV2_378 189 +#define DIV2_377 188 +#define DIV2_376 188 +#define DIV2_375 187 +#define DIV2_374 187 +#define DIV2_373 186 +#define DIV2_372 186 +#define DIV2_371 185 +#define DIV2_370 185 +#define DIV2_369 184 +#define DIV2_368 184 +#define DIV2_367 183 +#define DIV2_366 183 +#define DIV2_365 182 +#define DIV2_364 182 +#define DIV2_363 181 +#define DIV2_362 181 +#define DIV2_361 180 +#define DIV2_360 180 +#define DIV2_359 179 +#define DIV2_358 179 +#define DIV2_357 178 +#define DIV2_356 178 +#define DIV2_355 177 +#define DIV2_354 177 +#define DIV2_353 176 +#define DIV2_352 176 +#define DIV2_351 175 +#define DIV2_350 175 +#define DIV2_349 174 +#define DIV2_348 174 +#define DIV2_347 173 +#define DIV2_346 173 +#define DIV2_345 172 +#define DIV2_344 172 +#define DIV2_343 171 +#define DIV2_342 171 +#define DIV2_341 170 +#define DIV2_340 170 +#define DIV2_339 169 +#define DIV2_338 169 +#define DIV2_337 168 +#define DIV2_336 168 +#define DIV2_335 167 +#define DIV2_334 167 +#define DIV2_333 166 +#define DIV2_332 166 +#define DIV2_331 165 +#define DIV2_330 165 +#define DIV2_329 164 +#define DIV2_328 164 +#define DIV2_327 163 +#define DIV2_326 163 +#define DIV2_325 162 +#define DIV2_324 162 +#define DIV2_323 161 +#define DIV2_322 161 +#define DIV2_321 160 +#define DIV2_320 160 +#define DIV2_319 159 +#define DIV2_318 159 +#define DIV2_317 158 +#define DIV2_316 158 +#define DIV2_315 157 +#define DIV2_314 157 +#define DIV2_313 156 +#define DIV2_312 156 +#define DIV2_311 155 +#define DIV2_310 155 +#define DIV2_309 154 +#define DIV2_308 154 +#define DIV2_307 153 +#define DIV2_306 153 +#define DIV2_305 152 +#define DIV2_304 152 +#define DIV2_303 151 +#define DIV2_302 151 +#define DIV2_301 150 +#define DIV2_300 150 +#define DIV2_299 149 +#define DIV2_298 149 +#define DIV2_297 148 +#define DIV2_296 148 +#define DIV2_295 147 +#define DIV2_294 147 +#define DIV2_293 146 +#define DIV2_292 146 +#define DIV2_291 145 +#define DIV2_290 145 +#define DIV2_289 144 +#define DIV2_288 144 +#define DIV2_287 143 +#define DIV2_286 143 +#define DIV2_285 142 +#define DIV2_284 142 +#define DIV2_283 141 +#define DIV2_282 141 +#define DIV2_281 140 +#define DIV2_280 140 +#define DIV2_279 139 +#define DIV2_278 139 +#define DIV2_277 138 +#define DIV2_276 138 +#define DIV2_275 137 +#define DIV2_274 137 +#define DIV2_273 136 +#define DIV2_272 136 +#define DIV2_271 135 +#define DIV2_270 135 +#define DIV2_269 134 +#define DIV2_268 134 +#define DIV2_267 133 +#define DIV2_266 133 +#define DIV2_265 132 +#define DIV2_264 132 +#define DIV2_263 131 +#define DIV2_262 131 +#define DIV2_261 130 +#define DIV2_260 130 +#define DIV2_259 129 +#define DIV2_258 129 +#define DIV2_257 128 +#define DIV2_256 128 +#define DIV2_255 127 +#define DIV2_254 127 +#define DIV2_253 126 +#define DIV2_252 126 +#define DIV2_251 125 +#define DIV2_250 125 +#define DIV2_249 124 +#define DIV2_248 124 +#define DIV2_247 123 +#define DIV2_246 123 +#define DIV2_245 122 +#define DIV2_244 122 +#define DIV2_243 121 +#define DIV2_242 121 +#define DIV2_241 120 +#define DIV2_240 120 +#define DIV2_239 119 +#define DIV2_238 119 +#define DIV2_237 118 +#define DIV2_236 118 +#define DIV2_235 117 +#define DIV2_234 117 +#define DIV2_233 116 +#define DIV2_232 116 +#define DIV2_231 115 +#define DIV2_230 115 +#define DIV2_229 114 +#define DIV2_228 114 +#define DIV2_227 113 +#define DIV2_226 113 +#define DIV2_225 112 +#define DIV2_224 112 +#define DIV2_223 111 +#define DIV2_222 111 +#define DIV2_221 110 +#define DIV2_220 110 +#define DIV2_219 109 +#define DIV2_218 109 +#define DIV2_217 108 +#define DIV2_216 108 +#define DIV2_215 107 +#define DIV2_214 107 +#define DIV2_213 106 +#define DIV2_212 106 +#define DIV2_211 105 +#define DIV2_210 105 +#define DIV2_209 104 +#define DIV2_208 104 +#define DIV2_207 103 +#define DIV2_206 103 +#define DIV2_205 102 +#define DIV2_204 102 +#define DIV2_203 101 +#define DIV2_202 101 +#define DIV2_201 100 +#define DIV2_200 100 +#define DIV2_199 99 +#define DIV2_198 99 +#define DIV2_197 98 +#define DIV2_196 98 +#define DIV2_195 97 +#define DIV2_194 97 +#define DIV2_193 96 +#define DIV2_192 96 +#define DIV2_191 95 +#define DIV2_190 95 +#define DIV2_189 94 +#define DIV2_188 94 +#define DIV2_187 93 +#define DIV2_186 93 +#define DIV2_185 92 +#define DIV2_184 92 +#define DIV2_183 91 +#define DIV2_182 91 +#define DIV2_181 90 +#define DIV2_180 90 +#define DIV2_179 89 +#define DIV2_178 89 +#define DIV2_177 88 +#define DIV2_176 88 +#define DIV2_175 87 +#define DIV2_174 87 +#define DIV2_173 86 +#define DIV2_172 86 +#define DIV2_171 85 +#define DIV2_170 85 +#define DIV2_169 84 +#define DIV2_168 84 +#define DIV2_167 83 +#define DIV2_166 83 +#define DIV2_165 82 +#define DIV2_164 82 +#define DIV2_163 81 +#define DIV2_162 81 +#define DIV2_161 80 +#define DIV2_160 80 +#define DIV2_159 79 +#define DIV2_158 79 +#define DIV2_157 78 +#define DIV2_156 78 +#define DIV2_155 77 +#define DIV2_154 77 +#define DIV2_153 76 +#define DIV2_152 76 +#define DIV2_151 75 +#define DIV2_150 75 +#define DIV2_149 74 +#define DIV2_148 74 +#define DIV2_147 73 +#define DIV2_146 73 +#define DIV2_145 72 +#define DIV2_144 72 +#define DIV2_143 71 +#define DIV2_142 71 +#define DIV2_141 70 +#define DIV2_140 70 +#define DIV2_139 69 +#define DIV2_138 69 +#define DIV2_137 68 +#define DIV2_136 68 +#define DIV2_135 67 +#define DIV2_134 67 +#define DIV2_133 66 +#define DIV2_132 66 +#define DIV2_131 65 +#define DIV2_130 65 +#define DIV2_129 64 +#define DIV2_128 64 +#define DIV2_127 63 +#define DIV2_126 63 +#define DIV2_125 62 +#define DIV2_124 62 +#define DIV2_123 61 +#define DIV2_122 61 +#define DIV2_121 60 +#define DIV2_120 60 +#define DIV2_119 59 +#define DIV2_118 59 +#define DIV2_117 58 +#define DIV2_116 58 +#define DIV2_115 57 +#define DIV2_114 57 +#define DIV2_113 56 +#define DIV2_112 56 +#define DIV2_111 55 +#define DIV2_110 55 +#define DIV2_109 54 +#define DIV2_108 54 +#define DIV2_107 53 +#define DIV2_106 53 +#define DIV2_105 52 +#define DIV2_104 52 +#define DIV2_103 51 +#define DIV2_102 51 +#define DIV2_101 50 +#define DIV2_100 50 +#define DIV2_99 49 +#define DIV2_98 49 +#define DIV2_97 48 +#define DIV2_96 48 +#define DIV2_95 47 +#define DIV2_94 47 +#define DIV2_93 46 +#define DIV2_92 46 +#define DIV2_91 45 +#define DIV2_90 45 +#define DIV2_89 44 +#define DIV2_88 44 +#define DIV2_87 43 +#define DIV2_86 43 +#define DIV2_85 42 +#define DIV2_84 42 +#define DIV2_83 41 +#define DIV2_82 41 +#define DIV2_81 40 +#define DIV2_80 40 +#define DIV2_79 39 +#define DIV2_78 39 +#define DIV2_77 38 +#define DIV2_76 38 +#define DIV2_75 37 +#define DIV2_74 37 +#define DIV2_73 36 +#define DIV2_72 36 +#define DIV2_71 35 +#define DIV2_70 35 +#define DIV2_69 34 +#define DIV2_68 34 +#define DIV2_67 33 +#define DIV2_66 33 +#define DIV2_65 32 +#define DIV2_64 32 +#define DIV2_63 31 +#define DIV2_62 31 +#define DIV2_61 30 +#define DIV2_60 30 +#define DIV2_59 29 +#define DIV2_58 29 +#define DIV2_57 28 +#define DIV2_56 28 +#define DIV2_55 27 +#define DIV2_54 27 +#define DIV2_53 26 +#define DIV2_52 26 +#define DIV2_51 25 +#define DIV2_50 25 +#define DIV2_49 24 +#define DIV2_48 24 +#define DIV2_47 23 +#define DIV2_46 23 +#define DIV2_45 22 +#define DIV2_44 22 +#define DIV2_43 21 +#define DIV2_42 21 +#define DIV2_41 20 +#define DIV2_40 20 +#define DIV2_39 19 +#define DIV2_38 19 +#define DIV2_37 18 +#define DIV2_36 18 +#define DIV2_35 17 +#define DIV2_34 17 +#define DIV2_33 16 +#define DIV2_32 16 +#define DIV2_31 15 +#define DIV2_30 15 +#define DIV2_29 14 +#define DIV2_28 14 +#define DIV2_27 13 +#define DIV2_26 13 +#define DIV2_25 12 +#define DIV2_24 12 +#define DIV2_23 11 +#define DIV2_22 11 +#define DIV2_21 10 +#define DIV2_20 10 +#define DIV2_19 9 +#define DIV2_18 9 +#define DIV2_17 8 +#define DIV2_16 8 +#define DIV2_15 7 +#define DIV2_14 7 +#define DIV2_13 6 +#define DIV2_12 6 +#define DIV2_11 5 +#define DIV2_10 5 +#define DIV2_9 4 +#define DIV2_8 4 +#define DIV2_7 3 +#define DIV2_6 3 +#define DIV2_5 2 +#define DIV2_4 2 +#define DIV2_3 1 +#define DIV2_2 1 +#define DIV2_1 0 +#define DIV2_0 0 + +#define MOD2(x) C2(MOD2_,x) +#define MOD2_1024 0 +#define MOD2_1023 1 +#define MOD2_1022 0 +#define MOD2_1021 1 +#define MOD2_1020 0 +#define MOD2_1019 1 +#define MOD2_1018 0 +#define MOD2_1017 1 +#define MOD2_1016 0 +#define MOD2_1015 1 +#define MOD2_1014 0 +#define MOD2_1013 1 +#define MOD2_1012 0 +#define MOD2_1011 1 +#define MOD2_1010 0 +#define MOD2_1009 1 +#define MOD2_1008 0 +#define MOD2_1007 1 +#define MOD2_1006 0 +#define MOD2_1005 1 +#define MOD2_1004 0 +#define MOD2_1003 1 +#define MOD2_1002 0 +#define MOD2_1001 1 +#define MOD2_1000 0 +#define MOD2_999 1 +#define MOD2_998 0 +#define MOD2_997 1 +#define MOD2_996 0 +#define MOD2_995 1 +#define MOD2_994 0 +#define MOD2_993 1 +#define MOD2_992 0 +#define MOD2_991 1 +#define MOD2_990 0 +#define MOD2_989 1 +#define MOD2_988 0 +#define MOD2_987 1 +#define MOD2_986 0 +#define MOD2_985 1 +#define MOD2_984 0 +#define MOD2_983 1 +#define MOD2_982 0 +#define MOD2_981 1 +#define MOD2_980 0 +#define MOD2_979 1 +#define MOD2_978 0 +#define MOD2_977 1 +#define MOD2_976 0 +#define MOD2_975 1 +#define MOD2_974 0 +#define MOD2_973 1 +#define MOD2_972 0 +#define MOD2_971 1 +#define MOD2_970 0 +#define MOD2_969 1 +#define MOD2_968 0 +#define MOD2_967 1 +#define MOD2_966 0 +#define MOD2_965 1 +#define MOD2_964 0 +#define MOD2_963 1 +#define MOD2_962 0 +#define MOD2_961 1 +#define MOD2_960 0 +#define MOD2_959 1 +#define MOD2_958 0 +#define MOD2_957 1 +#define MOD2_956 0 +#define MOD2_955 1 +#define MOD2_954 0 +#define MOD2_953 1 +#define MOD2_952 0 +#define MOD2_951 1 +#define MOD2_950 0 +#define MOD2_949 1 +#define MOD2_948 0 +#define MOD2_947 1 +#define MOD2_946 0 +#define MOD2_945 1 +#define MOD2_944 0 +#define MOD2_943 1 +#define MOD2_942 0 +#define MOD2_941 1 +#define MOD2_940 0 +#define MOD2_939 1 +#define MOD2_938 0 +#define MOD2_937 1 +#define MOD2_936 0 +#define MOD2_935 1 +#define MOD2_934 0 +#define MOD2_933 1 +#define MOD2_932 0 +#define MOD2_931 1 +#define MOD2_930 0 +#define MOD2_929 1 +#define MOD2_928 0 +#define MOD2_927 1 +#define MOD2_926 0 +#define MOD2_925 1 +#define MOD2_924 0 +#define MOD2_923 1 +#define MOD2_922 0 +#define MOD2_921 1 +#define MOD2_920 0 +#define MOD2_919 1 +#define MOD2_918 0 +#define MOD2_917 1 +#define MOD2_916 0 +#define MOD2_915 1 +#define MOD2_914 0 +#define MOD2_913 1 +#define MOD2_912 0 +#define MOD2_911 1 +#define MOD2_910 0 +#define MOD2_909 1 +#define MOD2_908 0 +#define MOD2_907 1 +#define MOD2_906 0 +#define MOD2_905 1 +#define MOD2_904 0 +#define MOD2_903 1 +#define MOD2_902 0 +#define MOD2_901 1 +#define MOD2_900 0 +#define MOD2_899 1 +#define MOD2_898 0 +#define MOD2_897 1 +#define MOD2_896 0 +#define MOD2_895 1 +#define MOD2_894 0 +#define MOD2_893 1 +#define MOD2_892 0 +#define MOD2_891 1 +#define MOD2_890 0 +#define MOD2_889 1 +#define MOD2_888 0 +#define MOD2_887 1 +#define MOD2_886 0 +#define MOD2_885 1 +#define MOD2_884 0 +#define MOD2_883 1 +#define MOD2_882 0 +#define MOD2_881 1 +#define MOD2_880 0 +#define MOD2_879 1 +#define MOD2_878 0 +#define MOD2_877 1 +#define MOD2_876 0 +#define MOD2_875 1 +#define MOD2_874 0 +#define MOD2_873 1 +#define MOD2_872 0 +#define MOD2_871 1 +#define MOD2_870 0 +#define MOD2_869 1 +#define MOD2_868 0 +#define MOD2_867 1 +#define MOD2_866 0 +#define MOD2_865 1 +#define MOD2_864 0 +#define MOD2_863 1 +#define MOD2_862 0 +#define MOD2_861 1 +#define MOD2_860 0 +#define MOD2_859 1 +#define MOD2_858 0 +#define MOD2_857 1 +#define MOD2_856 0 +#define MOD2_855 1 +#define MOD2_854 0 +#define MOD2_853 1 +#define MOD2_852 0 +#define MOD2_851 1 +#define MOD2_850 0 +#define MOD2_849 1 +#define MOD2_848 0 +#define MOD2_847 1 +#define MOD2_846 0 +#define MOD2_845 1 +#define MOD2_844 0 +#define MOD2_843 1 +#define MOD2_842 0 +#define MOD2_841 1 +#define MOD2_840 0 +#define MOD2_839 1 +#define MOD2_838 0 +#define MOD2_837 1 +#define MOD2_836 0 +#define MOD2_835 1 +#define MOD2_834 0 +#define MOD2_833 1 +#define MOD2_832 0 +#define MOD2_831 1 +#define MOD2_830 0 +#define MOD2_829 1 +#define MOD2_828 0 +#define MOD2_827 1 +#define MOD2_826 0 +#define MOD2_825 1 +#define MOD2_824 0 +#define MOD2_823 1 +#define MOD2_822 0 +#define MOD2_821 1 +#define MOD2_820 0 +#define MOD2_819 1 +#define MOD2_818 0 +#define MOD2_817 1 +#define MOD2_816 0 +#define MOD2_815 1 +#define MOD2_814 0 +#define MOD2_813 1 +#define MOD2_812 0 +#define MOD2_811 1 +#define MOD2_810 0 +#define MOD2_809 1 +#define MOD2_808 0 +#define MOD2_807 1 +#define MOD2_806 0 +#define MOD2_805 1 +#define MOD2_804 0 +#define MOD2_803 1 +#define MOD2_802 0 +#define MOD2_801 1 +#define MOD2_800 0 +#define MOD2_799 1 +#define MOD2_798 0 +#define MOD2_797 1 +#define MOD2_796 0 +#define MOD2_795 1 +#define MOD2_794 0 +#define MOD2_793 1 +#define MOD2_792 0 +#define MOD2_791 1 +#define MOD2_790 0 +#define MOD2_789 1 +#define MOD2_788 0 +#define MOD2_787 1 +#define MOD2_786 0 +#define MOD2_785 1 +#define MOD2_784 0 +#define MOD2_783 1 +#define MOD2_782 0 +#define MOD2_781 1 +#define MOD2_780 0 +#define MOD2_779 1 +#define MOD2_778 0 +#define MOD2_777 1 +#define MOD2_776 0 +#define MOD2_775 1 +#define MOD2_774 0 +#define MOD2_773 1 +#define MOD2_772 0 +#define MOD2_771 1 +#define MOD2_770 0 +#define MOD2_769 1 +#define MOD2_768 0 +#define MOD2_767 1 +#define MOD2_766 0 +#define MOD2_765 1 +#define MOD2_764 0 +#define MOD2_763 1 +#define MOD2_762 0 +#define MOD2_761 1 +#define MOD2_760 0 +#define MOD2_759 1 +#define MOD2_758 0 +#define MOD2_757 1 +#define MOD2_756 0 +#define MOD2_755 1 +#define MOD2_754 0 +#define MOD2_753 1 +#define MOD2_752 0 +#define MOD2_751 1 +#define MOD2_750 0 +#define MOD2_749 1 +#define MOD2_748 0 +#define MOD2_747 1 +#define MOD2_746 0 +#define MOD2_745 1 +#define MOD2_744 0 +#define MOD2_743 1 +#define MOD2_742 0 +#define MOD2_741 1 +#define MOD2_740 0 +#define MOD2_739 1 +#define MOD2_738 0 +#define MOD2_737 1 +#define MOD2_736 0 +#define MOD2_735 1 +#define MOD2_734 0 +#define MOD2_733 1 +#define MOD2_732 0 +#define MOD2_731 1 +#define MOD2_730 0 +#define MOD2_729 1 +#define MOD2_728 0 +#define MOD2_727 1 +#define MOD2_726 0 +#define MOD2_725 1 +#define MOD2_724 0 +#define MOD2_723 1 +#define MOD2_722 0 +#define MOD2_721 1 +#define MOD2_720 0 +#define MOD2_719 1 +#define MOD2_718 0 +#define MOD2_717 1 +#define MOD2_716 0 +#define MOD2_715 1 +#define MOD2_714 0 +#define MOD2_713 1 +#define MOD2_712 0 +#define MOD2_711 1 +#define MOD2_710 0 +#define MOD2_709 1 +#define MOD2_708 0 +#define MOD2_707 1 +#define MOD2_706 0 +#define MOD2_705 1 +#define MOD2_704 0 +#define MOD2_703 1 +#define MOD2_702 0 +#define MOD2_701 1 +#define MOD2_700 0 +#define MOD2_699 1 +#define MOD2_698 0 +#define MOD2_697 1 +#define MOD2_696 0 +#define MOD2_695 1 +#define MOD2_694 0 +#define MOD2_693 1 +#define MOD2_692 0 +#define MOD2_691 1 +#define MOD2_690 0 +#define MOD2_689 1 +#define MOD2_688 0 +#define MOD2_687 1 +#define MOD2_686 0 +#define MOD2_685 1 +#define MOD2_684 0 +#define MOD2_683 1 +#define MOD2_682 0 +#define MOD2_681 1 +#define MOD2_680 0 +#define MOD2_679 1 +#define MOD2_678 0 +#define MOD2_677 1 +#define MOD2_676 0 +#define MOD2_675 1 +#define MOD2_674 0 +#define MOD2_673 1 +#define MOD2_672 0 +#define MOD2_671 1 +#define MOD2_670 0 +#define MOD2_669 1 +#define MOD2_668 0 +#define MOD2_667 1 +#define MOD2_666 0 +#define MOD2_665 1 +#define MOD2_664 0 +#define MOD2_663 1 +#define MOD2_662 0 +#define MOD2_661 1 +#define MOD2_660 0 +#define MOD2_659 1 +#define MOD2_658 0 +#define MOD2_657 1 +#define MOD2_656 0 +#define MOD2_655 1 +#define MOD2_654 0 +#define MOD2_653 1 +#define MOD2_652 0 +#define MOD2_651 1 +#define MOD2_650 0 +#define MOD2_649 1 +#define MOD2_648 0 +#define MOD2_647 1 +#define MOD2_646 0 +#define MOD2_645 1 +#define MOD2_644 0 +#define MOD2_643 1 +#define MOD2_642 0 +#define MOD2_641 1 +#define MOD2_640 0 +#define MOD2_639 1 +#define MOD2_638 0 +#define MOD2_637 1 +#define MOD2_636 0 +#define MOD2_635 1 +#define MOD2_634 0 +#define MOD2_633 1 +#define MOD2_632 0 +#define MOD2_631 1 +#define MOD2_630 0 +#define MOD2_629 1 +#define MOD2_628 0 +#define MOD2_627 1 +#define MOD2_626 0 +#define MOD2_625 1 +#define MOD2_624 0 +#define MOD2_623 1 +#define MOD2_622 0 +#define MOD2_621 1 +#define MOD2_620 0 +#define MOD2_619 1 +#define MOD2_618 0 +#define MOD2_617 1 +#define MOD2_616 0 +#define MOD2_615 1 +#define MOD2_614 0 +#define MOD2_613 1 +#define MOD2_612 0 +#define MOD2_611 1 +#define MOD2_610 0 +#define MOD2_609 1 +#define MOD2_608 0 +#define MOD2_607 1 +#define MOD2_606 0 +#define MOD2_605 1 +#define MOD2_604 0 +#define MOD2_603 1 +#define MOD2_602 0 +#define MOD2_601 1 +#define MOD2_600 0 +#define MOD2_599 1 +#define MOD2_598 0 +#define MOD2_597 1 +#define MOD2_596 0 +#define MOD2_595 1 +#define MOD2_594 0 +#define MOD2_593 1 +#define MOD2_592 0 +#define MOD2_591 1 +#define MOD2_590 0 +#define MOD2_589 1 +#define MOD2_588 0 +#define MOD2_587 1 +#define MOD2_586 0 +#define MOD2_585 1 +#define MOD2_584 0 +#define MOD2_583 1 +#define MOD2_582 0 +#define MOD2_581 1 +#define MOD2_580 0 +#define MOD2_579 1 +#define MOD2_578 0 +#define MOD2_577 1 +#define MOD2_576 0 +#define MOD2_575 1 +#define MOD2_574 0 +#define MOD2_573 1 +#define MOD2_572 0 +#define MOD2_571 1 +#define MOD2_570 0 +#define MOD2_569 1 +#define MOD2_568 0 +#define MOD2_567 1 +#define MOD2_566 0 +#define MOD2_565 1 +#define MOD2_564 0 +#define MOD2_563 1 +#define MOD2_562 0 +#define MOD2_561 1 +#define MOD2_560 0 +#define MOD2_559 1 +#define MOD2_558 0 +#define MOD2_557 1 +#define MOD2_556 0 +#define MOD2_555 1 +#define MOD2_554 0 +#define MOD2_553 1 +#define MOD2_552 0 +#define MOD2_551 1 +#define MOD2_550 0 +#define MOD2_549 1 +#define MOD2_548 0 +#define MOD2_547 1 +#define MOD2_546 0 +#define MOD2_545 1 +#define MOD2_544 0 +#define MOD2_543 1 +#define MOD2_542 0 +#define MOD2_541 1 +#define MOD2_540 0 +#define MOD2_539 1 +#define MOD2_538 0 +#define MOD2_537 1 +#define MOD2_536 0 +#define MOD2_535 1 +#define MOD2_534 0 +#define MOD2_533 1 +#define MOD2_532 0 +#define MOD2_531 1 +#define MOD2_530 0 +#define MOD2_529 1 +#define MOD2_528 0 +#define MOD2_527 1 +#define MOD2_526 0 +#define MOD2_525 1 +#define MOD2_524 0 +#define MOD2_523 1 +#define MOD2_522 0 +#define MOD2_521 1 +#define MOD2_520 0 +#define MOD2_519 1 +#define MOD2_518 0 +#define MOD2_517 1 +#define MOD2_516 0 +#define MOD2_515 1 +#define MOD2_514 0 +#define MOD2_513 1 +#define MOD2_512 0 +#define MOD2_511 1 +#define MOD2_510 0 +#define MOD2_509 1 +#define MOD2_508 0 +#define MOD2_507 1 +#define MOD2_506 0 +#define MOD2_505 1 +#define MOD2_504 0 +#define MOD2_503 1 +#define MOD2_502 0 +#define MOD2_501 1 +#define MOD2_500 0 +#define MOD2_499 1 +#define MOD2_498 0 +#define MOD2_497 1 +#define MOD2_496 0 +#define MOD2_495 1 +#define MOD2_494 0 +#define MOD2_493 1 +#define MOD2_492 0 +#define MOD2_491 1 +#define MOD2_490 0 +#define MOD2_489 1 +#define MOD2_488 0 +#define MOD2_487 1 +#define MOD2_486 0 +#define MOD2_485 1 +#define MOD2_484 0 +#define MOD2_483 1 +#define MOD2_482 0 +#define MOD2_481 1 +#define MOD2_480 0 +#define MOD2_479 1 +#define MOD2_478 0 +#define MOD2_477 1 +#define MOD2_476 0 +#define MOD2_475 1 +#define MOD2_474 0 +#define MOD2_473 1 +#define MOD2_472 0 +#define MOD2_471 1 +#define MOD2_470 0 +#define MOD2_469 1 +#define MOD2_468 0 +#define MOD2_467 1 +#define MOD2_466 0 +#define MOD2_465 1 +#define MOD2_464 0 +#define MOD2_463 1 +#define MOD2_462 0 +#define MOD2_461 1 +#define MOD2_460 0 +#define MOD2_459 1 +#define MOD2_458 0 +#define MOD2_457 1 +#define MOD2_456 0 +#define MOD2_455 1 +#define MOD2_454 0 +#define MOD2_453 1 +#define MOD2_452 0 +#define MOD2_451 1 +#define MOD2_450 0 +#define MOD2_449 1 +#define MOD2_448 0 +#define MOD2_447 1 +#define MOD2_446 0 +#define MOD2_445 1 +#define MOD2_444 0 +#define MOD2_443 1 +#define MOD2_442 0 +#define MOD2_441 1 +#define MOD2_440 0 +#define MOD2_439 1 +#define MOD2_438 0 +#define MOD2_437 1 +#define MOD2_436 0 +#define MOD2_435 1 +#define MOD2_434 0 +#define MOD2_433 1 +#define MOD2_432 0 +#define MOD2_431 1 +#define MOD2_430 0 +#define MOD2_429 1 +#define MOD2_428 0 +#define MOD2_427 1 +#define MOD2_426 0 +#define MOD2_425 1 +#define MOD2_424 0 +#define MOD2_423 1 +#define MOD2_422 0 +#define MOD2_421 1 +#define MOD2_420 0 +#define MOD2_419 1 +#define MOD2_418 0 +#define MOD2_417 1 +#define MOD2_416 0 +#define MOD2_415 1 +#define MOD2_414 0 +#define MOD2_413 1 +#define MOD2_412 0 +#define MOD2_411 1 +#define MOD2_410 0 +#define MOD2_409 1 +#define MOD2_408 0 +#define MOD2_407 1 +#define MOD2_406 0 +#define MOD2_405 1 +#define MOD2_404 0 +#define MOD2_403 1 +#define MOD2_402 0 +#define MOD2_401 1 +#define MOD2_400 0 +#define MOD2_399 1 +#define MOD2_398 0 +#define MOD2_397 1 +#define MOD2_396 0 +#define MOD2_395 1 +#define MOD2_394 0 +#define MOD2_393 1 +#define MOD2_392 0 +#define MOD2_391 1 +#define MOD2_390 0 +#define MOD2_389 1 +#define MOD2_388 0 +#define MOD2_387 1 +#define MOD2_386 0 +#define MOD2_385 1 +#define MOD2_384 0 +#define MOD2_383 1 +#define MOD2_382 0 +#define MOD2_381 1 +#define MOD2_380 0 +#define MOD2_379 1 +#define MOD2_378 0 +#define MOD2_377 1 +#define MOD2_376 0 +#define MOD2_375 1 +#define MOD2_374 0 +#define MOD2_373 1 +#define MOD2_372 0 +#define MOD2_371 1 +#define MOD2_370 0 +#define MOD2_369 1 +#define MOD2_368 0 +#define MOD2_367 1 +#define MOD2_366 0 +#define MOD2_365 1 +#define MOD2_364 0 +#define MOD2_363 1 +#define MOD2_362 0 +#define MOD2_361 1 +#define MOD2_360 0 +#define MOD2_359 1 +#define MOD2_358 0 +#define MOD2_357 1 +#define MOD2_356 0 +#define MOD2_355 1 +#define MOD2_354 0 +#define MOD2_353 1 +#define MOD2_352 0 +#define MOD2_351 1 +#define MOD2_350 0 +#define MOD2_349 1 +#define MOD2_348 0 +#define MOD2_347 1 +#define MOD2_346 0 +#define MOD2_345 1 +#define MOD2_344 0 +#define MOD2_343 1 +#define MOD2_342 0 +#define MOD2_341 1 +#define MOD2_340 0 +#define MOD2_339 1 +#define MOD2_338 0 +#define MOD2_337 1 +#define MOD2_336 0 +#define MOD2_335 1 +#define MOD2_334 0 +#define MOD2_333 1 +#define MOD2_332 0 +#define MOD2_331 1 +#define MOD2_330 0 +#define MOD2_329 1 +#define MOD2_328 0 +#define MOD2_327 1 +#define MOD2_326 0 +#define MOD2_325 1 +#define MOD2_324 0 +#define MOD2_323 1 +#define MOD2_322 0 +#define MOD2_321 1 +#define MOD2_320 0 +#define MOD2_319 1 +#define MOD2_318 0 +#define MOD2_317 1 +#define MOD2_316 0 +#define MOD2_315 1 +#define MOD2_314 0 +#define MOD2_313 1 +#define MOD2_312 0 +#define MOD2_311 1 +#define MOD2_310 0 +#define MOD2_309 1 +#define MOD2_308 0 +#define MOD2_307 1 +#define MOD2_306 0 +#define MOD2_305 1 +#define MOD2_304 0 +#define MOD2_303 1 +#define MOD2_302 0 +#define MOD2_301 1 +#define MOD2_300 0 +#define MOD2_299 1 +#define MOD2_298 0 +#define MOD2_297 1 +#define MOD2_296 0 +#define MOD2_295 1 +#define MOD2_294 0 +#define MOD2_293 1 +#define MOD2_292 0 +#define MOD2_291 1 +#define MOD2_290 0 +#define MOD2_289 1 +#define MOD2_288 0 +#define MOD2_287 1 +#define MOD2_286 0 +#define MOD2_285 1 +#define MOD2_284 0 +#define MOD2_283 1 +#define MOD2_282 0 +#define MOD2_281 1 +#define MOD2_280 0 +#define MOD2_279 1 +#define MOD2_278 0 +#define MOD2_277 1 +#define MOD2_276 0 +#define MOD2_275 1 +#define MOD2_274 0 +#define MOD2_273 1 +#define MOD2_272 0 +#define MOD2_271 1 +#define MOD2_270 0 +#define MOD2_269 1 +#define MOD2_268 0 +#define MOD2_267 1 +#define MOD2_266 0 +#define MOD2_265 1 +#define MOD2_264 0 +#define MOD2_263 1 +#define MOD2_262 0 +#define MOD2_261 1 +#define MOD2_260 0 +#define MOD2_259 1 +#define MOD2_258 0 +#define MOD2_257 1 +#define MOD2_256 0 +#define MOD2_255 1 +#define MOD2_254 0 +#define MOD2_253 1 +#define MOD2_252 0 +#define MOD2_251 1 +#define MOD2_250 0 +#define MOD2_249 1 +#define MOD2_248 0 +#define MOD2_247 1 +#define MOD2_246 0 +#define MOD2_245 1 +#define MOD2_244 0 +#define MOD2_243 1 +#define MOD2_242 0 +#define MOD2_241 1 +#define MOD2_240 0 +#define MOD2_239 1 +#define MOD2_238 0 +#define MOD2_237 1 +#define MOD2_236 0 +#define MOD2_235 1 +#define MOD2_234 0 +#define MOD2_233 1 +#define MOD2_232 0 +#define MOD2_231 1 +#define MOD2_230 0 +#define MOD2_229 1 +#define MOD2_228 0 +#define MOD2_227 1 +#define MOD2_226 0 +#define MOD2_225 1 +#define MOD2_224 0 +#define MOD2_223 1 +#define MOD2_222 0 +#define MOD2_221 1 +#define MOD2_220 0 +#define MOD2_219 1 +#define MOD2_218 0 +#define MOD2_217 1 +#define MOD2_216 0 +#define MOD2_215 1 +#define MOD2_214 0 +#define MOD2_213 1 +#define MOD2_212 0 +#define MOD2_211 1 +#define MOD2_210 0 +#define MOD2_209 1 +#define MOD2_208 0 +#define MOD2_207 1 +#define MOD2_206 0 +#define MOD2_205 1 +#define MOD2_204 0 +#define MOD2_203 1 +#define MOD2_202 0 +#define MOD2_201 1 +#define MOD2_200 0 +#define MOD2_199 1 +#define MOD2_198 0 +#define MOD2_197 1 +#define MOD2_196 0 +#define MOD2_195 1 +#define MOD2_194 0 +#define MOD2_193 1 +#define MOD2_192 0 +#define MOD2_191 1 +#define MOD2_190 0 +#define MOD2_189 1 +#define MOD2_188 0 +#define MOD2_187 1 +#define MOD2_186 0 +#define MOD2_185 1 +#define MOD2_184 0 +#define MOD2_183 1 +#define MOD2_182 0 +#define MOD2_181 1 +#define MOD2_180 0 +#define MOD2_179 1 +#define MOD2_178 0 +#define MOD2_177 1 +#define MOD2_176 0 +#define MOD2_175 1 +#define MOD2_174 0 +#define MOD2_173 1 +#define MOD2_172 0 +#define MOD2_171 1 +#define MOD2_170 0 +#define MOD2_169 1 +#define MOD2_168 0 +#define MOD2_167 1 +#define MOD2_166 0 +#define MOD2_165 1 +#define MOD2_164 0 +#define MOD2_163 1 +#define MOD2_162 0 +#define MOD2_161 1 +#define MOD2_160 0 +#define MOD2_159 1 +#define MOD2_158 0 +#define MOD2_157 1 +#define MOD2_156 0 +#define MOD2_155 1 +#define MOD2_154 0 +#define MOD2_153 1 +#define MOD2_152 0 +#define MOD2_151 1 +#define MOD2_150 0 +#define MOD2_149 1 +#define MOD2_148 0 +#define MOD2_147 1 +#define MOD2_146 0 +#define MOD2_145 1 +#define MOD2_144 0 +#define MOD2_143 1 +#define MOD2_142 0 +#define MOD2_141 1 +#define MOD2_140 0 +#define MOD2_139 1 +#define MOD2_138 0 +#define MOD2_137 1 +#define MOD2_136 0 +#define MOD2_135 1 +#define MOD2_134 0 +#define MOD2_133 1 +#define MOD2_132 0 +#define MOD2_131 1 +#define MOD2_130 0 +#define MOD2_129 1 +#define MOD2_128 0 +#define MOD2_127 1 +#define MOD2_126 0 +#define MOD2_125 1 +#define MOD2_124 0 +#define MOD2_123 1 +#define MOD2_122 0 +#define MOD2_121 1 +#define MOD2_120 0 +#define MOD2_119 1 +#define MOD2_118 0 +#define MOD2_117 1 +#define MOD2_116 0 +#define MOD2_115 1 +#define MOD2_114 0 +#define MOD2_113 1 +#define MOD2_112 0 +#define MOD2_111 1 +#define MOD2_110 0 +#define MOD2_109 1 +#define MOD2_108 0 +#define MOD2_107 1 +#define MOD2_106 0 +#define MOD2_105 1 +#define MOD2_104 0 +#define MOD2_103 1 +#define MOD2_102 0 +#define MOD2_101 1 +#define MOD2_100 0 +#define MOD2_99 1 +#define MOD2_98 0 +#define MOD2_97 1 +#define MOD2_96 0 +#define MOD2_95 1 +#define MOD2_94 0 +#define MOD2_93 1 +#define MOD2_92 0 +#define MOD2_91 1 +#define MOD2_90 0 +#define MOD2_89 1 +#define MOD2_88 0 +#define MOD2_87 1 +#define MOD2_86 0 +#define MOD2_85 1 +#define MOD2_84 0 +#define MOD2_83 1 +#define MOD2_82 0 +#define MOD2_81 1 +#define MOD2_80 0 +#define MOD2_79 1 +#define MOD2_78 0 +#define MOD2_77 1 +#define MOD2_76 0 +#define MOD2_75 1 +#define MOD2_74 0 +#define MOD2_73 1 +#define MOD2_72 0 +#define MOD2_71 1 +#define MOD2_70 0 +#define MOD2_69 1 +#define MOD2_68 0 +#define MOD2_67 1 +#define MOD2_66 0 +#define MOD2_65 1 +#define MOD2_64 0 +#define MOD2_63 1 +#define MOD2_62 0 +#define MOD2_61 1 +#define MOD2_60 0 +#define MOD2_59 1 +#define MOD2_58 0 +#define MOD2_57 1 +#define MOD2_56 0 +#define MOD2_55 1 +#define MOD2_54 0 +#define MOD2_53 1 +#define MOD2_52 0 +#define MOD2_51 1 +#define MOD2_50 0 +#define MOD2_49 1 +#define MOD2_48 0 +#define MOD2_47 1 +#define MOD2_46 0 +#define MOD2_45 1 +#define MOD2_44 0 +#define MOD2_43 1 +#define MOD2_42 0 +#define MOD2_41 1 +#define MOD2_40 0 +#define MOD2_39 1 +#define MOD2_38 0 +#define MOD2_37 1 +#define MOD2_36 0 +#define MOD2_35 1 +#define MOD2_34 0 +#define MOD2_33 1 +#define MOD2_32 0 +#define MOD2_31 1 +#define MOD2_30 0 +#define MOD2_29 1 +#define MOD2_28 0 +#define MOD2_27 1 +#define MOD2_26 0 +#define MOD2_25 1 +#define MOD2_24 0 +#define MOD2_23 1 +#define MOD2_22 0 +#define MOD2_21 1 +#define MOD2_20 0 +#define MOD2_19 1 +#define MOD2_18 0 +#define MOD2_17 1 +#define MOD2_16 0 +#define MOD2_15 1 +#define MOD2_14 0 +#define MOD2_13 1 +#define MOD2_12 0 +#define MOD2_11 1 +#define MOD2_10 0 +#define MOD2_9 1 +#define MOD2_8 0 +#define MOD2_7 1 +#define MOD2_6 0 +#define MOD2_5 1 +#define MOD2_4 0 +#define MOD2_3 1 +#define MOD2_2 0 +#define MOD2_1 1 +#define MOD2_0 0 + +#define THE_NTH_ARG(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124, ... ) P124 + +#define _TRIGGER_PARENTHESIS_(...) , + +#define LPAREN ( + +#ifdef _MSC_VER +#define COUNT_1_OR_MORE_ARG(...) THE_NTH_ARG LPAREN __VA_ARGS__, \ +123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define MORE_THAN_1_ARG(...) THE_NTH_ARG LPAREN __VA_ARGS__, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0) +#else +#define COUNT_1_OR_MORE_ARG(...) THE_NTH_ARG (__VA_ARGS__, \ +123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define MORE_THAN_1_ARG(...) THE_NTH_ARG(__VA_ARGS__, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0) +#endif + +#define COUNT_ARG(...) C2(COUNT_ARG_, ISEMPTY(__VA_ARGS__))(__VA_ARGS__) +#define COUNT_ARG_1(...) 0 +#define COUNT_ARG_0(...) C1(COUNT_1_OR_MORE_ARG(__VA_ARGS__)) + +#define ISEMPTY(...) C5(DISPTACH_EMPTY_, MORE_THAN_1_ARG(_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()), MORE_THAN_1_ARG(__VA_ARGS__), MORE_THAN_1_ARG(__VA_ARGS__ ()), MORE_THAN_1_ARG(_TRIGGER_PARENTHESIS_ __VA_ARGS__)) +#define DISPTACH_EMPTY_1000 1 +#define DISPTACH_EMPTY_0000 0 +#define DISPTACH_EMPTY_1100 0 +#define DISPTACH_EMPTY_1111 0 +#define DISPTACH_EMPTY_1001 0 +#define DISPTACH_EMPTY_1010 0 + + +#define C2_(x,y) x##y + +#define C2(x,y) C2_(x,y) + +#define C3(x,y,z) C2(x, C2(y,z)) + +#define C4(x,y,z, u) C2(C2(x,y), C2(z,u)) + +#define C5(x,y,z,u, v) C2(C4(x,y, z, u), v) + +#define C1_(x) x + +#define C1(x) C1_(x) + +#define C2STRING(x,y) x y + +#define C3STRING(x,y,z) x y z + +#define C4STRING(x,y,z,u) x y z u + +#define C5STRING(x,y,z,u,v) x y z u v + + +#define FOR_EACH_1_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P1) \ +FOR_EACH_1_123(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + +#define FOR_EACH_1_123(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(P1) \ +FOR_EACH_1_122(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + +#define FOR_EACH_1_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P1) \ +FOR_EACH_1_121(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_1_121(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(P1) \ +FOR_EACH_1_120(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + +#define FOR_EACH_1_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P1) \ +FOR_EACH_1_119(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_1_119(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(P1) \ +FOR_EACH_1_118(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + +#define FOR_EACH_1_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P1) \ +FOR_EACH_1_117(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_1_117(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(P1) \ +FOR_EACH_1_116(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + +#define FOR_EACH_1_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P1) \ +FOR_EACH_1_115(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_1_115(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(P1) \ +FOR_EACH_1_114(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + +#define FOR_EACH_1_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P1) \ +FOR_EACH_1_113(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_1_113(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(P1) \ +FOR_EACH_1_112(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + +#define FOR_EACH_1_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P1) \ +FOR_EACH_1_111(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_1_111(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(P1) \ +FOR_EACH_1_110(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + +#define FOR_EACH_1_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P1) \ +FOR_EACH_1_109(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_1_109(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(P1) \ +FOR_EACH_1_108(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + +#define FOR_EACH_1_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P1) \ +FOR_EACH_1_107(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_1_107(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(P1) \ +FOR_EACH_1_106(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + +#define FOR_EACH_1_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P1) \ +FOR_EACH_1_105(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_1_105(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(P1) \ +FOR_EACH_1_104(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + +#define FOR_EACH_1_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P1) \ +FOR_EACH_1_103(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_1_103(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(P1) \ +FOR_EACH_1_102(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + +#define FOR_EACH_1_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P1) \ +FOR_EACH_1_101(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_1_101(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(P1) \ +FOR_EACH_1_100(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + +#define FOR_EACH_1_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P1) \ +FOR_EACH_1_99(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_1_99(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(P1) \ +FOR_EACH_1_98(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + +#define FOR_EACH_1_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P1) \ +FOR_EACH_1_97(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_1_97(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(P1) \ +FOR_EACH_1_96(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + +#define FOR_EACH_1_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P1) \ +FOR_EACH_1_95(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_1_95(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(P1) \ +FOR_EACH_1_94(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + +#define FOR_EACH_1_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P1) \ +FOR_EACH_1_93(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_1_93(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(P1) \ +FOR_EACH_1_92(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + +#define FOR_EACH_1_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P1) \ +FOR_EACH_1_91(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_1_91(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(P1) \ +FOR_EACH_1_90(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + +#define FOR_EACH_1_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P1) \ +FOR_EACH_1_89(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_1_89(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(P1) \ +FOR_EACH_1_88(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + +#define FOR_EACH_1_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P1) \ +FOR_EACH_1_87(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_1_87(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(P1) \ +FOR_EACH_1_86(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + +#define FOR_EACH_1_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P1) \ +FOR_EACH_1_85(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_1_85(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(P1) \ +FOR_EACH_1_84(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + +#define FOR_EACH_1_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P1) \ +FOR_EACH_1_83(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_1_83(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(P1) \ +FOR_EACH_1_82(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + +#define FOR_EACH_1_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P1) \ +FOR_EACH_1_81(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_1_81(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(P1) \ +FOR_EACH_1_80(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + +#define FOR_EACH_1_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P1) \ +FOR_EACH_1_79(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_1_79(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(P1) \ +FOR_EACH_1_78(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + +#define FOR_EACH_1_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P1) \ +FOR_EACH_1_77(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_1_77(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(P1) \ +FOR_EACH_1_76(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + +#define FOR_EACH_1_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P1) \ +FOR_EACH_1_75(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_1_75(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(P1) \ +FOR_EACH_1_74(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + +#define FOR_EACH_1_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P1) \ +FOR_EACH_1_73(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_1_73(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(P1) \ +FOR_EACH_1_72(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + +#define FOR_EACH_1_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P1) \ +FOR_EACH_1_71(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_1_71(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(P1) \ +FOR_EACH_1_70(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + +#define FOR_EACH_1_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P1) \ +FOR_EACH_1_69(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_1_69(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(P1) \ +FOR_EACH_1_68(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + +#define FOR_EACH_1_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P1) \ +FOR_EACH_1_67(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_1_67(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(P1) \ +FOR_EACH_1_66(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + +#define FOR_EACH_1_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P1) \ +FOR_EACH_1_65(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_1_65(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(P1) \ +FOR_EACH_1_64(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + +#define FOR_EACH_1_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P1) \ +FOR_EACH_1_63(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_1_63(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(P1) \ +FOR_EACH_1_62(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + +#define FOR_EACH_1_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P1) \ +FOR_EACH_1_61(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_1_61(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(P1) \ +FOR_EACH_1_60(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + +#define FOR_EACH_1_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P1) \ +FOR_EACH_1_59(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_1_59(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(P1) \ +FOR_EACH_1_58(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + +#define FOR_EACH_1_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P1) \ +FOR_EACH_1_57(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_1_57(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(P1) \ +FOR_EACH_1_56(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + +#define FOR_EACH_1_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P1) \ +FOR_EACH_1_55(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_1_55(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(P1) \ +FOR_EACH_1_54(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + +#define FOR_EACH_1_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P1) \ +FOR_EACH_1_53(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_1_53(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(P1) \ +FOR_EACH_1_52(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + +#define FOR_EACH_1_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P1) \ +FOR_EACH_1_51(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_1_51(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(P1) \ +FOR_EACH_1_50(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + +#define FOR_EACH_1_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P1) \ +FOR_EACH_1_49(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_1_49(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(P1) \ +FOR_EACH_1_48(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + +#define FOR_EACH_1_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P1) \ +FOR_EACH_1_47(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_1_47(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(P1) \ +FOR_EACH_1_46(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + +#define FOR_EACH_1_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P1) \ +FOR_EACH_1_45(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_1_45(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(P1) \ +FOR_EACH_1_44(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + +#define FOR_EACH_1_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P1) \ +FOR_EACH_1_43(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_1_43(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(P1) \ +FOR_EACH_1_42(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + +#define FOR_EACH_1_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P1) \ +FOR_EACH_1_41(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_1_41(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(P1) \ +FOR_EACH_1_40(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + +#define FOR_EACH_1_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P1) \ +FOR_EACH_1_39(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_1_39(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(P1) \ +FOR_EACH_1_38(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + +#define FOR_EACH_1_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P1) \ +FOR_EACH_1_37(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_1_37(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(P1) \ +FOR_EACH_1_36(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + +#define FOR_EACH_1_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P1) \ +FOR_EACH_1_35(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_1_35(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(P1) \ +FOR_EACH_1_34(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + +#define FOR_EACH_1_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P1) \ +FOR_EACH_1_33(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_1_33(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(P1) \ +FOR_EACH_1_32(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + +#define FOR_EACH_1_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P1) \ +FOR_EACH_1_31(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_1_31(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(P1) \ +FOR_EACH_1_30(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + +#define FOR_EACH_1_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P1) \ +FOR_EACH_1_29(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_1_29(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(P1) \ +FOR_EACH_1_28(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + +#define FOR_EACH_1_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P1) \ +FOR_EACH_1_27(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_1_27(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(P1) \ +FOR_EACH_1_26(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + +#define FOR_EACH_1_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P1) \ +FOR_EACH_1_25(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_1_25(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(P1) \ +FOR_EACH_1_24(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + +#define FOR_EACH_1_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P1) \ +FOR_EACH_1_23(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_1_23(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(P1) \ +FOR_EACH_1_22(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + +#define FOR_EACH_1_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P1) \ +FOR_EACH_1_21(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_1_21(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(P1) \ +FOR_EACH_1_20(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + +#define FOR_EACH_1_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P1) \ +FOR_EACH_1_19(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_1_19(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(P1) \ +FOR_EACH_1_18(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + +#define FOR_EACH_1_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P1) \ +FOR_EACH_1_17(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_1_17(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(P1) \ +FOR_EACH_1_16(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + +#define FOR_EACH_1_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P1) \ +FOR_EACH_1_15(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_1_15(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(P1) \ +FOR_EACH_1_14(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + +#define FOR_EACH_1_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P1) \ +FOR_EACH_1_13(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_1_13(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(P1) \ +FOR_EACH_1_12(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + +#define FOR_EACH_1_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P1) \ +FOR_EACH_1_11(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_1_11(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(P1) \ +FOR_EACH_1_10(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + +#define FOR_EACH_1_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P1) \ +FOR_EACH_1_9(X, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_1_9(X, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(P1) \ +FOR_EACH_1_8(X, P2, P3, P4, P5, P6, P7, P8, P9) + +#define FOR_EACH_1_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P1) \ +FOR_EACH_1_7(X, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_1_7(X, P1, P2, P3, P4, P5, P6, P7) \ +X(P1) \ +FOR_EACH_1_6(X, P2, P3, P4, P5, P6, P7) + +#define FOR_EACH_1_6(X, P1, P2, P3, P4, P5, P6) \ +X(P1) \ +FOR_EACH_1_5(X, P2, P3, P4, P5, P6) + +#define FOR_EACH_1_5(X, P1, P2, P3, P4, P5) \ +X(P1) \ +FOR_EACH_1_4(X, P2, P3, P4, P5) + +#define FOR_EACH_1_4(X, P1, P2, P3, P4) \ +X(P1) \ +FOR_EACH_1_3(X, P2, P3, P4) + +#define FOR_EACH_1_3(X, P1, P2, P3) \ +X(P1) \ +FOR_EACH_1_2(X, P2, P3) + +#define FOR_EACH_1_2(X, P1, P2) \ +X(P1) \ +FOR_EACH_1_1(X, P2) + +#define FOR_EACH_1_1(X, P1) \ +X(P1) + +#ifdef _MSC_VER +#define FOR_EACH_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_,C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_,C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#define FOR_EACH_1_KEEP_1_124(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_123(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_1_KEEP_1_123(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_122(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + + +#define FOR_EACH_1_KEEP_1_122(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_121(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_1_KEEP_1_121(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_120(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + + +#define FOR_EACH_1_KEEP_1_120(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_119(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_1_KEEP_1_119(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_118(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + + +#define FOR_EACH_1_KEEP_1_118(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_117(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_1_KEEP_1_117(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_116(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + + +#define FOR_EACH_1_KEEP_1_116(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_115(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_1_KEEP_1_115(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_114(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + + +#define FOR_EACH_1_KEEP_1_114(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_113(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_1_KEEP_1_113(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_112(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + + +#define FOR_EACH_1_KEEP_1_112(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_111(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_1_KEEP_1_111(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_110(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + + +#define FOR_EACH_1_KEEP_1_110(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_109(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_1_KEEP_1_109(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_108(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + + +#define FOR_EACH_1_KEEP_1_108(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_107(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_1_KEEP_1_107(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_106(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + + +#define FOR_EACH_1_KEEP_1_106(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_105(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_1_KEEP_1_105(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_104(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + + +#define FOR_EACH_1_KEEP_1_104(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_103(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_1_KEEP_1_103(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_102(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + + +#define FOR_EACH_1_KEEP_1_102(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_101(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_1_KEEP_1_101(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_100(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + + +#define FOR_EACH_1_KEEP_1_100(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_99(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_1_KEEP_1_99(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_98(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + + +#define FOR_EACH_1_KEEP_1_98(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_97(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_1_KEEP_1_97(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_96(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + + +#define FOR_EACH_1_KEEP_1_96(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_95(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_1_KEEP_1_95(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_94(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + + +#define FOR_EACH_1_KEEP_1_94(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_93(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_1_KEEP_1_93(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_92(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + + +#define FOR_EACH_1_KEEP_1_92(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_91(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_1_KEEP_1_91(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_90(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + + +#define FOR_EACH_1_KEEP_1_90(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_89(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_1_KEEP_1_89(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_88(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + + +#define FOR_EACH_1_KEEP_1_88(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_87(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_1_KEEP_1_87(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_86(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + + +#define FOR_EACH_1_KEEP_1_86(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_85(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_1_KEEP_1_85(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_84(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + + +#define FOR_EACH_1_KEEP_1_84(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_83(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_1_KEEP_1_83(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_82(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + + +#define FOR_EACH_1_KEEP_1_82(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_81(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_1_KEEP_1_81(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_80(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + + +#define FOR_EACH_1_KEEP_1_80(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_79(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_1_KEEP_1_79(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_78(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + + +#define FOR_EACH_1_KEEP_1_78(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_77(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_1_KEEP_1_77(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_76(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + + +#define FOR_EACH_1_KEEP_1_76(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_75(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_1_KEEP_1_75(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_74(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + + +#define FOR_EACH_1_KEEP_1_74(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_73(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_1_KEEP_1_73(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_72(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + + +#define FOR_EACH_1_KEEP_1_72(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_71(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_1_KEEP_1_71(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_70(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + + +#define FOR_EACH_1_KEEP_1_70(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_69(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_1_KEEP_1_69(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_68(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + + +#define FOR_EACH_1_KEEP_1_68(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_67(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_1_KEEP_1_67(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_66(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + + +#define FOR_EACH_1_KEEP_1_66(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_65(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_1_KEEP_1_65(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_64(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + + +#define FOR_EACH_1_KEEP_1_64(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_63(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_1_KEEP_1_63(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_62(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + + +#define FOR_EACH_1_KEEP_1_62(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_61(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_1_KEEP_1_61(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_60(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + + +#define FOR_EACH_1_KEEP_1_60(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_59(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_1_KEEP_1_59(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_58(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + + +#define FOR_EACH_1_KEEP_1_58(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_57(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_1_KEEP_1_57(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_56(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + + +#define FOR_EACH_1_KEEP_1_56(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_55(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_1_KEEP_1_55(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_54(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + + +#define FOR_EACH_1_KEEP_1_54(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_53(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_1_KEEP_1_53(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_52(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + + +#define FOR_EACH_1_KEEP_1_52(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_51(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_1_KEEP_1_51(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_50(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + + +#define FOR_EACH_1_KEEP_1_50(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_49(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_1_KEEP_1_49(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_48(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + + +#define FOR_EACH_1_KEEP_1_48(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_47(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_1_KEEP_1_47(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_46(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + + +#define FOR_EACH_1_KEEP_1_46(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_45(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_1_KEEP_1_45(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_44(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + + +#define FOR_EACH_1_KEEP_1_44(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_43(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_1_KEEP_1_43(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_42(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + + +#define FOR_EACH_1_KEEP_1_42(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_41(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_1_KEEP_1_41(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_40(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + + +#define FOR_EACH_1_KEEP_1_40(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_39(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_1_KEEP_1_39(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_38(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + + +#define FOR_EACH_1_KEEP_1_38(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_37(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_1_KEEP_1_37(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_36(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + + +#define FOR_EACH_1_KEEP_1_36(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_35(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_1_KEEP_1_35(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_34(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + + +#define FOR_EACH_1_KEEP_1_34(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_33(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_1_KEEP_1_33(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_32(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + + +#define FOR_EACH_1_KEEP_1_32(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_31(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_1_KEEP_1_31(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_30(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + + +#define FOR_EACH_1_KEEP_1_30(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_29(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_1_KEEP_1_29(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_28(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + + +#define FOR_EACH_1_KEEP_1_28(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_27(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_1_KEEP_1_27(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_26(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + + +#define FOR_EACH_1_KEEP_1_26(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_25(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_1_KEEP_1_25(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_24(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + + +#define FOR_EACH_1_KEEP_1_24(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_23(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_1_KEEP_1_23(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_22(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + + +#define FOR_EACH_1_KEEP_1_22(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_21(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_1_KEEP_1_21(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_20(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + + +#define FOR_EACH_1_KEEP_1_20(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_19(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_1_KEEP_1_19(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_18(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + + +#define FOR_EACH_1_KEEP_1_18(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_17(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_1_KEEP_1_17(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_16(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + + +#define FOR_EACH_1_KEEP_1_16(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_15(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_1_KEEP_1_15(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_14(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + + +#define FOR_EACH_1_KEEP_1_14(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_13(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_1_KEEP_1_13(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_12(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + + +#define FOR_EACH_1_KEEP_1_12(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_11(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_1_KEEP_1_11(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_10(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + + +#define FOR_EACH_1_KEEP_1_10(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_9(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_1_KEEP_1_9(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_8(X, keep, P2, P3, P4, P5, P6, P7, P8, P9) + + +#define FOR_EACH_1_KEEP_1_8(X, keep, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_7(X, keep, P2, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_1_KEEP_1_7(X, keep, P1, P2, P3, P4, P5, P6, P7) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_6(X, keep, P2, P3, P4, P5, P6, P7) + + +#define FOR_EACH_1_KEEP_1_6(X, keep, P1, P2, P3, P4, P5, P6) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_5(X, keep, P2, P3, P4, P5, P6) + + +#define FOR_EACH_1_KEEP_1_5(X, keep, P1, P2, P3, P4, P5) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_4(X, keep, P2, P3, P4, P5) + + +#define FOR_EACH_1_KEEP_1_4(X, keep, P1, P2, P3, P4) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_3(X, keep, P2, P3, P4) + + +#define FOR_EACH_1_KEEP_1_3(X, keep, P1, P2, P3) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_2(X, keep, P2, P3) + + +#define FOR_EACH_1_KEEP_1_2(X, keep, P1, P2) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_1(X, keep, P2) + + + +#define FOR_EACH_1_KEEP_1_1(X, keep, P1) \ +X(keep, P1) + +#ifdef _MSC_VER +#define FOR_EACH_1_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_1_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#define FOR_EACH_2_KEEP_1_124(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_122(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_KEEP_1_122(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_120(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_2_KEEP_1_120(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_118(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_2_KEEP_1_118(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_116(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_2_KEEP_1_116(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_114(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_2_KEEP_1_114(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_112(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_2_KEEP_1_112(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_110(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_2_KEEP_1_110(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_108(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_2_KEEP_1_108(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_106(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_2_KEEP_1_106(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_104(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_2_KEEP_1_104(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_102(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_2_KEEP_1_102(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_100(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_2_KEEP_1_100(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_98(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_2_KEEP_1_98(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_96(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_2_KEEP_1_96(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_94(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_2_KEEP_1_94(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_92(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_2_KEEP_1_92(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_90(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_2_KEEP_1_90(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_88(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_2_KEEP_1_88(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_86(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_2_KEEP_1_86(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_84(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_2_KEEP_1_84(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_82(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_2_KEEP_1_82(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_80(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_2_KEEP_1_80(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_78(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_2_KEEP_1_78(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_76(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_2_KEEP_1_76(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_74(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_2_KEEP_1_74(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_72(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_2_KEEP_1_72(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_70(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_2_KEEP_1_70(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_68(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_2_KEEP_1_68(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_66(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_2_KEEP_1_66(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_64(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_2_KEEP_1_64(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_62(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_2_KEEP_1_62(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_60(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_2_KEEP_1_60(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_58(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_2_KEEP_1_58(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_56(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_2_KEEP_1_56(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_54(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_2_KEEP_1_54(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_52(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_2_KEEP_1_52(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_50(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_2_KEEP_1_50(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_48(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_2_KEEP_1_48(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_46(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_2_KEEP_1_46(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_44(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_2_KEEP_1_44(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_42(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_2_KEEP_1_42(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_40(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_2_KEEP_1_40(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_38(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_2_KEEP_1_38(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_36(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_2_KEEP_1_36(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_34(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_2_KEEP_1_34(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_32(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_2_KEEP_1_32(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_30(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_2_KEEP_1_30(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_28(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_2_KEEP_1_28(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_26(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_2_KEEP_1_26(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_24(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_2_KEEP_1_24(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_22(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_2_KEEP_1_22(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_20(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_2_KEEP_1_20(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_18(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_2_KEEP_1_18(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_16(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_2_KEEP_1_16(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_14(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_2_KEEP_1_14(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_12(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_2_KEEP_1_12(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_10(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_2_KEEP_1_10(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_8(X, keep, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_2_KEEP_1_8(X, keep, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_6(X, keep, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_2_KEEP_1_6(X, keep, P1, P2, P3, P4, P5, P6) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_4(X, keep, P3, P4, P5, P6) + + +#define FOR_EACH_2_KEEP_1_4(X, keep, P1, P2, P3, P4) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_2(X, keep, P3, P4) + + + +#define FOR_EACH_2_KEEP_1_1(...) + +#define FOR_EACH_2_KEEP_1_0(...) + +#define FOR_EACH_2_KEEP_1_2(X, keep, P1, P2) \ + X(keep, P1, P2) \ + +#ifdef _MSC_VER +#define FOR_EACH_2_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + + +#define FOR_EACH_2_KEEP_2_124(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_122(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_KEEP_2_122(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_120(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_2_KEEP_2_120(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_118(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_2_KEEP_2_118(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_116(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_2_KEEP_2_116(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_114(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_2_KEEP_2_114(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_112(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_2_KEEP_2_112(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_110(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_2_KEEP_2_110(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_108(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_2_KEEP_2_108(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_106(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_2_KEEP_2_106(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_104(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_2_KEEP_2_104(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_102(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_2_KEEP_2_102(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_100(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_2_KEEP_2_100(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_98(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_2_KEEP_2_98(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_96(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_2_KEEP_2_96(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_94(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_2_KEEP_2_94(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_92(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_2_KEEP_2_92(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_90(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_2_KEEP_2_90(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_88(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_2_KEEP_2_88(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_86(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_2_KEEP_2_86(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_84(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_2_KEEP_2_84(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_82(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_2_KEEP_2_82(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_80(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_2_KEEP_2_80(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_78(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_2_KEEP_2_78(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_76(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_2_KEEP_2_76(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_74(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_2_KEEP_2_74(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_72(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_2_KEEP_2_72(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_70(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_2_KEEP_2_70(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_68(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_2_KEEP_2_68(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_66(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_2_KEEP_2_66(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_64(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_2_KEEP_2_64(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_62(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_2_KEEP_2_62(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_60(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_2_KEEP_2_60(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_58(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_2_KEEP_2_58(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_56(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_2_KEEP_2_56(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_54(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_2_KEEP_2_54(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_52(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_2_KEEP_2_52(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_50(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_2_KEEP_2_50(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_48(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_2_KEEP_2_48(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_46(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_2_KEEP_2_46(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_44(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_2_KEEP_2_44(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_42(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_2_KEEP_2_42(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_40(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_2_KEEP_2_40(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_38(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_2_KEEP_2_38(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_36(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_2_KEEP_2_36(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_34(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_2_KEEP_2_34(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_32(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_2_KEEP_2_32(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_30(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_2_KEEP_2_30(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_28(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_2_KEEP_2_28(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_26(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_2_KEEP_2_26(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_24(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_2_KEEP_2_24(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_22(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_2_KEEP_2_22(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_20(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_2_KEEP_2_20(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_18(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_2_KEEP_2_18(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_16(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_2_KEEP_2_16(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_14(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_2_KEEP_2_14(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_12(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_2_KEEP_2_12(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_10(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_2_KEEP_2_10(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_8(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_2_KEEP_2_8(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_6(X, keep1, keep2, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_2_KEEP_2_6(X, keep1, keep2, P1, P2, P3, P4, P5, P6) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_4(X, keep1, keep2, P3, P4, P5, P6) + + +#define FOR_EACH_2_KEEP_2_4(X, keep1, keep2, P1, P2, P3, P4) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_2(X, keep1, keep2, P3, P4) + + + +#define FOR_EACH_2_KEEP_2_1(...) + +#define FOR_EACH_2_KEEP_2_0(...) + +#define FOR_EACH_2_KEEP_2_2(X, keep1, keep2, P1, P2) \ + X(keep1, keep2, P1, P2) \ + +#ifdef _MSC_VER +#define FOR_EACH_2_KEEP_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_2_, C2(DEC,C2(DEC,C1(COUNT_ARG(__VA_ARGS__))))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2_KEEP_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_2_, C2(DEC, C2(DEC,C1(COUNT_ARG(__VA_ARGS__))))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + + +#define FOR_EACH_2_0(...) + +#define FOR_EACH_2_2(X, P1, P2) \ +X(P1, P2) + +#define FOR_EACH_2_4(X, P1, P2, P3, P4) \ +X(P1, P2) \ +FOR_EACH_2_2(X, P3, P4) + +#define FOR_EACH_2_6(X, P1, P2, P3, P4, P5, P6) \ +X(P1, P2) \ +FOR_EACH_2_4(X, P3, P4, P5, P6) + +#define FOR_EACH_2_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P1, P2) \ +FOR_EACH_2_6(X, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P1, P2) \ +FOR_EACH_2_8(X, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P1, P2) \ +FOR_EACH_2_10(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P1, P2) \ +FOR_EACH_2_12(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P1, P2) \ +FOR_EACH_2_14(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P1, P2) \ +FOR_EACH_2_16(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P1, P2) \ +FOR_EACH_2_18(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P1, P2) \ +FOR_EACH_2_20(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P1, P2) \ +FOR_EACH_2_22(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P1, P2) \ +FOR_EACH_2_24(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P1, P2) \ +FOR_EACH_2_26(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P1, P2) \ +FOR_EACH_2_28(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P1, P2) \ +FOR_EACH_2_30(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P1, P2) \ +FOR_EACH_2_32(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P1, P2) \ +FOR_EACH_2_34(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P1, P2) \ +FOR_EACH_2_36(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P1, P2) \ +FOR_EACH_2_38(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P1, P2) \ +FOR_EACH_2_40(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P1, P2) \ +FOR_EACH_2_42(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P1, P2) \ +FOR_EACH_2_44(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P1, P2) \ +FOR_EACH_2_46(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P1, P2) \ +FOR_EACH_2_48(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P1, P2) \ +FOR_EACH_2_50(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P1, P2) \ +FOR_EACH_2_52(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P1, P2) \ +FOR_EACH_2_54(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P1, P2) \ +FOR_EACH_2_56(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P1, P2) \ +FOR_EACH_2_58(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P1, P2) \ +FOR_EACH_2_60(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P1, P2) \ +FOR_EACH_2_62(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P1, P2) \ +FOR_EACH_2_64(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P1, P2) \ +FOR_EACH_2_66(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P1, P2) \ +FOR_EACH_2_68(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P1, P2) \ +FOR_EACH_2_70(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P1, P2) \ +FOR_EACH_2_72(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P1, P2) \ +FOR_EACH_2_74(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P1, P2) \ +FOR_EACH_2_76(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P1, P2) \ +FOR_EACH_2_78(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P1, P2) \ +FOR_EACH_2_80(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P1, P2) \ +FOR_EACH_2_82(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P1, P2) \ +FOR_EACH_2_84(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P1, P2) \ +FOR_EACH_2_86(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P1, P2) \ +FOR_EACH_2_88(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P1, P2) \ +FOR_EACH_2_90(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P1, P2) \ +FOR_EACH_2_92(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P1, P2) \ +FOR_EACH_2_94(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P1, P2) \ +FOR_EACH_2_96(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P1, P2) \ +FOR_EACH_2_98(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P1, P2) \ +FOR_EACH_2_100(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P1, P2) \ +FOR_EACH_2_102(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P1, P2) \ +FOR_EACH_2_104(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P1, P2) \ +FOR_EACH_2_106(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P1, P2) \ +FOR_EACH_2_108(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P1, P2) \ +FOR_EACH_2_110(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P1, P2) \ +FOR_EACH_2_112(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P1, P2) \ +FOR_EACH_2_114(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P1, P2) \ +FOR_EACH_2_116(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P1, P2) \ +FOR_EACH_2_118(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P1, P2) \ +FOR_EACH_2_120(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_2_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P1, P2) \ +FOR_EACH_2_122(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_REVERSE_0(...) + +#define FOR_EACH_2_REVERSE_2(X, P1, P2) \ +X(P1, P2) + +#define FOR_EACH_2_REVERSE_4(X, P1, P2, P3, P4) \ +X(P3, P4) \ +FOR_EACH_2_REVERSE_2(X, P1, P2) + +#define FOR_EACH_2_REVERSE_6(X, P1, P2, P3, P4, P5, P6) \ +X(P5, P6) \ +FOR_EACH_2_REVERSE_4(X, P1, P2, P3, P4) + +#define FOR_EACH_2_REVERSE_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P7, P8) \ +FOR_EACH_2_REVERSE_6(X, P1, P2, P3, P4, P5, P6) + +#define FOR_EACH_2_REVERSE_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P9, P10) \ +FOR_EACH_2_REVERSE_8(X, P1, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_REVERSE_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P11, P12) \ +FOR_EACH_2_REVERSE_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_REVERSE_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P13, P14) \ +FOR_EACH_2_REVERSE_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_REVERSE_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P15, P16) \ +FOR_EACH_2_REVERSE_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_REVERSE_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P17, P18) \ +FOR_EACH_2_REVERSE_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_REVERSE_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P19, P20) \ +FOR_EACH_2_REVERSE_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_REVERSE_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P21, P22) \ +FOR_EACH_2_REVERSE_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_REVERSE_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P23, P24) \ +FOR_EACH_2_REVERSE_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_REVERSE_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P25, P26) \ +FOR_EACH_2_REVERSE_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_REVERSE_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P27, P28) \ +FOR_EACH_2_REVERSE_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_REVERSE_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P29, P30) \ +FOR_EACH_2_REVERSE_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_REVERSE_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P31, P32) \ +FOR_EACH_2_REVERSE_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_REVERSE_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P33, P34) \ +FOR_EACH_2_REVERSE_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_REVERSE_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P35, P36) \ +FOR_EACH_2_REVERSE_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_REVERSE_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P37, P38) \ +FOR_EACH_2_REVERSE_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_REVERSE_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P39, P40) \ +FOR_EACH_2_REVERSE_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_REVERSE_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P41, P42) \ +FOR_EACH_2_REVERSE_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_REVERSE_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P43, P44) \ +FOR_EACH_2_REVERSE_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_REVERSE_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P45, P46) \ +FOR_EACH_2_REVERSE_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_REVERSE_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P47, P48) \ +FOR_EACH_2_REVERSE_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_REVERSE_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P49, P50) \ +FOR_EACH_2_REVERSE_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_REVERSE_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P51, P52) \ +FOR_EACH_2_REVERSE_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_REVERSE_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P53, P54) \ +FOR_EACH_2_REVERSE_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_REVERSE_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P55, P56) \ +FOR_EACH_2_REVERSE_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_REVERSE_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P57, P58) \ +FOR_EACH_2_REVERSE_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_REVERSE_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P59, P60) \ +FOR_EACH_2_REVERSE_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_REVERSE_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P61, P62) \ +FOR_EACH_2_REVERSE_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_REVERSE_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P63, P64) \ +FOR_EACH_2_REVERSE_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_REVERSE_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P65, P66) \ +FOR_EACH_2_REVERSE_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_REVERSE_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P67, P68) \ +FOR_EACH_2_REVERSE_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_REVERSE_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P69, P70) \ +FOR_EACH_2_REVERSE_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_REVERSE_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P71, P72) \ +FOR_EACH_2_REVERSE_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_REVERSE_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P73, P74) \ +FOR_EACH_2_REVERSE_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_REVERSE_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P75, P76) \ +FOR_EACH_2_REVERSE_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_REVERSE_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P77, P78) \ +FOR_EACH_2_REVERSE_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_REVERSE_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P79, P80) \ +FOR_EACH_2_REVERSE_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_REVERSE_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P81, P82) \ +FOR_EACH_2_REVERSE_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_REVERSE_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P83, P84) \ +FOR_EACH_2_REVERSE_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_REVERSE_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P85, P86) \ +FOR_EACH_2_REVERSE_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_REVERSE_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P87, P88) \ +FOR_EACH_2_REVERSE_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_REVERSE_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P89, P90) \ +FOR_EACH_2_REVERSE_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_REVERSE_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P91, P92) \ +FOR_EACH_2_REVERSE_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_REVERSE_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P93, P94) \ +FOR_EACH_2_REVERSE_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_REVERSE_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P95, P96) \ +FOR_EACH_2_REVERSE_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_REVERSE_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P97, P98) \ +FOR_EACH_2_REVERSE_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_REVERSE_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P99, P100) \ +FOR_EACH_2_REVERSE_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_REVERSE_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P101, P102) \ +FOR_EACH_2_REVERSE_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_REVERSE_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P103, P104) \ +FOR_EACH_2_REVERSE_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_REVERSE_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P105, P106) \ +FOR_EACH_2_REVERSE_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_REVERSE_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P107, P108) \ +FOR_EACH_2_REVERSE_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_REVERSE_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P109, P110) \ +FOR_EACH_2_REVERSE_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_REVERSE_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P111, P112) \ +FOR_EACH_2_REVERSE_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_REVERSE_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P113, P114) \ +FOR_EACH_2_REVERSE_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_REVERSE_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P115, P116) \ +FOR_EACH_2_REVERSE_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_REVERSE_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P117, P118) \ +FOR_EACH_2_REVERSE_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_REVERSE_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P119, P120) \ +FOR_EACH_2_REVERSE_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_REVERSE_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P121, P122) \ +FOR_EACH_2_REVERSE_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_REVERSE_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P123, P124) \ +FOR_EACH_2_REVERSE_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_1_COUNTED_0(...) + +#define FOR_EACH_1_COUNTED_1(X, P1) \ + X(1, P1) + +#define FOR_EACH_1_COUNTED_2(X, P1, P2) \ +X(2, P1) \ +FOR_EACH_1_COUNTED_1(X, P2) + +#define FOR_EACH_1_COUNTED_3(X, P1, P2, P3) \ +X(3, P1) \ +FOR_EACH_1_COUNTED_2(X, P2, P3) + +#define FOR_EACH_1_COUNTED_4(X, P1, P2, P3, P4) \ +X(4, P1) \ +FOR_EACH_1_COUNTED_3(X, P2, P3, P4) + +#define FOR_EACH_1_COUNTED_5(X, P1, P2, P3, P4, P5) \ +X(5, P1) \ +FOR_EACH_1_COUNTED_4(X, P2, P3, P4, P5) + +#define FOR_EACH_1_COUNTED_6(X, P1, P2, P3, P4, P5, P6) \ +X(6, P1) \ +FOR_EACH_1_COUNTED_5(X, P2, P3, P4, P5, P6) + +#define FOR_EACH_1_COUNTED_7(X, P1, P2, P3, P4, P5, P6, P7) \ +X(7, P1) \ +FOR_EACH_1_COUNTED_6(X, P2, P3, P4, P5, P6, P7) + +#define FOR_EACH_1_COUNTED_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(8, P1) \ +FOR_EACH_1_COUNTED_7(X, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_1_COUNTED_9(X, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(9, P1) \ +FOR_EACH_1_COUNTED_8(X, P2, P3, P4, P5, P6, P7, P8, P9) + +#define FOR_EACH_1_COUNTED_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(10, P1) \ +FOR_EACH_1_COUNTED_9(X, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_1_COUNTED_11(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(11, P1) \ +FOR_EACH_1_COUNTED_10(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + +#define FOR_EACH_1_COUNTED_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(12, P1) \ +FOR_EACH_1_COUNTED_11(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_1_COUNTED_13(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(13, P1) \ +FOR_EACH_1_COUNTED_12(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + +#define FOR_EACH_1_COUNTED_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(14, P1) \ +FOR_EACH_1_COUNTED_13(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_1_COUNTED_15(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(15, P1) \ +FOR_EACH_1_COUNTED_14(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + +#define FOR_EACH_1_COUNTED_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(16, P1) \ +FOR_EACH_1_COUNTED_15(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_1_COUNTED_17(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(17, P1) \ +FOR_EACH_1_COUNTED_16(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + +#define FOR_EACH_1_COUNTED_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(18, P1) \ +FOR_EACH_1_COUNTED_17(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_1_COUNTED_19(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(19, P1) \ +FOR_EACH_1_COUNTED_18(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + +#define FOR_EACH_1_COUNTED_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(20, P1) \ +FOR_EACH_1_COUNTED_19(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_1_COUNTED_21(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(21, P1) \ +FOR_EACH_1_COUNTED_20(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + +#define FOR_EACH_1_COUNTED_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(22, P1) \ +FOR_EACH_1_COUNTED_21(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_1_COUNTED_23(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(23, P1) \ +FOR_EACH_1_COUNTED_22(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + +#define FOR_EACH_1_COUNTED_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(24, P1) \ +FOR_EACH_1_COUNTED_23(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_1_COUNTED_25(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(25, P1) \ +FOR_EACH_1_COUNTED_24(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + +#define FOR_EACH_1_COUNTED_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(26, P1) \ +FOR_EACH_1_COUNTED_25(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_1_COUNTED_27(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(27, P1) \ +FOR_EACH_1_COUNTED_26(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + +#define FOR_EACH_1_COUNTED_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(28, P1) \ +FOR_EACH_1_COUNTED_27(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_1_COUNTED_29(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(29, P1) \ +FOR_EACH_1_COUNTED_28(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + +#define FOR_EACH_1_COUNTED_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(30, P1) \ +FOR_EACH_1_COUNTED_29(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_1_COUNTED_31(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(31, P1) \ +FOR_EACH_1_COUNTED_30(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + +#define FOR_EACH_1_COUNTED_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(32, P1) \ +FOR_EACH_1_COUNTED_31(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_1_COUNTED_33(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(33, P1) \ +FOR_EACH_1_COUNTED_32(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + +#define FOR_EACH_1_COUNTED_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(34, P1) \ +FOR_EACH_1_COUNTED_33(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_1_COUNTED_35(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(35, P1) \ +FOR_EACH_1_COUNTED_34(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + +#define FOR_EACH_1_COUNTED_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(36, P1) \ +FOR_EACH_1_COUNTED_35(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_1_COUNTED_37(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(37, P1) \ +FOR_EACH_1_COUNTED_36(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + +#define FOR_EACH_1_COUNTED_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(38, P1) \ +FOR_EACH_1_COUNTED_37(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_1_COUNTED_39(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(39, P1) \ +FOR_EACH_1_COUNTED_38(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + +#define FOR_EACH_1_COUNTED_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(40, P1) \ +FOR_EACH_1_COUNTED_39(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_1_COUNTED_41(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(41, P1) \ +FOR_EACH_1_COUNTED_40(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + +#define FOR_EACH_1_COUNTED_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(42, P1) \ +FOR_EACH_1_COUNTED_41(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_1_COUNTED_43(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(43, P1) \ +FOR_EACH_1_COUNTED_42(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + +#define FOR_EACH_1_COUNTED_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(44, P1) \ +FOR_EACH_1_COUNTED_43(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_1_COUNTED_45(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(45, P1) \ +FOR_EACH_1_COUNTED_44(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + +#define FOR_EACH_1_COUNTED_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(46, P1) \ +FOR_EACH_1_COUNTED_45(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_1_COUNTED_47(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(47, P1) \ +FOR_EACH_1_COUNTED_46(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + +#define FOR_EACH_1_COUNTED_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(48, P1) \ +FOR_EACH_1_COUNTED_47(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_1_COUNTED_49(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(49, P1) \ +FOR_EACH_1_COUNTED_48(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + +#define FOR_EACH_1_COUNTED_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(50, P1) \ +FOR_EACH_1_COUNTED_49(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_1_COUNTED_51(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(51, P1) \ +FOR_EACH_1_COUNTED_50(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + +#define FOR_EACH_1_COUNTED_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(52, P1) \ +FOR_EACH_1_COUNTED_51(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_1_COUNTED_53(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(53, P1) \ +FOR_EACH_1_COUNTED_52(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + +#define FOR_EACH_1_COUNTED_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(54, P1) \ +FOR_EACH_1_COUNTED_53(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_1_COUNTED_55(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(55, P1) \ +FOR_EACH_1_COUNTED_54(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + +#define FOR_EACH_1_COUNTED_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(56, P1) \ +FOR_EACH_1_COUNTED_55(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_1_COUNTED_57(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(57, P1) \ +FOR_EACH_1_COUNTED_56(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + +#define FOR_EACH_1_COUNTED_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(58, P1) \ +FOR_EACH_1_COUNTED_57(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_1_COUNTED_59(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(59, P1) \ +FOR_EACH_1_COUNTED_58(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + +#define FOR_EACH_1_COUNTED_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(60, P1) \ +FOR_EACH_1_COUNTED_59(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_1_COUNTED_61(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(61, P1) \ +FOR_EACH_1_COUNTED_60(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + +#define FOR_EACH_1_COUNTED_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(62, P1) \ +FOR_EACH_1_COUNTED_61(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_1_COUNTED_63(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(63, P1) \ +FOR_EACH_1_COUNTED_62(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + +#define FOR_EACH_1_COUNTED_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(64, P1) \ +FOR_EACH_1_COUNTED_63(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_1_COUNTED_65(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(65, P1) \ +FOR_EACH_1_COUNTED_64(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + +#define FOR_EACH_1_COUNTED_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(66, P1) \ +FOR_EACH_1_COUNTED_65(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_1_COUNTED_67(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(67, P1) \ +FOR_EACH_1_COUNTED_66(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + +#define FOR_EACH_1_COUNTED_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(68, P1) \ +FOR_EACH_1_COUNTED_67(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_1_COUNTED_69(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(69, P1) \ +FOR_EACH_1_COUNTED_68(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + +#define FOR_EACH_1_COUNTED_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(70, P1) \ +FOR_EACH_1_COUNTED_69(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_1_COUNTED_71(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(71, P1) \ +FOR_EACH_1_COUNTED_70(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + +#define FOR_EACH_1_COUNTED_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(72, P1) \ +FOR_EACH_1_COUNTED_71(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_1_COUNTED_73(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(73, P1) \ +FOR_EACH_1_COUNTED_72(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + +#define FOR_EACH_1_COUNTED_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(74, P1) \ +FOR_EACH_1_COUNTED_73(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_1_COUNTED_75(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(75, P1) \ +FOR_EACH_1_COUNTED_74(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + +#define FOR_EACH_1_COUNTED_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(76, P1) \ +FOR_EACH_1_COUNTED_75(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_1_COUNTED_77(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(77, P1) \ +FOR_EACH_1_COUNTED_76(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + +#define FOR_EACH_1_COUNTED_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(78, P1) \ +FOR_EACH_1_COUNTED_77(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_1_COUNTED_79(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(79, P1) \ +FOR_EACH_1_COUNTED_78(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + +#define FOR_EACH_1_COUNTED_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(80, P1) \ +FOR_EACH_1_COUNTED_79(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_1_COUNTED_81(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(81, P1) \ +FOR_EACH_1_COUNTED_80(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + +#define FOR_EACH_1_COUNTED_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(82, P1) \ +FOR_EACH_1_COUNTED_81(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_1_COUNTED_83(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(83, P1) \ +FOR_EACH_1_COUNTED_82(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + +#define FOR_EACH_1_COUNTED_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(84, P1) \ +FOR_EACH_1_COUNTED_83(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_1_COUNTED_85(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(85, P1) \ +FOR_EACH_1_COUNTED_84(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + +#define FOR_EACH_1_COUNTED_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(86, P1) \ +FOR_EACH_1_COUNTED_85(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_1_COUNTED_87(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(87, P1) \ +FOR_EACH_1_COUNTED_86(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + +#define FOR_EACH_1_COUNTED_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(88, P1) \ +FOR_EACH_1_COUNTED_87(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_1_COUNTED_89(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(89, P1) \ +FOR_EACH_1_COUNTED_88(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + +#define FOR_EACH_1_COUNTED_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(90, P1) \ +FOR_EACH_1_COUNTED_89(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_1_COUNTED_91(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(91, P1) \ +FOR_EACH_1_COUNTED_90(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + +#define FOR_EACH_1_COUNTED_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(92, P1) \ +FOR_EACH_1_COUNTED_91(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_1_COUNTED_93(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(93, P1) \ +FOR_EACH_1_COUNTED_92(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + +#define FOR_EACH_1_COUNTED_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(94, P1) \ +FOR_EACH_1_COUNTED_93(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_1_COUNTED_95(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(95, P1) \ +FOR_EACH_1_COUNTED_94(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + +#define FOR_EACH_1_COUNTED_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(96, P1) \ +FOR_EACH_1_COUNTED_95(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_1_COUNTED_97(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(97, P1) \ +FOR_EACH_1_COUNTED_96(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + +#define FOR_EACH_1_COUNTED_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(98, P1) \ +FOR_EACH_1_COUNTED_97(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_1_COUNTED_99(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(99, P1) \ +FOR_EACH_1_COUNTED_98(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + +#define FOR_EACH_1_COUNTED_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(100, P1) \ +FOR_EACH_1_COUNTED_99(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_1_COUNTED_101(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(101, P1) \ +FOR_EACH_1_COUNTED_100(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + +#define FOR_EACH_1_COUNTED_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(102, P1) \ +FOR_EACH_1_COUNTED_101(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_1_COUNTED_103(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(103, P1) \ +FOR_EACH_1_COUNTED_102(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + +#define FOR_EACH_1_COUNTED_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(104, P1) \ +FOR_EACH_1_COUNTED_103(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_1_COUNTED_105(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(105, P1) \ +FOR_EACH_1_COUNTED_104(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + +#define FOR_EACH_1_COUNTED_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(106, P1) \ +FOR_EACH_1_COUNTED_105(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_1_COUNTED_107(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(107, P1) \ +FOR_EACH_1_COUNTED_106(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + +#define FOR_EACH_1_COUNTED_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(108, P1) \ +FOR_EACH_1_COUNTED_107(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_1_COUNTED_109(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(109, P1) \ +FOR_EACH_1_COUNTED_108(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + +#define FOR_EACH_1_COUNTED_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(110, P1) \ +FOR_EACH_1_COUNTED_109(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_1_COUNTED_111(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(111, P1) \ +FOR_EACH_1_COUNTED_110(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + +#define FOR_EACH_1_COUNTED_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(112, P1) \ +FOR_EACH_1_COUNTED_111(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_1_COUNTED_113(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(113, P1) \ +FOR_EACH_1_COUNTED_112(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + +#define FOR_EACH_1_COUNTED_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(114, P1) \ +FOR_EACH_1_COUNTED_113(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_1_COUNTED_115(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(115, P1) \ +FOR_EACH_1_COUNTED_114(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + +#define FOR_EACH_1_COUNTED_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(116, P1) \ +FOR_EACH_1_COUNTED_115(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_1_COUNTED_117(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(117, P1) \ +FOR_EACH_1_COUNTED_116(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + +#define FOR_EACH_1_COUNTED_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(118, P1) \ +FOR_EACH_1_COUNTED_117(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_1_COUNTED_119(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(119, P1) \ +FOR_EACH_1_COUNTED_118(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + +#define FOR_EACH_1_COUNTED_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(120, P1) \ +FOR_EACH_1_COUNTED_119(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_1_COUNTED_121(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(121, P1) \ +FOR_EACH_1_COUNTED_120(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + +#define FOR_EACH_1_COUNTED_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(122, P1) \ +FOR_EACH_1_COUNTED_121(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_1_COUNTED_123(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(123, P1) \ +FOR_EACH_1_COUNTED_122(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + +#define FOR_EACH_1_COUNTED_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(124, P1) \ +FOR_EACH_1_COUNTED_123(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_COUNTED_0(...) + +#define FOR_EACH_2_COUNTED_2(X, P1, P2) \ + X(2, P1, P2) + +#define FOR_EACH_2_COUNTED_4(X, P1, P2, P3, P4) \ +X(4, P1, P2) \ +FOR_EACH_2_COUNTED_2(X, P3, P4) + +#define FOR_EACH_2_COUNTED_6(X, P1, P2, P3, P4, P5, P6) \ +X(6, P1, P2) \ +FOR_EACH_2_COUNTED_4(X, P3, P4, P5, P6) + +#define FOR_EACH_2_COUNTED_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(8, P1, P2) \ +FOR_EACH_2_COUNTED_6(X, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_COUNTED_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(10, P1, P2) \ +FOR_EACH_2_COUNTED_8(X, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_COUNTED_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(12, P1, P2) \ +FOR_EACH_2_COUNTED_10(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_COUNTED_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(14, P1, P2) \ +FOR_EACH_2_COUNTED_12(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_COUNTED_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(16, P1, P2) \ +FOR_EACH_2_COUNTED_14(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_COUNTED_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(18, P1, P2) \ +FOR_EACH_2_COUNTED_16(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_COUNTED_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(20, P1, P2) \ +FOR_EACH_2_COUNTED_18(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_COUNTED_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(22, P1, P2) \ +FOR_EACH_2_COUNTED_20(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_COUNTED_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(24, P1, P2) \ +FOR_EACH_2_COUNTED_22(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_COUNTED_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(26, P1, P2) \ +FOR_EACH_2_COUNTED_24(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_COUNTED_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(28, P1, P2) \ +FOR_EACH_2_COUNTED_26(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_COUNTED_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(30, P1, P2) \ +FOR_EACH_2_COUNTED_28(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_COUNTED_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(32, P1, P2) \ +FOR_EACH_2_COUNTED_30(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_COUNTED_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(34, P1, P2) \ +FOR_EACH_2_COUNTED_32(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_COUNTED_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(36, P1, P2) \ +FOR_EACH_2_COUNTED_34(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_COUNTED_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(38, P1, P2) \ +FOR_EACH_2_COUNTED_36(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_COUNTED_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(40, P1, P2) \ +FOR_EACH_2_COUNTED_38(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_COUNTED_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(42, P1, P2) \ +FOR_EACH_2_COUNTED_40(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_COUNTED_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(44, P1, P2) \ +FOR_EACH_2_COUNTED_42(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_COUNTED_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(46, P1, P2) \ +FOR_EACH_2_COUNTED_44(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_COUNTED_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(48, P1, P2) \ +FOR_EACH_2_COUNTED_46(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_COUNTED_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(50, P1, P2) \ +FOR_EACH_2_COUNTED_48(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_COUNTED_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(52, P1, P2) \ +FOR_EACH_2_COUNTED_50(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_COUNTED_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(54, P1, P2) \ +FOR_EACH_2_COUNTED_52(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_COUNTED_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(56, P1, P2) \ +FOR_EACH_2_COUNTED_54(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_COUNTED_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(58, P1, P2) \ +FOR_EACH_2_COUNTED_56(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_COUNTED_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(60, P1, P2) \ +FOR_EACH_2_COUNTED_58(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_COUNTED_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(62, P1, P2) \ +FOR_EACH_2_COUNTED_60(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_COUNTED_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(64, P1, P2) \ +FOR_EACH_2_COUNTED_62(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_COUNTED_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(66, P1, P2) \ +FOR_EACH_2_COUNTED_64(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_COUNTED_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(68, P1, P2) \ +FOR_EACH_2_COUNTED_66(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_COUNTED_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(70, P1, P2) \ +FOR_EACH_2_COUNTED_68(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_COUNTED_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(72, P1, P2) \ +FOR_EACH_2_COUNTED_70(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_COUNTED_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(74, P1, P2) \ +FOR_EACH_2_COUNTED_72(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_COUNTED_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(76, P1, P2) \ +FOR_EACH_2_COUNTED_74(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_COUNTED_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(78, P1, P2) \ +FOR_EACH_2_COUNTED_76(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_COUNTED_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(80, P1, P2) \ +FOR_EACH_2_COUNTED_78(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_COUNTED_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(82, P1, P2) \ +FOR_EACH_2_COUNTED_80(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_COUNTED_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(84, P1, P2) \ +FOR_EACH_2_COUNTED_82(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_COUNTED_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(86, P1, P2) \ +FOR_EACH_2_COUNTED_84(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_COUNTED_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(88, P1, P2) \ +FOR_EACH_2_COUNTED_86(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_COUNTED_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(90, P1, P2) \ +FOR_EACH_2_COUNTED_88(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_COUNTED_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(92, P1, P2) \ +FOR_EACH_2_COUNTED_90(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_COUNTED_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(94, P1, P2) \ +FOR_EACH_2_COUNTED_92(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_COUNTED_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(96, P1, P2) \ +FOR_EACH_2_COUNTED_94(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_COUNTED_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(98, P1, P2) \ +FOR_EACH_2_COUNTED_96(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_COUNTED_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(100, P1, P2) \ +FOR_EACH_2_COUNTED_98(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_COUNTED_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(102, P1, P2) \ +FOR_EACH_2_COUNTED_100(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_COUNTED_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(104, P1, P2) \ +FOR_EACH_2_COUNTED_102(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_COUNTED_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(106, P1, P2) \ +FOR_EACH_2_COUNTED_104(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_COUNTED_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(108, P1, P2) \ +FOR_EACH_2_COUNTED_106(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_COUNTED_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(110, P1, P2) \ +FOR_EACH_2_COUNTED_108(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_COUNTED_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(112, P1, P2) \ +FOR_EACH_2_COUNTED_110(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_COUNTED_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(114, P1, P2) \ +FOR_EACH_2_COUNTED_112(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_COUNTED_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(116, P1, P2) \ +FOR_EACH_2_COUNTED_114(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_COUNTED_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(118, P1, P2) \ +FOR_EACH_2_COUNTED_116(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_COUNTED_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(120, P1, P2) \ +FOR_EACH_2_COUNTED_118(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_COUNTED_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(122, P1, P2) \ +FOR_EACH_2_COUNTED_120(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_2_COUNTED_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(124, P1, P2) \ +FOR_EACH_2_COUNTED_122(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#ifdef _MSC_VER +#define FOR_EACH_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +/*the COUNTED breed of FOR_EACH macro invokes a macro with 3 parameters: 1st being the count of invocation. For example. +FOR_EACH_2_COUNTER(MACRO, a,b,c,d,e,f) will result in +MACRO(6, a,b) +MACRO(4, c,d) +MACRO(2, e,f) +This macro exists because we need a "stop condition" in outputting COMMA... when calling a function f(a,b,c,d) cannot be f(a,b,c,d,) <=doesn't compile (as opposed to enum definition) +*/ +#define FOR_EACH_2_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_1_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) + +/*FOR_EACH_2_REVERSE acts just like FOR_EACH_2, but in reverse order. Example: +FOR_EACH_2_REVERSE(X,a,b,c,d,e,f) => X(e,f) X(c,d) X (a, b) in this order */ +#define FOR_EACH_2_REVERSE(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_REVERSE_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_2_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_1_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_2_REVERSE(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_REVERSE_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#ifdef _MSC_VER +#define EXPAND_OR_C1(x) x +#else +#define EXPAND_OR_C1(...) __VA_ARGS__ +#endif + +#define EXPAND_ARGS(...) __VA_ARGS__ +#define EXPAND_TWICE(...) EXPAND_ARGS(__VA_ARGS__) + +#define DO_0(MACRO, ...) \ +MACRO(0, __VA_ARGS__) + +#define DO_1(MACRO, ...) \ +MACRO(1, __VA_ARGS__) \ +DO_0(MACRO, __VA_ARGS__) + + +#define DO_2(MACRO, ...) \ +MACRO(2, __VA_ARGS__) \ +DO_1(MACRO, __VA_ARGS__) + + +#define DO_3(MACRO, ...) \ +MACRO(3, __VA_ARGS__) \ +DO_2(MACRO, __VA_ARGS__) + + +#define DO_4(MACRO, ...) \ +MACRO(4, __VA_ARGS__) \ +DO_3(MACRO, __VA_ARGS__) + + +#define DO_5(MACRO, ...) \ +MACRO(5, __VA_ARGS__) \ +DO_4(MACRO, __VA_ARGS__) + + +#define DO_6(MACRO, ...) \ +MACRO(6, __VA_ARGS__) \ +DO_5(MACRO, __VA_ARGS__) + + +#define DO_7(MACRO, ...) \ +MACRO(7, __VA_ARGS__) \ +DO_6(MACRO, __VA_ARGS__) + + +#define DO_8(MACRO, ...) \ +MACRO(8, __VA_ARGS__) \ +DO_7(MACRO, __VA_ARGS__) + + +#define DO_9(MACRO, ...) \ +MACRO(9, __VA_ARGS__) \ +DO_8(MACRO, __VA_ARGS__) + + +#define DO_10(MACRO, ...) \ +MACRO(10, __VA_ARGS__) \ +DO_9(MACRO, __VA_ARGS__) + + +#define DO_11(MACRO, ...) \ +MACRO(11, __VA_ARGS__) \ +DO_10(MACRO, __VA_ARGS__) + + +#define DO_12(MACRO, ...) \ +MACRO(12, __VA_ARGS__) \ +DO_11(MACRO, __VA_ARGS__) + + +#define DO_13(MACRO, ...) \ +MACRO(13, __VA_ARGS__) \ +DO_12(MACRO, __VA_ARGS__) + + +#define DO_14(MACRO, ...) \ +MACRO(14, __VA_ARGS__) \ +DO_13(MACRO, __VA_ARGS__) + + +#define DO_15(MACRO, ...) \ +MACRO(15, __VA_ARGS__) \ +DO_14(MACRO, __VA_ARGS__) + + +#define DO_16(MACRO, ...) \ +MACRO(16, __VA_ARGS__) \ +DO_15(MACRO, __VA_ARGS__) + + +#define DO_17(MACRO, ...) \ +MACRO(17, __VA_ARGS__) \ +DO_16(MACRO, __VA_ARGS__) + + +#define DO_18(MACRO, ...) \ +MACRO(18, __VA_ARGS__) \ +DO_17(MACRO, __VA_ARGS__) + + +#define DO_19(MACRO, ...) \ +MACRO(19, __VA_ARGS__) \ +DO_18(MACRO, __VA_ARGS__) + + +#define DO_20(MACRO, ...) \ +MACRO(20, __VA_ARGS__) \ +DO_19(MACRO, __VA_ARGS__) + + +#define DO_21(MACRO, ...) \ +MACRO(21, __VA_ARGS__) \ +DO_20(MACRO, __VA_ARGS__) + + +#define DO_22(MACRO, ...) \ +MACRO(22, __VA_ARGS__) \ +DO_21(MACRO, __VA_ARGS__) + + +#define DO_23(MACRO, ...) \ +MACRO(23, __VA_ARGS__) \ +DO_22(MACRO, __VA_ARGS__) + + +#define DO_24(MACRO, ...) \ +MACRO(24, __VA_ARGS__) \ +DO_23(MACRO, __VA_ARGS__) + + +#define DO_25(MACRO, ...) \ +MACRO(25, __VA_ARGS__) \ +DO_24(MACRO, __VA_ARGS__) + + +#define DO_26(MACRO, ...) \ +MACRO(26, __VA_ARGS__) \ +DO_25(MACRO, __VA_ARGS__) + + +#define DO_27(MACRO, ...) \ +MACRO(27, __VA_ARGS__) \ +DO_26(MACRO, __VA_ARGS__) + + +#define DO_28(MACRO, ...) \ +MACRO(28, __VA_ARGS__) \ +DO_27(MACRO, __VA_ARGS__) + + +#define DO_29(MACRO, ...) \ +MACRO(29, __VA_ARGS__) \ +DO_28(MACRO, __VA_ARGS__) + + +#define DO_30(MACRO, ...) \ +MACRO(30, __VA_ARGS__) \ +DO_29(MACRO, __VA_ARGS__) + + +#define DO_31(MACRO, ...) \ +MACRO(31, __VA_ARGS__) \ +DO_30(MACRO, __VA_ARGS__) + + +#define DO_32(MACRO, ...) \ +MACRO(32, __VA_ARGS__) \ +DO_31(MACRO, __VA_ARGS__) + + +#define DO_33(MACRO, ...) \ +MACRO(33, __VA_ARGS__) \ +DO_32(MACRO, __VA_ARGS__) + + +#define DO_34(MACRO, ...) \ +MACRO(34, __VA_ARGS__) \ +DO_33(MACRO, __VA_ARGS__) + + +#define DO_35(MACRO, ...) \ +MACRO(35, __VA_ARGS__) \ +DO_34(MACRO, __VA_ARGS__) + + +#define DO_36(MACRO, ...) \ +MACRO(36, __VA_ARGS__) \ +DO_35(MACRO, __VA_ARGS__) + + +#define DO_37(MACRO, ...) \ +MACRO(37, __VA_ARGS__) \ +DO_36(MACRO, __VA_ARGS__) + + +#define DO_38(MACRO, ...) \ +MACRO(38, __VA_ARGS__) \ +DO_37(MACRO, __VA_ARGS__) + + +#define DO_39(MACRO, ...) \ +MACRO(39, __VA_ARGS__) \ +DO_38(MACRO, __VA_ARGS__) + + +#define DO_40(MACRO, ...) \ +MACRO(40, __VA_ARGS__) \ +DO_39(MACRO, __VA_ARGS__) + + +#define DO_41(MACRO, ...) \ +MACRO(41, __VA_ARGS__) \ +DO_40(MACRO, __VA_ARGS__) + + +#define DO_42(MACRO, ...) \ +MACRO(42, __VA_ARGS__) \ +DO_41(MACRO, __VA_ARGS__) + + +#define DO_43(MACRO, ...) \ +MACRO(43, __VA_ARGS__) \ +DO_42(MACRO, __VA_ARGS__) + + +#define DO_44(MACRO, ...) \ +MACRO(44, __VA_ARGS__) \ +DO_43(MACRO, __VA_ARGS__) + + +#define DO_45(MACRO, ...) \ +MACRO(45, __VA_ARGS__) \ +DO_44(MACRO, __VA_ARGS__) + + +#define DO_46(MACRO, ...) \ +MACRO(46, __VA_ARGS__) \ +DO_45(MACRO, __VA_ARGS__) + + +#define DO_47(MACRO, ...) \ +MACRO(47, __VA_ARGS__) \ +DO_46(MACRO, __VA_ARGS__) + + +#define DO_48(MACRO, ...) \ +MACRO(48, __VA_ARGS__) \ +DO_47(MACRO, __VA_ARGS__) + + +#define DO_49(MACRO, ...) \ +MACRO(49, __VA_ARGS__) \ +DO_48(MACRO, __VA_ARGS__) + + +#define DO_50(MACRO, ...) \ +MACRO(50, __VA_ARGS__) \ +DO_49(MACRO, __VA_ARGS__) + + +#define DO_51(MACRO, ...) \ +MACRO(51, __VA_ARGS__) \ +DO_50(MACRO, __VA_ARGS__) + + +#define DO_52(MACRO, ...) \ +MACRO(52, __VA_ARGS__) \ +DO_51(MACRO, __VA_ARGS__) + + +#define DO_53(MACRO, ...) \ +MACRO(53, __VA_ARGS__) \ +DO_52(MACRO, __VA_ARGS__) + + +#define DO_54(MACRO, ...) \ +MACRO(54, __VA_ARGS__) \ +DO_53(MACRO, __VA_ARGS__) + + +#define DO_55(MACRO, ...) \ +MACRO(55, __VA_ARGS__) \ +DO_54(MACRO, __VA_ARGS__) + + +#define DO_56(MACRO, ...) \ +MACRO(56, __VA_ARGS__) \ +DO_55(MACRO, __VA_ARGS__) + + +#define DO_57(MACRO, ...) \ +MACRO(57, __VA_ARGS__) \ +DO_56(MACRO, __VA_ARGS__) + + +#define DO_58(MACRO, ...) \ +MACRO(58, __VA_ARGS__) \ +DO_57(MACRO, __VA_ARGS__) + + +#define DO_59(MACRO, ...) \ +MACRO(59, __VA_ARGS__) \ +DO_58(MACRO, __VA_ARGS__) + + +#define DO_60(MACRO, ...) \ +MACRO(60, __VA_ARGS__) \ +DO_59(MACRO, __VA_ARGS__) + + +#define DO_61(MACRO, ...) \ +MACRO(61, __VA_ARGS__) \ +DO_60(MACRO, __VA_ARGS__) + + +#define DO_62(MACRO, ...) \ +MACRO(62, __VA_ARGS__) \ +DO_61(MACRO, __VA_ARGS__) + + +#define DO_63(MACRO, ...) \ +MACRO(63, __VA_ARGS__) \ +DO_62(MACRO, __VA_ARGS__) + + +#define DO_64(MACRO, ...) \ +MACRO(64, __VA_ARGS__) \ +DO_63(MACRO, __VA_ARGS__) + + +#define DO_65(MACRO, ...) \ +MACRO(65, __VA_ARGS__) \ +DO_64(MACRO, __VA_ARGS__) + + +#define DO_66(MACRO, ...) \ +MACRO(66, __VA_ARGS__) \ +DO_65(MACRO, __VA_ARGS__) + + +#define DO_67(MACRO, ...) \ +MACRO(67, __VA_ARGS__) \ +DO_66(MACRO, __VA_ARGS__) + + +#define DO_68(MACRO, ...) \ +MACRO(68, __VA_ARGS__) \ +DO_67(MACRO, __VA_ARGS__) + + +#define DO_69(MACRO, ...) \ +MACRO(69, __VA_ARGS__) \ +DO_68(MACRO, __VA_ARGS__) + + +#define DO_70(MACRO, ...) \ +MACRO(70, __VA_ARGS__) \ +DO_69(MACRO, __VA_ARGS__) + + +#define DO_71(MACRO, ...) \ +MACRO(71, __VA_ARGS__) \ +DO_70(MACRO, __VA_ARGS__) + + +#define DO_72(MACRO, ...) \ +MACRO(72, __VA_ARGS__) \ +DO_71(MACRO, __VA_ARGS__) + + +#define DO_73(MACRO, ...) \ +MACRO(73, __VA_ARGS__) \ +DO_72(MACRO, __VA_ARGS__) + + +#define DO_74(MACRO, ...) \ +MACRO(74, __VA_ARGS__) \ +DO_73(MACRO, __VA_ARGS__) + + +#define DO_75(MACRO, ...) \ +MACRO(75, __VA_ARGS__) \ +DO_74(MACRO, __VA_ARGS__) + + +#define DO_76(MACRO, ...) \ +MACRO(76, __VA_ARGS__) \ +DO_75(MACRO, __VA_ARGS__) + + +#define DO_77(MACRO, ...) \ +MACRO(77, __VA_ARGS__) \ +DO_76(MACRO, __VA_ARGS__) + + +#define DO_78(MACRO, ...) \ +MACRO(78, __VA_ARGS__) \ +DO_77(MACRO, __VA_ARGS__) + + +#define DO_79(MACRO, ...) \ +MACRO(79, __VA_ARGS__) \ +DO_78(MACRO, __VA_ARGS__) + + +#define DO_80(MACRO, ...) \ +MACRO(80, __VA_ARGS__) \ +DO_79(MACRO, __VA_ARGS__) + + +#define DO_81(MACRO, ...) \ +MACRO(81, __VA_ARGS__) \ +DO_80(MACRO, __VA_ARGS__) + + +#define DO_82(MACRO, ...) \ +MACRO(82, __VA_ARGS__) \ +DO_81(MACRO, __VA_ARGS__) + + +#define DO_83(MACRO, ...) \ +MACRO(83, __VA_ARGS__) \ +DO_82(MACRO, __VA_ARGS__) + + +#define DO_84(MACRO, ...) \ +MACRO(84, __VA_ARGS__) \ +DO_83(MACRO, __VA_ARGS__) + + +#define DO_85(MACRO, ...) \ +MACRO(85, __VA_ARGS__) \ +DO_84(MACRO, __VA_ARGS__) + + +#define DO_86(MACRO, ...) \ +MACRO(86, __VA_ARGS__) \ +DO_85(MACRO, __VA_ARGS__) + + +#define DO_87(MACRO, ...) \ +MACRO(87, __VA_ARGS__) \ +DO_86(MACRO, __VA_ARGS__) + + +#define DO_88(MACRO, ...) \ +MACRO(88, __VA_ARGS__) \ +DO_87(MACRO, __VA_ARGS__) + + +#define DO_89(MACRO, ...) \ +MACRO(89, __VA_ARGS__) \ +DO_88(MACRO, __VA_ARGS__) + + +#define DO_90(MACRO, ...) \ +MACRO(90, __VA_ARGS__) \ +DO_89(MACRO, __VA_ARGS__) + + +#define DO_91(MACRO, ...) \ +MACRO(91, __VA_ARGS__) \ +DO_90(MACRO, __VA_ARGS__) + + +#define DO_92(MACRO, ...) \ +MACRO(92, __VA_ARGS__) \ +DO_91(MACRO, __VA_ARGS__) + + +#define DO_93(MACRO, ...) \ +MACRO(93, __VA_ARGS__) \ +DO_92(MACRO, __VA_ARGS__) + + +#define DO_94(MACRO, ...) \ +MACRO(94, __VA_ARGS__) \ +DO_93(MACRO, __VA_ARGS__) + + +#define DO_95(MACRO, ...) \ +MACRO(95, __VA_ARGS__) \ +DO_94(MACRO, __VA_ARGS__) + + +#define DO_96(MACRO, ...) \ +MACRO(96, __VA_ARGS__) \ +DO_95(MACRO, __VA_ARGS__) + + +#define DO_97(MACRO, ...) \ +MACRO(97, __VA_ARGS__) \ +DO_96(MACRO, __VA_ARGS__) + + +#define DO_98(MACRO, ...) \ +MACRO(98, __VA_ARGS__) \ +DO_97(MACRO, __VA_ARGS__) + + +#define DO_99(MACRO, ...) \ +MACRO(99, __VA_ARGS__) \ +DO_98(MACRO, __VA_ARGS__) + + +#define DO_100(MACRO, ...) \ +MACRO(100, __VA_ARGS__) \ +DO_99(MACRO, __VA_ARGS__) + + +#define DO_101(MACRO, ...) \ +MACRO(101, __VA_ARGS__) \ +DO_100(MACRO, __VA_ARGS__) + + +#define DO_102(MACRO, ...) \ +MACRO(102, __VA_ARGS__) \ +DO_101(MACRO, __VA_ARGS__) + + +#define DO_103(MACRO, ...) \ +MACRO(103, __VA_ARGS__) \ +DO_102(MACRO, __VA_ARGS__) + + +#define DO_104(MACRO, ...) \ +MACRO(104, __VA_ARGS__) \ +DO_103(MACRO, __VA_ARGS__) + + +#define DO_105(MACRO, ...) \ +MACRO(105, __VA_ARGS__) \ +DO_104(MACRO, __VA_ARGS__) + + +#define DO_106(MACRO, ...) \ +MACRO(106, __VA_ARGS__) \ +DO_105(MACRO, __VA_ARGS__) + + +#define DO_107(MACRO, ...) \ +MACRO(107, __VA_ARGS__) \ +DO_106(MACRO, __VA_ARGS__) + + +#define DO_108(MACRO, ...) \ +MACRO(108, __VA_ARGS__) \ +DO_107(MACRO, __VA_ARGS__) + + +#define DO_109(MACRO, ...) \ +MACRO(109, __VA_ARGS__) \ +DO_108(MACRO, __VA_ARGS__) + + +#define DO_110(MACRO, ...) \ +MACRO(110, __VA_ARGS__) \ +DO_109(MACRO, __VA_ARGS__) + + +#define DO_111(MACRO, ...) \ +MACRO(111, __VA_ARGS__) \ +DO_110(MACRO, __VA_ARGS__) + + +#define DO_112(MACRO, ...) \ +MACRO(112, __VA_ARGS__) \ +DO_111(MACRO, __VA_ARGS__) + + +#define DO_113(MACRO, ...) \ +MACRO(113, __VA_ARGS__) \ +DO_112(MACRO, __VA_ARGS__) + + +#define DO_114(MACRO, ...) \ +MACRO(114, __VA_ARGS__) \ +DO_113(MACRO, __VA_ARGS__) + + +#define DO_115(MACRO, ...) \ +MACRO(115, __VA_ARGS__) \ +DO_114(MACRO, __VA_ARGS__) + + +#define DO_116(MACRO, ...) \ +MACRO(116, __VA_ARGS__) \ +DO_115(MACRO, __VA_ARGS__) + + +#define DO_117(MACRO, ...) \ +MACRO(117, __VA_ARGS__) \ +DO_116(MACRO, __VA_ARGS__) + + +#define DO_118(MACRO, ...) \ +MACRO(118, __VA_ARGS__) \ +DO_117(MACRO, __VA_ARGS__) + + +#define DO_119(MACRO, ...) \ +MACRO(119, __VA_ARGS__) \ +DO_118(MACRO, __VA_ARGS__) + + +#define DO_120(MACRO, ...) \ +MACRO(120, __VA_ARGS__) \ +DO_119(MACRO, __VA_ARGS__) + + +#define DO_121(MACRO, ...) \ +MACRO(121, __VA_ARGS__) \ +DO_120(MACRO, __VA_ARGS__) + + +#define DO_122(MACRO, ...) \ +MACRO(122, __VA_ARGS__) \ +DO_121(MACRO, __VA_ARGS__) + + +#define DO_123(MACRO, ...) \ +MACRO(123, __VA_ARGS__) \ +DO_122(MACRO, __VA_ARGS__) + + +#define DO_124(MACRO, ...) \ +MACRO(124, __VA_ARGS__) \ +DO_123(MACRO, __VA_ARGS__) + + +#define DO_125(MACRO, ...) \ +MACRO(125, __VA_ARGS__) \ +DO_124(MACRO, __VA_ARGS__) + + +#define DO_126(MACRO, ...) \ +MACRO(126, __VA_ARGS__) \ +DO_125(MACRO, __VA_ARGS__) + + +#define DO_127(MACRO, ...) \ +MACRO(127, __VA_ARGS__) \ +DO_126(MACRO, __VA_ARGS__) + + +#define DO_128(MACRO, ...) \ +MACRO(128, __VA_ARGS__) \ +DO_127(MACRO, __VA_ARGS__) + + +#define DO_129(MACRO, ...) \ +MACRO(129, __VA_ARGS__) \ +DO_128(MACRO, __VA_ARGS__) + + +#define DO_130(MACRO, ...) \ +MACRO(130, __VA_ARGS__) \ +DO_129(MACRO, __VA_ARGS__) + + +#define DO_131(MACRO, ...) \ +MACRO(131, __VA_ARGS__) \ +DO_130(MACRO, __VA_ARGS__) + + +#define DO_132(MACRO, ...) \ +MACRO(132, __VA_ARGS__) \ +DO_131(MACRO, __VA_ARGS__) + + +#define DO_133(MACRO, ...) \ +MACRO(133, __VA_ARGS__) \ +DO_132(MACRO, __VA_ARGS__) + + +#define DO_134(MACRO, ...) \ +MACRO(134, __VA_ARGS__) \ +DO_133(MACRO, __VA_ARGS__) + + +#define DO_135(MACRO, ...) \ +MACRO(135, __VA_ARGS__) \ +DO_134(MACRO, __VA_ARGS__) + + +#define DO_136(MACRO, ...) \ +MACRO(136, __VA_ARGS__) \ +DO_135(MACRO, __VA_ARGS__) + + +#define DO_137(MACRO, ...) \ +MACRO(137, __VA_ARGS__) \ +DO_136(MACRO, __VA_ARGS__) + + +#define DO_138(MACRO, ...) \ +MACRO(138, __VA_ARGS__) \ +DO_137(MACRO, __VA_ARGS__) + + +#define DO_139(MACRO, ...) \ +MACRO(139, __VA_ARGS__) \ +DO_138(MACRO, __VA_ARGS__) + + +#define DO_140(MACRO, ...) \ +MACRO(140, __VA_ARGS__) \ +DO_139(MACRO, __VA_ARGS__) + + +#define DO_141(MACRO, ...) \ +MACRO(141, __VA_ARGS__) \ +DO_140(MACRO, __VA_ARGS__) + + +#define DO_142(MACRO, ...) \ +MACRO(142, __VA_ARGS__) \ +DO_141(MACRO, __VA_ARGS__) + + +#define DO_143(MACRO, ...) \ +MACRO(143, __VA_ARGS__) \ +DO_142(MACRO, __VA_ARGS__) + + +#define DO_144(MACRO, ...) \ +MACRO(144, __VA_ARGS__) \ +DO_143(MACRO, __VA_ARGS__) + + +#define DO_145(MACRO, ...) \ +MACRO(145, __VA_ARGS__) \ +DO_144(MACRO, __VA_ARGS__) + + +#define DO_146(MACRO, ...) \ +MACRO(146, __VA_ARGS__) \ +DO_145(MACRO, __VA_ARGS__) + + +#define DO_147(MACRO, ...) \ +MACRO(147, __VA_ARGS__) \ +DO_146(MACRO, __VA_ARGS__) + + +#define DO_148(MACRO, ...) \ +MACRO(148, __VA_ARGS__) \ +DO_147(MACRO, __VA_ARGS__) + + +#define DO_149(MACRO, ...) \ +MACRO(149, __VA_ARGS__) \ +DO_148(MACRO, __VA_ARGS__) + + +#define DO_150(MACRO, ...) \ +MACRO(150, __VA_ARGS__) \ +DO_149(MACRO, __VA_ARGS__) + + +#define DO_151(MACRO, ...) \ +MACRO(151, __VA_ARGS__) \ +DO_150(MACRO, __VA_ARGS__) + + +#define DO_152(MACRO, ...) \ +MACRO(152, __VA_ARGS__) \ +DO_151(MACRO, __VA_ARGS__) + + +#define DO_153(MACRO, ...) \ +MACRO(153, __VA_ARGS__) \ +DO_152(MACRO, __VA_ARGS__) + + +#define DO_154(MACRO, ...) \ +MACRO(154, __VA_ARGS__) \ +DO_153(MACRO, __VA_ARGS__) + + +#define DO_155(MACRO, ...) \ +MACRO(155, __VA_ARGS__) \ +DO_154(MACRO, __VA_ARGS__) + + +#define DO_156(MACRO, ...) \ +MACRO(156, __VA_ARGS__) \ +DO_155(MACRO, __VA_ARGS__) + + +#define DO_157(MACRO, ...) \ +MACRO(157, __VA_ARGS__) \ +DO_156(MACRO, __VA_ARGS__) + + +#define DO_158(MACRO, ...) \ +MACRO(158, __VA_ARGS__) \ +DO_157(MACRO, __VA_ARGS__) + + +#define DO_159(MACRO, ...) \ +MACRO(159, __VA_ARGS__) \ +DO_158(MACRO, __VA_ARGS__) + + +#define DO_160(MACRO, ...) \ +MACRO(160, __VA_ARGS__) \ +DO_159(MACRO, __VA_ARGS__) + + +#define DO_161(MACRO, ...) \ +MACRO(161, __VA_ARGS__) \ +DO_160(MACRO, __VA_ARGS__) + + +#define DO_162(MACRO, ...) \ +MACRO(162, __VA_ARGS__) \ +DO_161(MACRO, __VA_ARGS__) + + +#define DO_163(MACRO, ...) \ +MACRO(163, __VA_ARGS__) \ +DO_162(MACRO, __VA_ARGS__) + + +#define DO_164(MACRO, ...) \ +MACRO(164, __VA_ARGS__) \ +DO_163(MACRO, __VA_ARGS__) + + +#define DO_165(MACRO, ...) \ +MACRO(165, __VA_ARGS__) \ +DO_164(MACRO, __VA_ARGS__) + + +#define DO_166(MACRO, ...) \ +MACRO(166, __VA_ARGS__) \ +DO_165(MACRO, __VA_ARGS__) + + +#define DO_167(MACRO, ...) \ +MACRO(167, __VA_ARGS__) \ +DO_166(MACRO, __VA_ARGS__) + + +#define DO_168(MACRO, ...) \ +MACRO(168, __VA_ARGS__) \ +DO_167(MACRO, __VA_ARGS__) + + +#define DO_169(MACRO, ...) \ +MACRO(169, __VA_ARGS__) \ +DO_168(MACRO, __VA_ARGS__) + + +#define DO_170(MACRO, ...) \ +MACRO(170, __VA_ARGS__) \ +DO_169(MACRO, __VA_ARGS__) + + +#define DO_171(MACRO, ...) \ +MACRO(171, __VA_ARGS__) \ +DO_170(MACRO, __VA_ARGS__) + + +#define DO_172(MACRO, ...) \ +MACRO(172, __VA_ARGS__) \ +DO_171(MACRO, __VA_ARGS__) + + +#define DO_173(MACRO, ...) \ +MACRO(173, __VA_ARGS__) \ +DO_172(MACRO, __VA_ARGS__) + + +#define DO_174(MACRO, ...) \ +MACRO(174, __VA_ARGS__) \ +DO_173(MACRO, __VA_ARGS__) + + +#define DO_175(MACRO, ...) \ +MACRO(175, __VA_ARGS__) \ +DO_174(MACRO, __VA_ARGS__) + + +#define DO_176(MACRO, ...) \ +MACRO(176, __VA_ARGS__) \ +DO_175(MACRO, __VA_ARGS__) + + +#define DO_177(MACRO, ...) \ +MACRO(177, __VA_ARGS__) \ +DO_176(MACRO, __VA_ARGS__) + + +#define DO_178(MACRO, ...) \ +MACRO(178, __VA_ARGS__) \ +DO_177(MACRO, __VA_ARGS__) + + +#define DO_179(MACRO, ...) \ +MACRO(179, __VA_ARGS__) \ +DO_178(MACRO, __VA_ARGS__) + + +#define DO_180(MACRO, ...) \ +MACRO(180, __VA_ARGS__) \ +DO_179(MACRO, __VA_ARGS__) + + +#define DO_181(MACRO, ...) \ +MACRO(181, __VA_ARGS__) \ +DO_180(MACRO, __VA_ARGS__) + + +#define DO_182(MACRO, ...) \ +MACRO(182, __VA_ARGS__) \ +DO_181(MACRO, __VA_ARGS__) + + +#define DO_183(MACRO, ...) \ +MACRO(183, __VA_ARGS__) \ +DO_182(MACRO, __VA_ARGS__) + + +#define DO_184(MACRO, ...) \ +MACRO(184, __VA_ARGS__) \ +DO_183(MACRO, __VA_ARGS__) + + +#define DO_185(MACRO, ...) \ +MACRO(185, __VA_ARGS__) \ +DO_184(MACRO, __VA_ARGS__) + + +#define DO_186(MACRO, ...) \ +MACRO(186, __VA_ARGS__) \ +DO_185(MACRO, __VA_ARGS__) + + +#define DO_187(MACRO, ...) \ +MACRO(187, __VA_ARGS__) \ +DO_186(MACRO, __VA_ARGS__) + + +#define DO_188(MACRO, ...) \ +MACRO(188, __VA_ARGS__) \ +DO_187(MACRO, __VA_ARGS__) + + +#define DO_189(MACRO, ...) \ +MACRO(189, __VA_ARGS__) \ +DO_188(MACRO, __VA_ARGS__) + + +#define DO_190(MACRO, ...) \ +MACRO(190, __VA_ARGS__) \ +DO_189(MACRO, __VA_ARGS__) + + +#define DO_191(MACRO, ...) \ +MACRO(191, __VA_ARGS__) \ +DO_190(MACRO, __VA_ARGS__) + + +#define DO_192(MACRO, ...) \ +MACRO(192, __VA_ARGS__) \ +DO_191(MACRO, __VA_ARGS__) + + +#define DO_193(MACRO, ...) \ +MACRO(193, __VA_ARGS__) \ +DO_192(MACRO, __VA_ARGS__) + + +#define DO_194(MACRO, ...) \ +MACRO(194, __VA_ARGS__) \ +DO_193(MACRO, __VA_ARGS__) + + +#define DO_195(MACRO, ...) \ +MACRO(195, __VA_ARGS__) \ +DO_194(MACRO, __VA_ARGS__) + + +#define DO_196(MACRO, ...) \ +MACRO(196, __VA_ARGS__) \ +DO_195(MACRO, __VA_ARGS__) + + +#define DO_197(MACRO, ...) \ +MACRO(197, __VA_ARGS__) \ +DO_196(MACRO, __VA_ARGS__) + + +#define DO_198(MACRO, ...) \ +MACRO(198, __VA_ARGS__) \ +DO_197(MACRO, __VA_ARGS__) + + +#define DO_199(MACRO, ...) \ +MACRO(199, __VA_ARGS__) \ +DO_198(MACRO, __VA_ARGS__) + + +#define DO_200(MACRO, ...) \ +MACRO(200, __VA_ARGS__) \ +DO_199(MACRO, __VA_ARGS__) + + +#define DO_201(MACRO, ...) \ +MACRO(201, __VA_ARGS__) \ +DO_200(MACRO, __VA_ARGS__) + + +#define DO_202(MACRO, ...) \ +MACRO(202, __VA_ARGS__) \ +DO_201(MACRO, __VA_ARGS__) + + +#define DO_203(MACRO, ...) \ +MACRO(203, __VA_ARGS__) \ +DO_202(MACRO, __VA_ARGS__) + + +#define DO_204(MACRO, ...) \ +MACRO(204, __VA_ARGS__) \ +DO_203(MACRO, __VA_ARGS__) + + +#define DO_205(MACRO, ...) \ +MACRO(205, __VA_ARGS__) \ +DO_204(MACRO, __VA_ARGS__) + + +#define DO_206(MACRO, ...) \ +MACRO(206, __VA_ARGS__) \ +DO_205(MACRO, __VA_ARGS__) + + +#define DO_207(MACRO, ...) \ +MACRO(207, __VA_ARGS__) \ +DO_206(MACRO, __VA_ARGS__) + + +#define DO_208(MACRO, ...) \ +MACRO(208, __VA_ARGS__) \ +DO_207(MACRO, __VA_ARGS__) + + +#define DO_209(MACRO, ...) \ +MACRO(209, __VA_ARGS__) \ +DO_208(MACRO, __VA_ARGS__) + + +#define DO_210(MACRO, ...) \ +MACRO(210, __VA_ARGS__) \ +DO_209(MACRO, __VA_ARGS__) + + +#define DO_211(MACRO, ...) \ +MACRO(211, __VA_ARGS__) \ +DO_210(MACRO, __VA_ARGS__) + + +#define DO_212(MACRO, ...) \ +MACRO(212, __VA_ARGS__) \ +DO_211(MACRO, __VA_ARGS__) + + +#define DO_213(MACRO, ...) \ +MACRO(213, __VA_ARGS__) \ +DO_212(MACRO, __VA_ARGS__) + + +#define DO_214(MACRO, ...) \ +MACRO(214, __VA_ARGS__) \ +DO_213(MACRO, __VA_ARGS__) + + +#define DO_215(MACRO, ...) \ +MACRO(215, __VA_ARGS__) \ +DO_214(MACRO, __VA_ARGS__) + + +#define DO_216(MACRO, ...) \ +MACRO(216, __VA_ARGS__) \ +DO_215(MACRO, __VA_ARGS__) + + +#define DO_217(MACRO, ...) \ +MACRO(217, __VA_ARGS__) \ +DO_216(MACRO, __VA_ARGS__) + + +#define DO_218(MACRO, ...) \ +MACRO(218, __VA_ARGS__) \ +DO_217(MACRO, __VA_ARGS__) + + +#define DO_219(MACRO, ...) \ +MACRO(219, __VA_ARGS__) \ +DO_218(MACRO, __VA_ARGS__) + + +#define DO_220(MACRO, ...) \ +MACRO(220, __VA_ARGS__) \ +DO_219(MACRO, __VA_ARGS__) + + +#define DO_221(MACRO, ...) \ +MACRO(221, __VA_ARGS__) \ +DO_220(MACRO, __VA_ARGS__) + + +#define DO_222(MACRO, ...) \ +MACRO(222, __VA_ARGS__) \ +DO_221(MACRO, __VA_ARGS__) + + +#define DO_223(MACRO, ...) \ +MACRO(223, __VA_ARGS__) \ +DO_222(MACRO, __VA_ARGS__) + + +#define DO_224(MACRO, ...) \ +MACRO(224, __VA_ARGS__) \ +DO_223(MACRO, __VA_ARGS__) + + +#define DO_225(MACRO, ...) \ +MACRO(225, __VA_ARGS__) \ +DO_224(MACRO, __VA_ARGS__) + + +#define DO_226(MACRO, ...) \ +MACRO(226, __VA_ARGS__) \ +DO_225(MACRO, __VA_ARGS__) + + +#define DO_227(MACRO, ...) \ +MACRO(227, __VA_ARGS__) \ +DO_226(MACRO, __VA_ARGS__) + + +#define DO_228(MACRO, ...) \ +MACRO(228, __VA_ARGS__) \ +DO_227(MACRO, __VA_ARGS__) + + +#define DO_229(MACRO, ...) \ +MACRO(229, __VA_ARGS__) \ +DO_228(MACRO, __VA_ARGS__) + + +#define DO_230(MACRO, ...) \ +MACRO(230, __VA_ARGS__) \ +DO_229(MACRO, __VA_ARGS__) + + +#define DO_231(MACRO, ...) \ +MACRO(231, __VA_ARGS__) \ +DO_230(MACRO, __VA_ARGS__) + + +#define DO_232(MACRO, ...) \ +MACRO(232, __VA_ARGS__) \ +DO_231(MACRO, __VA_ARGS__) + + +#define DO_233(MACRO, ...) \ +MACRO(233, __VA_ARGS__) \ +DO_232(MACRO, __VA_ARGS__) + + +#define DO_234(MACRO, ...) \ +MACRO(234, __VA_ARGS__) \ +DO_233(MACRO, __VA_ARGS__) + + +#define DO_235(MACRO, ...) \ +MACRO(235, __VA_ARGS__) \ +DO_234(MACRO, __VA_ARGS__) + + +#define DO_236(MACRO, ...) \ +MACRO(236, __VA_ARGS__) \ +DO_235(MACRO, __VA_ARGS__) + + +#define DO_237(MACRO, ...) \ +MACRO(237, __VA_ARGS__) \ +DO_236(MACRO, __VA_ARGS__) + + +#define DO_238(MACRO, ...) \ +MACRO(238, __VA_ARGS__) \ +DO_237(MACRO, __VA_ARGS__) + + +#define DO_239(MACRO, ...) \ +MACRO(239, __VA_ARGS__) \ +DO_238(MACRO, __VA_ARGS__) + + +#define DO_240(MACRO, ...) \ +MACRO(240, __VA_ARGS__) \ +DO_239(MACRO, __VA_ARGS__) + + +#define DO_241(MACRO, ...) \ +MACRO(241, __VA_ARGS__) \ +DO_240(MACRO, __VA_ARGS__) + + +#define DO_242(MACRO, ...) \ +MACRO(242, __VA_ARGS__) \ +DO_241(MACRO, __VA_ARGS__) + + +#define DO_243(MACRO, ...) \ +MACRO(243, __VA_ARGS__) \ +DO_242(MACRO, __VA_ARGS__) + + +#define DO_244(MACRO, ...) \ +MACRO(244, __VA_ARGS__) \ +DO_243(MACRO, __VA_ARGS__) + + +#define DO_245(MACRO, ...) \ +MACRO(245, __VA_ARGS__) \ +DO_244(MACRO, __VA_ARGS__) + + +#define DO_246(MACRO, ...) \ +MACRO(246, __VA_ARGS__) \ +DO_245(MACRO, __VA_ARGS__) + + +#define DO_247(MACRO, ...) \ +MACRO(247, __VA_ARGS__) \ +DO_246(MACRO, __VA_ARGS__) + + +#define DO_248(MACRO, ...) \ +MACRO(248, __VA_ARGS__) \ +DO_247(MACRO, __VA_ARGS__) + + +#define DO_249(MACRO, ...) \ +MACRO(249, __VA_ARGS__) \ +DO_248(MACRO, __VA_ARGS__) + + +#define DO_250(MACRO, ...) \ +MACRO(250, __VA_ARGS__) \ +DO_249(MACRO, __VA_ARGS__) + + +#define DO_251(MACRO, ...) \ +MACRO(251, __VA_ARGS__) \ +DO_250(MACRO, __VA_ARGS__) + + +#define DO_252(MACRO, ...) \ +MACRO(252, __VA_ARGS__) \ +DO_251(MACRO, __VA_ARGS__) + + +#define DO_253(MACRO, ...) \ +MACRO(253, __VA_ARGS__) \ +DO_252(MACRO, __VA_ARGS__) + + +#define DO_254(MACRO, ...) \ +MACRO(254, __VA_ARGS__) \ +DO_253(MACRO, __VA_ARGS__) + + +#define DO_255(MACRO, ...) \ +MACRO(255, __VA_ARGS__) \ +DO_254(MACRO, __VA_ARGS__) + + +#define DO_256(MACRO, ...) \ +MACRO(256, __VA_ARGS__) \ +DO_255(MACRO, __VA_ARGS__) + + +#define DO_257(MACRO, ...) \ +MACRO(257, __VA_ARGS__) \ +DO_256(MACRO, __VA_ARGS__) + + +#define DO_258(MACRO, ...) \ +MACRO(258, __VA_ARGS__) \ +DO_257(MACRO, __VA_ARGS__) + + +#define DO_259(MACRO, ...) \ +MACRO(259, __VA_ARGS__) \ +DO_258(MACRO, __VA_ARGS__) + + +#define DO_260(MACRO, ...) \ +MACRO(260, __VA_ARGS__) \ +DO_259(MACRO, __VA_ARGS__) + + +#define DO_261(MACRO, ...) \ +MACRO(261, __VA_ARGS__) \ +DO_260(MACRO, __VA_ARGS__) + + +#define DO_262(MACRO, ...) \ +MACRO(262, __VA_ARGS__) \ +DO_261(MACRO, __VA_ARGS__) + + +#define DO_263(MACRO, ...) \ +MACRO(263, __VA_ARGS__) \ +DO_262(MACRO, __VA_ARGS__) + + +#define DO_264(MACRO, ...) \ +MACRO(264, __VA_ARGS__) \ +DO_263(MACRO, __VA_ARGS__) + + +#define DO_265(MACRO, ...) \ +MACRO(265, __VA_ARGS__) \ +DO_264(MACRO, __VA_ARGS__) + + +#define DO_266(MACRO, ...) \ +MACRO(266, __VA_ARGS__) \ +DO_265(MACRO, __VA_ARGS__) + + +#define DO_267(MACRO, ...) \ +MACRO(267, __VA_ARGS__) \ +DO_266(MACRO, __VA_ARGS__) + + +#define DO_268(MACRO, ...) \ +MACRO(268, __VA_ARGS__) \ +DO_267(MACRO, __VA_ARGS__) + + +#define DO_269(MACRO, ...) \ +MACRO(269, __VA_ARGS__) \ +DO_268(MACRO, __VA_ARGS__) + + +#define DO_270(MACRO, ...) \ +MACRO(270, __VA_ARGS__) \ +DO_269(MACRO, __VA_ARGS__) + + +#define DO_271(MACRO, ...) \ +MACRO(271, __VA_ARGS__) \ +DO_270(MACRO, __VA_ARGS__) + + +#define DO_272(MACRO, ...) \ +MACRO(272, __VA_ARGS__) \ +DO_271(MACRO, __VA_ARGS__) + + +#define DO_273(MACRO, ...) \ +MACRO(273, __VA_ARGS__) \ +DO_272(MACRO, __VA_ARGS__) + + +#define DO_274(MACRO, ...) \ +MACRO(274, __VA_ARGS__) \ +DO_273(MACRO, __VA_ARGS__) + + +#define DO_275(MACRO, ...) \ +MACRO(275, __VA_ARGS__) \ +DO_274(MACRO, __VA_ARGS__) + + +#define DO_276(MACRO, ...) \ +MACRO(276, __VA_ARGS__) \ +DO_275(MACRO, __VA_ARGS__) + + +#define DO_277(MACRO, ...) \ +MACRO(277, __VA_ARGS__) \ +DO_276(MACRO, __VA_ARGS__) + + +#define DO_278(MACRO, ...) \ +MACRO(278, __VA_ARGS__) \ +DO_277(MACRO, __VA_ARGS__) + + +#define DO_279(MACRO, ...) \ +MACRO(279, __VA_ARGS__) \ +DO_278(MACRO, __VA_ARGS__) + + +#define DO_280(MACRO, ...) \ +MACRO(280, __VA_ARGS__) \ +DO_279(MACRO, __VA_ARGS__) + + +#define DO_281(MACRO, ...) \ +MACRO(281, __VA_ARGS__) \ +DO_280(MACRO, __VA_ARGS__) + + +#define DO_282(MACRO, ...) \ +MACRO(282, __VA_ARGS__) \ +DO_281(MACRO, __VA_ARGS__) + + +#define DO_283(MACRO, ...) \ +MACRO(283, __VA_ARGS__) \ +DO_282(MACRO, __VA_ARGS__) + + +#define DO_284(MACRO, ...) \ +MACRO(284, __VA_ARGS__) \ +DO_283(MACRO, __VA_ARGS__) + + +#define DO_285(MACRO, ...) \ +MACRO(285, __VA_ARGS__) \ +DO_284(MACRO, __VA_ARGS__) + + +#define DO_286(MACRO, ...) \ +MACRO(286, __VA_ARGS__) \ +DO_285(MACRO, __VA_ARGS__) + + +#define DO_287(MACRO, ...) \ +MACRO(287, __VA_ARGS__) \ +DO_286(MACRO, __VA_ARGS__) + + +#define DO_288(MACRO, ...) \ +MACRO(288, __VA_ARGS__) \ +DO_287(MACRO, __VA_ARGS__) + + +#define DO_289(MACRO, ...) \ +MACRO(289, __VA_ARGS__) \ +DO_288(MACRO, __VA_ARGS__) + + +#define DO_290(MACRO, ...) \ +MACRO(290, __VA_ARGS__) \ +DO_289(MACRO, __VA_ARGS__) + + +#define DO_291(MACRO, ...) \ +MACRO(291, __VA_ARGS__) \ +DO_290(MACRO, __VA_ARGS__) + + +#define DO_292(MACRO, ...) \ +MACRO(292, __VA_ARGS__) \ +DO_291(MACRO, __VA_ARGS__) + + +#define DO_293(MACRO, ...) \ +MACRO(293, __VA_ARGS__) \ +DO_292(MACRO, __VA_ARGS__) + + +#define DO_294(MACRO, ...) \ +MACRO(294, __VA_ARGS__) \ +DO_293(MACRO, __VA_ARGS__) + + +#define DO_295(MACRO, ...) \ +MACRO(295, __VA_ARGS__) \ +DO_294(MACRO, __VA_ARGS__) + + +#define DO_296(MACRO, ...) \ +MACRO(296, __VA_ARGS__) \ +DO_295(MACRO, __VA_ARGS__) + + +#define DO_297(MACRO, ...) \ +MACRO(297, __VA_ARGS__) \ +DO_296(MACRO, __VA_ARGS__) + + +#define DO_298(MACRO, ...) \ +MACRO(298, __VA_ARGS__) \ +DO_297(MACRO, __VA_ARGS__) + + +#define DO_299(MACRO, ...) \ +MACRO(299, __VA_ARGS__) \ +DO_298(MACRO, __VA_ARGS__) + + +#define DO_300(MACRO, ...) \ +MACRO(300, __VA_ARGS__) \ +DO_299(MACRO, __VA_ARGS__) + + +#define DO_301(MACRO, ...) \ +MACRO(301, __VA_ARGS__) \ +DO_300(MACRO, __VA_ARGS__) + + +#define DO_302(MACRO, ...) \ +MACRO(302, __VA_ARGS__) \ +DO_301(MACRO, __VA_ARGS__) + + +#define DO_303(MACRO, ...) \ +MACRO(303, __VA_ARGS__) \ +DO_302(MACRO, __VA_ARGS__) + + +#define DO_304(MACRO, ...) \ +MACRO(304, __VA_ARGS__) \ +DO_303(MACRO, __VA_ARGS__) + + +#define DO_305(MACRO, ...) \ +MACRO(305, __VA_ARGS__) \ +DO_304(MACRO, __VA_ARGS__) + + +#define DO_306(MACRO, ...) \ +MACRO(306, __VA_ARGS__) \ +DO_305(MACRO, __VA_ARGS__) + + +#define DO_307(MACRO, ...) \ +MACRO(307, __VA_ARGS__) \ +DO_306(MACRO, __VA_ARGS__) + + +#define DO_308(MACRO, ...) \ +MACRO(308, __VA_ARGS__) \ +DO_307(MACRO, __VA_ARGS__) + + +#define DO_309(MACRO, ...) \ +MACRO(309, __VA_ARGS__) \ +DO_308(MACRO, __VA_ARGS__) + + +#define DO_310(MACRO, ...) \ +MACRO(310, __VA_ARGS__) \ +DO_309(MACRO, __VA_ARGS__) + + +#define DO_311(MACRO, ...) \ +MACRO(311, __VA_ARGS__) \ +DO_310(MACRO, __VA_ARGS__) + + +#define DO_312(MACRO, ...) \ +MACRO(312, __VA_ARGS__) \ +DO_311(MACRO, __VA_ARGS__) + + +#define DO_313(MACRO, ...) \ +MACRO(313, __VA_ARGS__) \ +DO_312(MACRO, __VA_ARGS__) + + +#define DO_314(MACRO, ...) \ +MACRO(314, __VA_ARGS__) \ +DO_313(MACRO, __VA_ARGS__) + + +#define DO_315(MACRO, ...) \ +MACRO(315, __VA_ARGS__) \ +DO_314(MACRO, __VA_ARGS__) + + +#define DO_316(MACRO, ...) \ +MACRO(316, __VA_ARGS__) \ +DO_315(MACRO, __VA_ARGS__) + + +#define DO_317(MACRO, ...) \ +MACRO(317, __VA_ARGS__) \ +DO_316(MACRO, __VA_ARGS__) + + +#define DO_318(MACRO, ...) \ +MACRO(318, __VA_ARGS__) \ +DO_317(MACRO, __VA_ARGS__) + + +#define DO_319(MACRO, ...) \ +MACRO(319, __VA_ARGS__) \ +DO_318(MACRO, __VA_ARGS__) + + +#define DO_320(MACRO, ...) \ +MACRO(320, __VA_ARGS__) \ +DO_319(MACRO, __VA_ARGS__) + + +#define DO_321(MACRO, ...) \ +MACRO(321, __VA_ARGS__) \ +DO_320(MACRO, __VA_ARGS__) + + +#define DO_322(MACRO, ...) \ +MACRO(322, __VA_ARGS__) \ +DO_321(MACRO, __VA_ARGS__) + + +#define DO_323(MACRO, ...) \ +MACRO(323, __VA_ARGS__) \ +DO_322(MACRO, __VA_ARGS__) + + +#define DO_324(MACRO, ...) \ +MACRO(324, __VA_ARGS__) \ +DO_323(MACRO, __VA_ARGS__) + + +#define DO_325(MACRO, ...) \ +MACRO(325, __VA_ARGS__) \ +DO_324(MACRO, __VA_ARGS__) + + +#define DO_326(MACRO, ...) \ +MACRO(326, __VA_ARGS__) \ +DO_325(MACRO, __VA_ARGS__) + + +#define DO_327(MACRO, ...) \ +MACRO(327, __VA_ARGS__) \ +DO_326(MACRO, __VA_ARGS__) + + +#define DO_328(MACRO, ...) \ +MACRO(328, __VA_ARGS__) \ +DO_327(MACRO, __VA_ARGS__) + + +#define DO_329(MACRO, ...) \ +MACRO(329, __VA_ARGS__) \ +DO_328(MACRO, __VA_ARGS__) + + +#define DO_330(MACRO, ...) \ +MACRO(330, __VA_ARGS__) \ +DO_329(MACRO, __VA_ARGS__) + + +#define DO_331(MACRO, ...) \ +MACRO(331, __VA_ARGS__) \ +DO_330(MACRO, __VA_ARGS__) + + +#define DO_332(MACRO, ...) \ +MACRO(332, __VA_ARGS__) \ +DO_331(MACRO, __VA_ARGS__) + + +#define DO_333(MACRO, ...) \ +MACRO(333, __VA_ARGS__) \ +DO_332(MACRO, __VA_ARGS__) + + +#define DO_334(MACRO, ...) \ +MACRO(334, __VA_ARGS__) \ +DO_333(MACRO, __VA_ARGS__) + + +#define DO_335(MACRO, ...) \ +MACRO(335, __VA_ARGS__) \ +DO_334(MACRO, __VA_ARGS__) + + +#define DO_336(MACRO, ...) \ +MACRO(336, __VA_ARGS__) \ +DO_335(MACRO, __VA_ARGS__) + + +#define DO_337(MACRO, ...) \ +MACRO(337, __VA_ARGS__) \ +DO_336(MACRO, __VA_ARGS__) + + +#define DO_338(MACRO, ...) \ +MACRO(338, __VA_ARGS__) \ +DO_337(MACRO, __VA_ARGS__) + + +#define DO_339(MACRO, ...) \ +MACRO(339, __VA_ARGS__) \ +DO_338(MACRO, __VA_ARGS__) + + +#define DO_340(MACRO, ...) \ +MACRO(340, __VA_ARGS__) \ +DO_339(MACRO, __VA_ARGS__) + + +#define DO_341(MACRO, ...) \ +MACRO(341, __VA_ARGS__) \ +DO_340(MACRO, __VA_ARGS__) + + +#define DO_342(MACRO, ...) \ +MACRO(342, __VA_ARGS__) \ +DO_341(MACRO, __VA_ARGS__) + + +#define DO_343(MACRO, ...) \ +MACRO(343, __VA_ARGS__) \ +DO_342(MACRO, __VA_ARGS__) + + +#define DO_344(MACRO, ...) \ +MACRO(344, __VA_ARGS__) \ +DO_343(MACRO, __VA_ARGS__) + + +#define DO_345(MACRO, ...) \ +MACRO(345, __VA_ARGS__) \ +DO_344(MACRO, __VA_ARGS__) + + +#define DO_346(MACRO, ...) \ +MACRO(346, __VA_ARGS__) \ +DO_345(MACRO, __VA_ARGS__) + + +#define DO_347(MACRO, ...) \ +MACRO(347, __VA_ARGS__) \ +DO_346(MACRO, __VA_ARGS__) + + +#define DO_348(MACRO, ...) \ +MACRO(348, __VA_ARGS__) \ +DO_347(MACRO, __VA_ARGS__) + + +#define DO_349(MACRO, ...) \ +MACRO(349, __VA_ARGS__) \ +DO_348(MACRO, __VA_ARGS__) + + +#define DO_350(MACRO, ...) \ +MACRO(350, __VA_ARGS__) \ +DO_349(MACRO, __VA_ARGS__) + + +#define DO_351(MACRO, ...) \ +MACRO(351, __VA_ARGS__) \ +DO_350(MACRO, __VA_ARGS__) + + +#define DO_352(MACRO, ...) \ +MACRO(352, __VA_ARGS__) \ +DO_351(MACRO, __VA_ARGS__) + + +#define DO_353(MACRO, ...) \ +MACRO(353, __VA_ARGS__) \ +DO_352(MACRO, __VA_ARGS__) + + +#define DO_354(MACRO, ...) \ +MACRO(354, __VA_ARGS__) \ +DO_353(MACRO, __VA_ARGS__) + + +#define DO_355(MACRO, ...) \ +MACRO(355, __VA_ARGS__) \ +DO_354(MACRO, __VA_ARGS__) + + +#define DO_356(MACRO, ...) \ +MACRO(356, __VA_ARGS__) \ +DO_355(MACRO, __VA_ARGS__) + + +#define DO_357(MACRO, ...) \ +MACRO(357, __VA_ARGS__) \ +DO_356(MACRO, __VA_ARGS__) + + +#define DO_358(MACRO, ...) \ +MACRO(358, __VA_ARGS__) \ +DO_357(MACRO, __VA_ARGS__) + + +#define DO_359(MACRO, ...) \ +MACRO(359, __VA_ARGS__) \ +DO_358(MACRO, __VA_ARGS__) + + +#define DO_360(MACRO, ...) \ +MACRO(360, __VA_ARGS__) \ +DO_359(MACRO, __VA_ARGS__) + + +#define DO_361(MACRO, ...) \ +MACRO(361, __VA_ARGS__) \ +DO_360(MACRO, __VA_ARGS__) + + +#define DO_362(MACRO, ...) \ +MACRO(362, __VA_ARGS__) \ +DO_361(MACRO, __VA_ARGS__) + + +#define DO_363(MACRO, ...) \ +MACRO(363, __VA_ARGS__) \ +DO_362(MACRO, __VA_ARGS__) + + +#define DO_364(MACRO, ...) \ +MACRO(364, __VA_ARGS__) \ +DO_363(MACRO, __VA_ARGS__) + + +#define DO_365(MACRO, ...) \ +MACRO(365, __VA_ARGS__) \ +DO_364(MACRO, __VA_ARGS__) + + +#define DO_366(MACRO, ...) \ +MACRO(366, __VA_ARGS__) \ +DO_365(MACRO, __VA_ARGS__) + + +#define DO_367(MACRO, ...) \ +MACRO(367, __VA_ARGS__) \ +DO_366(MACRO, __VA_ARGS__) + + +#define DO_368(MACRO, ...) \ +MACRO(368, __VA_ARGS__) \ +DO_367(MACRO, __VA_ARGS__) + + +#define DO_369(MACRO, ...) \ +MACRO(369, __VA_ARGS__) \ +DO_368(MACRO, __VA_ARGS__) + + +#define DO_370(MACRO, ...) \ +MACRO(370, __VA_ARGS__) \ +DO_369(MACRO, __VA_ARGS__) + + +#define DO_371(MACRO, ...) \ +MACRO(371, __VA_ARGS__) \ +DO_370(MACRO, __VA_ARGS__) + + +#define DO_372(MACRO, ...) \ +MACRO(372, __VA_ARGS__) \ +DO_371(MACRO, __VA_ARGS__) + + +#define DO_373(MACRO, ...) \ +MACRO(373, __VA_ARGS__) \ +DO_372(MACRO, __VA_ARGS__) + + +#define DO_374(MACRO, ...) \ +MACRO(374, __VA_ARGS__) \ +DO_373(MACRO, __VA_ARGS__) + + +#define DO_375(MACRO, ...) \ +MACRO(375, __VA_ARGS__) \ +DO_374(MACRO, __VA_ARGS__) + + +#define DO_376(MACRO, ...) \ +MACRO(376, __VA_ARGS__) \ +DO_375(MACRO, __VA_ARGS__) + + +#define DO_377(MACRO, ...) \ +MACRO(377, __VA_ARGS__) \ +DO_376(MACRO, __VA_ARGS__) + + +#define DO_378(MACRO, ...) \ +MACRO(378, __VA_ARGS__) \ +DO_377(MACRO, __VA_ARGS__) + + +#define DO_379(MACRO, ...) \ +MACRO(379, __VA_ARGS__) \ +DO_378(MACRO, __VA_ARGS__) + + +#define DO_380(MACRO, ...) \ +MACRO(380, __VA_ARGS__) \ +DO_379(MACRO, __VA_ARGS__) + + +#define DO_381(MACRO, ...) \ +MACRO(381, __VA_ARGS__) \ +DO_380(MACRO, __VA_ARGS__) + + +#define DO_382(MACRO, ...) \ +MACRO(382, __VA_ARGS__) \ +DO_381(MACRO, __VA_ARGS__) + + +#define DO_383(MACRO, ...) \ +MACRO(383, __VA_ARGS__) \ +DO_382(MACRO, __VA_ARGS__) + + +#define DO_384(MACRO, ...) \ +MACRO(384, __VA_ARGS__) \ +DO_383(MACRO, __VA_ARGS__) + + +#define DO_385(MACRO, ...) \ +MACRO(385, __VA_ARGS__) \ +DO_384(MACRO, __VA_ARGS__) + + +#define DO_386(MACRO, ...) \ +MACRO(386, __VA_ARGS__) \ +DO_385(MACRO, __VA_ARGS__) + + +#define DO_387(MACRO, ...) \ +MACRO(387, __VA_ARGS__) \ +DO_386(MACRO, __VA_ARGS__) + + +#define DO_388(MACRO, ...) \ +MACRO(388, __VA_ARGS__) \ +DO_387(MACRO, __VA_ARGS__) + + +#define DO_389(MACRO, ...) \ +MACRO(389, __VA_ARGS__) \ +DO_388(MACRO, __VA_ARGS__) + + +#define DO_390(MACRO, ...) \ +MACRO(390, __VA_ARGS__) \ +DO_389(MACRO, __VA_ARGS__) + + +#define DO_391(MACRO, ...) \ +MACRO(391, __VA_ARGS__) \ +DO_390(MACRO, __VA_ARGS__) + + +#define DO_392(MACRO, ...) \ +MACRO(392, __VA_ARGS__) \ +DO_391(MACRO, __VA_ARGS__) + + +#define DO_393(MACRO, ...) \ +MACRO(393, __VA_ARGS__) \ +DO_392(MACRO, __VA_ARGS__) + + +#define DO_394(MACRO, ...) \ +MACRO(394, __VA_ARGS__) \ +DO_393(MACRO, __VA_ARGS__) + + +#define DO_395(MACRO, ...) \ +MACRO(395, __VA_ARGS__) \ +DO_394(MACRO, __VA_ARGS__) + + +#define DO_396(MACRO, ...) \ +MACRO(396, __VA_ARGS__) \ +DO_395(MACRO, __VA_ARGS__) + + +#define DO_397(MACRO, ...) \ +MACRO(397, __VA_ARGS__) \ +DO_396(MACRO, __VA_ARGS__) + + +#define DO_398(MACRO, ...) \ +MACRO(398, __VA_ARGS__) \ +DO_397(MACRO, __VA_ARGS__) + + +#define DO_399(MACRO, ...) \ +MACRO(399, __VA_ARGS__) \ +DO_398(MACRO, __VA_ARGS__) + + +#define DO_400(MACRO, ...) \ +MACRO(400, __VA_ARGS__) \ +DO_399(MACRO, __VA_ARGS__) + + +#define DO_401(MACRO, ...) \ +MACRO(401, __VA_ARGS__) \ +DO_400(MACRO, __VA_ARGS__) + + +#define DO_402(MACRO, ...) \ +MACRO(402, __VA_ARGS__) \ +DO_401(MACRO, __VA_ARGS__) + + +#define DO_403(MACRO, ...) \ +MACRO(403, __VA_ARGS__) \ +DO_402(MACRO, __VA_ARGS__) + + +#define DO_404(MACRO, ...) \ +MACRO(404, __VA_ARGS__) \ +DO_403(MACRO, __VA_ARGS__) + + +#define DO_405(MACRO, ...) \ +MACRO(405, __VA_ARGS__) \ +DO_404(MACRO, __VA_ARGS__) + + +#define DO_406(MACRO, ...) \ +MACRO(406, __VA_ARGS__) \ +DO_405(MACRO, __VA_ARGS__) + + +#define DO_407(MACRO, ...) \ +MACRO(407, __VA_ARGS__) \ +DO_406(MACRO, __VA_ARGS__) + + +#define DO_408(MACRO, ...) \ +MACRO(408, __VA_ARGS__) \ +DO_407(MACRO, __VA_ARGS__) + + +#define DO_409(MACRO, ...) \ +MACRO(409, __VA_ARGS__) \ +DO_408(MACRO, __VA_ARGS__) + + +#define DO_410(MACRO, ...) \ +MACRO(410, __VA_ARGS__) \ +DO_409(MACRO, __VA_ARGS__) + + +#define DO_411(MACRO, ...) \ +MACRO(411, __VA_ARGS__) \ +DO_410(MACRO, __VA_ARGS__) + + +#define DO_412(MACRO, ...) \ +MACRO(412, __VA_ARGS__) \ +DO_411(MACRO, __VA_ARGS__) + + +#define DO_413(MACRO, ...) \ +MACRO(413, __VA_ARGS__) \ +DO_412(MACRO, __VA_ARGS__) + + +#define DO_414(MACRO, ...) \ +MACRO(414, __VA_ARGS__) \ +DO_413(MACRO, __VA_ARGS__) + + +#define DO_415(MACRO, ...) \ +MACRO(415, __VA_ARGS__) \ +DO_414(MACRO, __VA_ARGS__) + + +#define DO_416(MACRO, ...) \ +MACRO(416, __VA_ARGS__) \ +DO_415(MACRO, __VA_ARGS__) + + +#define DO_417(MACRO, ...) \ +MACRO(417, __VA_ARGS__) \ +DO_416(MACRO, __VA_ARGS__) + + +#define DO_418(MACRO, ...) \ +MACRO(418, __VA_ARGS__) \ +DO_417(MACRO, __VA_ARGS__) + + +#define DO_419(MACRO, ...) \ +MACRO(419, __VA_ARGS__) \ +DO_418(MACRO, __VA_ARGS__) + + +#define DO_420(MACRO, ...) \ +MACRO(420, __VA_ARGS__) \ +DO_419(MACRO, __VA_ARGS__) + + +#define DO_421(MACRO, ...) \ +MACRO(421, __VA_ARGS__) \ +DO_420(MACRO, __VA_ARGS__) + + +#define DO_422(MACRO, ...) \ +MACRO(422, __VA_ARGS__) \ +DO_421(MACRO, __VA_ARGS__) + + +#define DO_423(MACRO, ...) \ +MACRO(423, __VA_ARGS__) \ +DO_422(MACRO, __VA_ARGS__) + + +#define DO_424(MACRO, ...) \ +MACRO(424, __VA_ARGS__) \ +DO_423(MACRO, __VA_ARGS__) + + +#define DO_425(MACRO, ...) \ +MACRO(425, __VA_ARGS__) \ +DO_424(MACRO, __VA_ARGS__) + + +#define DO_426(MACRO, ...) \ +MACRO(426, __VA_ARGS__) \ +DO_425(MACRO, __VA_ARGS__) + + +#define DO_427(MACRO, ...) \ +MACRO(427, __VA_ARGS__) \ +DO_426(MACRO, __VA_ARGS__) + + +#define DO_428(MACRO, ...) \ +MACRO(428, __VA_ARGS__) \ +DO_427(MACRO, __VA_ARGS__) + + +#define DO_429(MACRO, ...) \ +MACRO(429, __VA_ARGS__) \ +DO_428(MACRO, __VA_ARGS__) + + +#define DO_430(MACRO, ...) \ +MACRO(430, __VA_ARGS__) \ +DO_429(MACRO, __VA_ARGS__) + + +#define DO_431(MACRO, ...) \ +MACRO(431, __VA_ARGS__) \ +DO_430(MACRO, __VA_ARGS__) + + +#define DO_432(MACRO, ...) \ +MACRO(432, __VA_ARGS__) \ +DO_431(MACRO, __VA_ARGS__) + + +#define DO_433(MACRO, ...) \ +MACRO(433, __VA_ARGS__) \ +DO_432(MACRO, __VA_ARGS__) + + +#define DO_434(MACRO, ...) \ +MACRO(434, __VA_ARGS__) \ +DO_433(MACRO, __VA_ARGS__) + + +#define DO_435(MACRO, ...) \ +MACRO(435, __VA_ARGS__) \ +DO_434(MACRO, __VA_ARGS__) + + +#define DO_436(MACRO, ...) \ +MACRO(436, __VA_ARGS__) \ +DO_435(MACRO, __VA_ARGS__) + + +#define DO_437(MACRO, ...) \ +MACRO(437, __VA_ARGS__) \ +DO_436(MACRO, __VA_ARGS__) + + +#define DO_438(MACRO, ...) \ +MACRO(438, __VA_ARGS__) \ +DO_437(MACRO, __VA_ARGS__) + + +#define DO_439(MACRO, ...) \ +MACRO(439, __VA_ARGS__) \ +DO_438(MACRO, __VA_ARGS__) + + +#define DO_440(MACRO, ...) \ +MACRO(440, __VA_ARGS__) \ +DO_439(MACRO, __VA_ARGS__) + + +#define DO_441(MACRO, ...) \ +MACRO(441, __VA_ARGS__) \ +DO_440(MACRO, __VA_ARGS__) + + +#define DO_442(MACRO, ...) \ +MACRO(442, __VA_ARGS__) \ +DO_441(MACRO, __VA_ARGS__) + + +#define DO_443(MACRO, ...) \ +MACRO(443, __VA_ARGS__) \ +DO_442(MACRO, __VA_ARGS__) + + +#define DO_444(MACRO, ...) \ +MACRO(444, __VA_ARGS__) \ +DO_443(MACRO, __VA_ARGS__) + + +#define DO_445(MACRO, ...) \ +MACRO(445, __VA_ARGS__) \ +DO_444(MACRO, __VA_ARGS__) + + +#define DO_446(MACRO, ...) \ +MACRO(446, __VA_ARGS__) \ +DO_445(MACRO, __VA_ARGS__) + + +#define DO_447(MACRO, ...) \ +MACRO(447, __VA_ARGS__) \ +DO_446(MACRO, __VA_ARGS__) + + +#define DO_448(MACRO, ...) \ +MACRO(448, __VA_ARGS__) \ +DO_447(MACRO, __VA_ARGS__) + + +#define DO_449(MACRO, ...) \ +MACRO(449, __VA_ARGS__) \ +DO_448(MACRO, __VA_ARGS__) + + +#define DO_450(MACRO, ...) \ +MACRO(450, __VA_ARGS__) \ +DO_449(MACRO, __VA_ARGS__) + + +#define DO_451(MACRO, ...) \ +MACRO(451, __VA_ARGS__) \ +DO_450(MACRO, __VA_ARGS__) + + +#define DO_452(MACRO, ...) \ +MACRO(452, __VA_ARGS__) \ +DO_451(MACRO, __VA_ARGS__) + + +#define DO_453(MACRO, ...) \ +MACRO(453, __VA_ARGS__) \ +DO_452(MACRO, __VA_ARGS__) + + +#define DO_454(MACRO, ...) \ +MACRO(454, __VA_ARGS__) \ +DO_453(MACRO, __VA_ARGS__) + + +#define DO_455(MACRO, ...) \ +MACRO(455, __VA_ARGS__) \ +DO_454(MACRO, __VA_ARGS__) + + +#define DO_456(MACRO, ...) \ +MACRO(456, __VA_ARGS__) \ +DO_455(MACRO, __VA_ARGS__) + + +#define DO_457(MACRO, ...) \ +MACRO(457, __VA_ARGS__) \ +DO_456(MACRO, __VA_ARGS__) + + +#define DO_458(MACRO, ...) \ +MACRO(458, __VA_ARGS__) \ +DO_457(MACRO, __VA_ARGS__) + + +#define DO_459(MACRO, ...) \ +MACRO(459, __VA_ARGS__) \ +DO_458(MACRO, __VA_ARGS__) + + +#define DO_460(MACRO, ...) \ +MACRO(460, __VA_ARGS__) \ +DO_459(MACRO, __VA_ARGS__) + + +#define DO_461(MACRO, ...) \ +MACRO(461, __VA_ARGS__) \ +DO_460(MACRO, __VA_ARGS__) + + +#define DO_462(MACRO, ...) \ +MACRO(462, __VA_ARGS__) \ +DO_461(MACRO, __VA_ARGS__) + + +#define DO_463(MACRO, ...) \ +MACRO(463, __VA_ARGS__) \ +DO_462(MACRO, __VA_ARGS__) + + +#define DO_464(MACRO, ...) \ +MACRO(464, __VA_ARGS__) \ +DO_463(MACRO, __VA_ARGS__) + + +#define DO_465(MACRO, ...) \ +MACRO(465, __VA_ARGS__) \ +DO_464(MACRO, __VA_ARGS__) + + +#define DO_466(MACRO, ...) \ +MACRO(466, __VA_ARGS__) \ +DO_465(MACRO, __VA_ARGS__) + + +#define DO_467(MACRO, ...) \ +MACRO(467, __VA_ARGS__) \ +DO_466(MACRO, __VA_ARGS__) + + +#define DO_468(MACRO, ...) \ +MACRO(468, __VA_ARGS__) \ +DO_467(MACRO, __VA_ARGS__) + + +#define DO_469(MACRO, ...) \ +MACRO(469, __VA_ARGS__) \ +DO_468(MACRO, __VA_ARGS__) + + +#define DO_470(MACRO, ...) \ +MACRO(470, __VA_ARGS__) \ +DO_469(MACRO, __VA_ARGS__) + + +#define DO_471(MACRO, ...) \ +MACRO(471, __VA_ARGS__) \ +DO_470(MACRO, __VA_ARGS__) + + +#define DO_472(MACRO, ...) \ +MACRO(472, __VA_ARGS__) \ +DO_471(MACRO, __VA_ARGS__) + + +#define DO_473(MACRO, ...) \ +MACRO(473, __VA_ARGS__) \ +DO_472(MACRO, __VA_ARGS__) + + +#define DO_474(MACRO, ...) \ +MACRO(474, __VA_ARGS__) \ +DO_473(MACRO, __VA_ARGS__) + + +#define DO_475(MACRO, ...) \ +MACRO(475, __VA_ARGS__) \ +DO_474(MACRO, __VA_ARGS__) + + +#define DO_476(MACRO, ...) \ +MACRO(476, __VA_ARGS__) \ +DO_475(MACRO, __VA_ARGS__) + + +#define DO_477(MACRO, ...) \ +MACRO(477, __VA_ARGS__) \ +DO_476(MACRO, __VA_ARGS__) + + +#define DO_478(MACRO, ...) \ +MACRO(478, __VA_ARGS__) \ +DO_477(MACRO, __VA_ARGS__) + + +#define DO_479(MACRO, ...) \ +MACRO(479, __VA_ARGS__) \ +DO_478(MACRO, __VA_ARGS__) + + +#define DO_480(MACRO, ...) \ +MACRO(480, __VA_ARGS__) \ +DO_479(MACRO, __VA_ARGS__) + + +#define DO_481(MACRO, ...) \ +MACRO(481, __VA_ARGS__) \ +DO_480(MACRO, __VA_ARGS__) + + +#define DO_482(MACRO, ...) \ +MACRO(482, __VA_ARGS__) \ +DO_481(MACRO, __VA_ARGS__) + + +#define DO_483(MACRO, ...) \ +MACRO(483, __VA_ARGS__) \ +DO_482(MACRO, __VA_ARGS__) + + +#define DO_484(MACRO, ...) \ +MACRO(484, __VA_ARGS__) \ +DO_483(MACRO, __VA_ARGS__) + + +#define DO_485(MACRO, ...) \ +MACRO(485, __VA_ARGS__) \ +DO_484(MACRO, __VA_ARGS__) + + +#define DO_486(MACRO, ...) \ +MACRO(486, __VA_ARGS__) \ +DO_485(MACRO, __VA_ARGS__) + + +#define DO_487(MACRO, ...) \ +MACRO(487, __VA_ARGS__) \ +DO_486(MACRO, __VA_ARGS__) + + +#define DO_488(MACRO, ...) \ +MACRO(488, __VA_ARGS__) \ +DO_487(MACRO, __VA_ARGS__) + + +#define DO_489(MACRO, ...) \ +MACRO(489, __VA_ARGS__) \ +DO_488(MACRO, __VA_ARGS__) + + +#define DO_490(MACRO, ...) \ +MACRO(490, __VA_ARGS__) \ +DO_489(MACRO, __VA_ARGS__) + + +#define DO_491(MACRO, ...) \ +MACRO(491, __VA_ARGS__) \ +DO_490(MACRO, __VA_ARGS__) + + +#define DO_492(MACRO, ...) \ +MACRO(492, __VA_ARGS__) \ +DO_491(MACRO, __VA_ARGS__) + + +#define DO_493(MACRO, ...) \ +MACRO(493, __VA_ARGS__) \ +DO_492(MACRO, __VA_ARGS__) + + +#define DO_494(MACRO, ...) \ +MACRO(494, __VA_ARGS__) \ +DO_493(MACRO, __VA_ARGS__) + + +#define DO_495(MACRO, ...) \ +MACRO(495, __VA_ARGS__) \ +DO_494(MACRO, __VA_ARGS__) + + +#define DO_496(MACRO, ...) \ +MACRO(496, __VA_ARGS__) \ +DO_495(MACRO, __VA_ARGS__) + + +#define DO_497(MACRO, ...) \ +MACRO(497, __VA_ARGS__) \ +DO_496(MACRO, __VA_ARGS__) + + +#define DO_498(MACRO, ...) \ +MACRO(498, __VA_ARGS__) \ +DO_497(MACRO, __VA_ARGS__) + + +#define DO_499(MACRO, ...) \ +MACRO(499, __VA_ARGS__) \ +DO_498(MACRO, __VA_ARGS__) + + +#define DO_500(MACRO, ...) \ +MACRO(500, __VA_ARGS__) \ +DO_499(MACRO, __VA_ARGS__) + + +#define DO_501(MACRO, ...) \ +MACRO(501, __VA_ARGS__) \ +DO_500(MACRO, __VA_ARGS__) + + +#define DO_502(MACRO, ...) \ +MACRO(502, __VA_ARGS__) \ +DO_501(MACRO, __VA_ARGS__) + + +#define DO_503(MACRO, ...) \ +MACRO(503, __VA_ARGS__) \ +DO_502(MACRO, __VA_ARGS__) + + +#define DO_504(MACRO, ...) \ +MACRO(504, __VA_ARGS__) \ +DO_503(MACRO, __VA_ARGS__) + + +#define DO_505(MACRO, ...) \ +MACRO(505, __VA_ARGS__) \ +DO_504(MACRO, __VA_ARGS__) + + +#define DO_506(MACRO, ...) \ +MACRO(506, __VA_ARGS__) \ +DO_505(MACRO, __VA_ARGS__) + + +#define DO_507(MACRO, ...) \ +MACRO(507, __VA_ARGS__) \ +DO_506(MACRO, __VA_ARGS__) + + +#define DO_508(MACRO, ...) \ +MACRO(508, __VA_ARGS__) \ +DO_507(MACRO, __VA_ARGS__) + + +#define DO_509(MACRO, ...) \ +MACRO(509, __VA_ARGS__) \ +DO_508(MACRO, __VA_ARGS__) + + +#define DO_510(MACRO, ...) \ +MACRO(510, __VA_ARGS__) \ +DO_509(MACRO, __VA_ARGS__) + + +#define DO_511(MACRO, ...) \ +MACRO(511, __VA_ARGS__) \ +DO_510(MACRO, __VA_ARGS__) + + +#define DO_512(MACRO, ...) \ +MACRO(512, __VA_ARGS__) \ +DO_511(MACRO, __VA_ARGS__) + + +#define DO_513(MACRO, ...) \ +MACRO(513, __VA_ARGS__) \ +DO_512(MACRO, __VA_ARGS__) + + +#define DO_514(MACRO, ...) \ +MACRO(514, __VA_ARGS__) \ +DO_513(MACRO, __VA_ARGS__) + + +#define DO_515(MACRO, ...) \ +MACRO(515, __VA_ARGS__) \ +DO_514(MACRO, __VA_ARGS__) + + +#define DO_516(MACRO, ...) \ +MACRO(516, __VA_ARGS__) \ +DO_515(MACRO, __VA_ARGS__) + + +#define DO_517(MACRO, ...) \ +MACRO(517, __VA_ARGS__) \ +DO_516(MACRO, __VA_ARGS__) + + +#define DO_518(MACRO, ...) \ +MACRO(518, __VA_ARGS__) \ +DO_517(MACRO, __VA_ARGS__) + + +#define DO_519(MACRO, ...) \ +MACRO(519, __VA_ARGS__) \ +DO_518(MACRO, __VA_ARGS__) + + +#define DO_520(MACRO, ...) \ +MACRO(520, __VA_ARGS__) \ +DO_519(MACRO, __VA_ARGS__) + + +#define DO_521(MACRO, ...) \ +MACRO(521, __VA_ARGS__) \ +DO_520(MACRO, __VA_ARGS__) + + +#define DO_522(MACRO, ...) \ +MACRO(522, __VA_ARGS__) \ +DO_521(MACRO, __VA_ARGS__) + + +#define DO_523(MACRO, ...) \ +MACRO(523, __VA_ARGS__) \ +DO_522(MACRO, __VA_ARGS__) + + +#define DO_524(MACRO, ...) \ +MACRO(524, __VA_ARGS__) \ +DO_523(MACRO, __VA_ARGS__) + + +#define DO_525(MACRO, ...) \ +MACRO(525, __VA_ARGS__) \ +DO_524(MACRO, __VA_ARGS__) + + +#define DO_526(MACRO, ...) \ +MACRO(526, __VA_ARGS__) \ +DO_525(MACRO, __VA_ARGS__) + + +#define DO_527(MACRO, ...) \ +MACRO(527, __VA_ARGS__) \ +DO_526(MACRO, __VA_ARGS__) + + +#define DO_528(MACRO, ...) \ +MACRO(528, __VA_ARGS__) \ +DO_527(MACRO, __VA_ARGS__) + + +#define DO_529(MACRO, ...) \ +MACRO(529, __VA_ARGS__) \ +DO_528(MACRO, __VA_ARGS__) + + +#define DO_530(MACRO, ...) \ +MACRO(530, __VA_ARGS__) \ +DO_529(MACRO, __VA_ARGS__) + + +#define DO_531(MACRO, ...) \ +MACRO(531, __VA_ARGS__) \ +DO_530(MACRO, __VA_ARGS__) + + +#define DO_532(MACRO, ...) \ +MACRO(532, __VA_ARGS__) \ +DO_531(MACRO, __VA_ARGS__) + + +#define DO_533(MACRO, ...) \ +MACRO(533, __VA_ARGS__) \ +DO_532(MACRO, __VA_ARGS__) + + +#define DO_534(MACRO, ...) \ +MACRO(534, __VA_ARGS__) \ +DO_533(MACRO, __VA_ARGS__) + + +#define DO_535(MACRO, ...) \ +MACRO(535, __VA_ARGS__) \ +DO_534(MACRO, __VA_ARGS__) + + +#define DO_536(MACRO, ...) \ +MACRO(536, __VA_ARGS__) \ +DO_535(MACRO, __VA_ARGS__) + + +#define DO_537(MACRO, ...) \ +MACRO(537, __VA_ARGS__) \ +DO_536(MACRO, __VA_ARGS__) + + +#define DO_538(MACRO, ...) \ +MACRO(538, __VA_ARGS__) \ +DO_537(MACRO, __VA_ARGS__) + + +#define DO_539(MACRO, ...) \ +MACRO(539, __VA_ARGS__) \ +DO_538(MACRO, __VA_ARGS__) + + +#define DO_540(MACRO, ...) \ +MACRO(540, __VA_ARGS__) \ +DO_539(MACRO, __VA_ARGS__) + + +#define DO_541(MACRO, ...) \ +MACRO(541, __VA_ARGS__) \ +DO_540(MACRO, __VA_ARGS__) + + +#define DO_542(MACRO, ...) \ +MACRO(542, __VA_ARGS__) \ +DO_541(MACRO, __VA_ARGS__) + + +#define DO_543(MACRO, ...) \ +MACRO(543, __VA_ARGS__) \ +DO_542(MACRO, __VA_ARGS__) + + +#define DO_544(MACRO, ...) \ +MACRO(544, __VA_ARGS__) \ +DO_543(MACRO, __VA_ARGS__) + + +#define DO_545(MACRO, ...) \ +MACRO(545, __VA_ARGS__) \ +DO_544(MACRO, __VA_ARGS__) + + +#define DO_546(MACRO, ...) \ +MACRO(546, __VA_ARGS__) \ +DO_545(MACRO, __VA_ARGS__) + + +#define DO_547(MACRO, ...) \ +MACRO(547, __VA_ARGS__) \ +DO_546(MACRO, __VA_ARGS__) + + +#define DO_548(MACRO, ...) \ +MACRO(548, __VA_ARGS__) \ +DO_547(MACRO, __VA_ARGS__) + + +#define DO_549(MACRO, ...) \ +MACRO(549, __VA_ARGS__) \ +DO_548(MACRO, __VA_ARGS__) + + +#define DO_550(MACRO, ...) \ +MACRO(550, __VA_ARGS__) \ +DO_549(MACRO, __VA_ARGS__) + + +#define DO_551(MACRO, ...) \ +MACRO(551, __VA_ARGS__) \ +DO_550(MACRO, __VA_ARGS__) + + +#define DO_552(MACRO, ...) \ +MACRO(552, __VA_ARGS__) \ +DO_551(MACRO, __VA_ARGS__) + + +#define DO_553(MACRO, ...) \ +MACRO(553, __VA_ARGS__) \ +DO_552(MACRO, __VA_ARGS__) + + +#define DO_554(MACRO, ...) \ +MACRO(554, __VA_ARGS__) \ +DO_553(MACRO, __VA_ARGS__) + + +#define DO_555(MACRO, ...) \ +MACRO(555, __VA_ARGS__) \ +DO_554(MACRO, __VA_ARGS__) + + +#define DO_556(MACRO, ...) \ +MACRO(556, __VA_ARGS__) \ +DO_555(MACRO, __VA_ARGS__) + + +#define DO_557(MACRO, ...) \ +MACRO(557, __VA_ARGS__) \ +DO_556(MACRO, __VA_ARGS__) + + +#define DO_558(MACRO, ...) \ +MACRO(558, __VA_ARGS__) \ +DO_557(MACRO, __VA_ARGS__) + + +#define DO_559(MACRO, ...) \ +MACRO(559, __VA_ARGS__) \ +DO_558(MACRO, __VA_ARGS__) + + +#define DO_560(MACRO, ...) \ +MACRO(560, __VA_ARGS__) \ +DO_559(MACRO, __VA_ARGS__) + + +#define DO_561(MACRO, ...) \ +MACRO(561, __VA_ARGS__) \ +DO_560(MACRO, __VA_ARGS__) + + +#define DO_562(MACRO, ...) \ +MACRO(562, __VA_ARGS__) \ +DO_561(MACRO, __VA_ARGS__) + + +#define DO_563(MACRO, ...) \ +MACRO(563, __VA_ARGS__) \ +DO_562(MACRO, __VA_ARGS__) + + +#define DO_564(MACRO, ...) \ +MACRO(564, __VA_ARGS__) \ +DO_563(MACRO, __VA_ARGS__) + + +#define DO_565(MACRO, ...) \ +MACRO(565, __VA_ARGS__) \ +DO_564(MACRO, __VA_ARGS__) + + +#define DO_566(MACRO, ...) \ +MACRO(566, __VA_ARGS__) \ +DO_565(MACRO, __VA_ARGS__) + + +#define DO_567(MACRO, ...) \ +MACRO(567, __VA_ARGS__) \ +DO_566(MACRO, __VA_ARGS__) + + +#define DO_568(MACRO, ...) \ +MACRO(568, __VA_ARGS__) \ +DO_567(MACRO, __VA_ARGS__) + + +#define DO_569(MACRO, ...) \ +MACRO(569, __VA_ARGS__) \ +DO_568(MACRO, __VA_ARGS__) + + +#define DO_570(MACRO, ...) \ +MACRO(570, __VA_ARGS__) \ +DO_569(MACRO, __VA_ARGS__) + + +#define DO_571(MACRO, ...) \ +MACRO(571, __VA_ARGS__) \ +DO_570(MACRO, __VA_ARGS__) + + +#define DO_572(MACRO, ...) \ +MACRO(572, __VA_ARGS__) \ +DO_571(MACRO, __VA_ARGS__) + + +#define DO_573(MACRO, ...) \ +MACRO(573, __VA_ARGS__) \ +DO_572(MACRO, __VA_ARGS__) + + +#define DO_574(MACRO, ...) \ +MACRO(574, __VA_ARGS__) \ +DO_573(MACRO, __VA_ARGS__) + + +#define DO_575(MACRO, ...) \ +MACRO(575, __VA_ARGS__) \ +DO_574(MACRO, __VA_ARGS__) + + +#define DO_576(MACRO, ...) \ +MACRO(576, __VA_ARGS__) \ +DO_575(MACRO, __VA_ARGS__) + + +#define DO_577(MACRO, ...) \ +MACRO(577, __VA_ARGS__) \ +DO_576(MACRO, __VA_ARGS__) + + +#define DO_578(MACRO, ...) \ +MACRO(578, __VA_ARGS__) \ +DO_577(MACRO, __VA_ARGS__) + + +#define DO_579(MACRO, ...) \ +MACRO(579, __VA_ARGS__) \ +DO_578(MACRO, __VA_ARGS__) + + +#define DO_580(MACRO, ...) \ +MACRO(580, __VA_ARGS__) \ +DO_579(MACRO, __VA_ARGS__) + + +#define DO_581(MACRO, ...) \ +MACRO(581, __VA_ARGS__) \ +DO_580(MACRO, __VA_ARGS__) + + +#define DO_582(MACRO, ...) \ +MACRO(582, __VA_ARGS__) \ +DO_581(MACRO, __VA_ARGS__) + + +#define DO_583(MACRO, ...) \ +MACRO(583, __VA_ARGS__) \ +DO_582(MACRO, __VA_ARGS__) + + +#define DO_584(MACRO, ...) \ +MACRO(584, __VA_ARGS__) \ +DO_583(MACRO, __VA_ARGS__) + + +#define DO_585(MACRO, ...) \ +MACRO(585, __VA_ARGS__) \ +DO_584(MACRO, __VA_ARGS__) + + +#define DO_586(MACRO, ...) \ +MACRO(586, __VA_ARGS__) \ +DO_585(MACRO, __VA_ARGS__) + + +#define DO_587(MACRO, ...) \ +MACRO(587, __VA_ARGS__) \ +DO_586(MACRO, __VA_ARGS__) + + +#define DO_588(MACRO, ...) \ +MACRO(588, __VA_ARGS__) \ +DO_587(MACRO, __VA_ARGS__) + + +#define DO_589(MACRO, ...) \ +MACRO(589, __VA_ARGS__) \ +DO_588(MACRO, __VA_ARGS__) + + +#define DO_590(MACRO, ...) \ +MACRO(590, __VA_ARGS__) \ +DO_589(MACRO, __VA_ARGS__) + + +#define DO_591(MACRO, ...) \ +MACRO(591, __VA_ARGS__) \ +DO_590(MACRO, __VA_ARGS__) + + +#define DO_592(MACRO, ...) \ +MACRO(592, __VA_ARGS__) \ +DO_591(MACRO, __VA_ARGS__) + + +#define DO_593(MACRO, ...) \ +MACRO(593, __VA_ARGS__) \ +DO_592(MACRO, __VA_ARGS__) + + +#define DO_594(MACRO, ...) \ +MACRO(594, __VA_ARGS__) \ +DO_593(MACRO, __VA_ARGS__) + + +#define DO_595(MACRO, ...) \ +MACRO(595, __VA_ARGS__) \ +DO_594(MACRO, __VA_ARGS__) + + +#define DO_596(MACRO, ...) \ +MACRO(596, __VA_ARGS__) \ +DO_595(MACRO, __VA_ARGS__) + + +#define DO_597(MACRO, ...) \ +MACRO(597, __VA_ARGS__) \ +DO_596(MACRO, __VA_ARGS__) + + +#define DO_598(MACRO, ...) \ +MACRO(598, __VA_ARGS__) \ +DO_597(MACRO, __VA_ARGS__) + + +#define DO_599(MACRO, ...) \ +MACRO(599, __VA_ARGS__) \ +DO_598(MACRO, __VA_ARGS__) + + +#define DO_600(MACRO, ...) \ +MACRO(600, __VA_ARGS__) \ +DO_599(MACRO, __VA_ARGS__) + + +#define DO_601(MACRO, ...) \ +MACRO(601, __VA_ARGS__) \ +DO_600(MACRO, __VA_ARGS__) + + +#define DO_602(MACRO, ...) \ +MACRO(602, __VA_ARGS__) \ +DO_601(MACRO, __VA_ARGS__) + + +#define DO_603(MACRO, ...) \ +MACRO(603, __VA_ARGS__) \ +DO_602(MACRO, __VA_ARGS__) + + +#define DO_604(MACRO, ...) \ +MACRO(604, __VA_ARGS__) \ +DO_603(MACRO, __VA_ARGS__) + + +#define DO_605(MACRO, ...) \ +MACRO(605, __VA_ARGS__) \ +DO_604(MACRO, __VA_ARGS__) + + +#define DO_606(MACRO, ...) \ +MACRO(606, __VA_ARGS__) \ +DO_605(MACRO, __VA_ARGS__) + + +#define DO_607(MACRO, ...) \ +MACRO(607, __VA_ARGS__) \ +DO_606(MACRO, __VA_ARGS__) + + +#define DO_608(MACRO, ...) \ +MACRO(608, __VA_ARGS__) \ +DO_607(MACRO, __VA_ARGS__) + + +#define DO_609(MACRO, ...) \ +MACRO(609, __VA_ARGS__) \ +DO_608(MACRO, __VA_ARGS__) + + +#define DO_610(MACRO, ...) \ +MACRO(610, __VA_ARGS__) \ +DO_609(MACRO, __VA_ARGS__) + + +#define DO_611(MACRO, ...) \ +MACRO(611, __VA_ARGS__) \ +DO_610(MACRO, __VA_ARGS__) + + +#define DO_612(MACRO, ...) \ +MACRO(612, __VA_ARGS__) \ +DO_611(MACRO, __VA_ARGS__) + + +#define DO_613(MACRO, ...) \ +MACRO(613, __VA_ARGS__) \ +DO_612(MACRO, __VA_ARGS__) + + +#define DO_614(MACRO, ...) \ +MACRO(614, __VA_ARGS__) \ +DO_613(MACRO, __VA_ARGS__) + + +#define DO_615(MACRO, ...) \ +MACRO(615, __VA_ARGS__) \ +DO_614(MACRO, __VA_ARGS__) + + +#define DO_616(MACRO, ...) \ +MACRO(616, __VA_ARGS__) \ +DO_615(MACRO, __VA_ARGS__) + + +#define DO_617(MACRO, ...) \ +MACRO(617, __VA_ARGS__) \ +DO_616(MACRO, __VA_ARGS__) + + +#define DO_618(MACRO, ...) \ +MACRO(618, __VA_ARGS__) \ +DO_617(MACRO, __VA_ARGS__) + + +#define DO_619(MACRO, ...) \ +MACRO(619, __VA_ARGS__) \ +DO_618(MACRO, __VA_ARGS__) + + +#define DO_620(MACRO, ...) \ +MACRO(620, __VA_ARGS__) \ +DO_619(MACRO, __VA_ARGS__) + + +#define DO_621(MACRO, ...) \ +MACRO(621, __VA_ARGS__) \ +DO_620(MACRO, __VA_ARGS__) + + +#define DO_622(MACRO, ...) \ +MACRO(622, __VA_ARGS__) \ +DO_621(MACRO, __VA_ARGS__) + + +#define DO_623(MACRO, ...) \ +MACRO(623, __VA_ARGS__) \ +DO_622(MACRO, __VA_ARGS__) + + +#define DO_624(MACRO, ...) \ +MACRO(624, __VA_ARGS__) \ +DO_623(MACRO, __VA_ARGS__) + + +#define DO_625(MACRO, ...) \ +MACRO(625, __VA_ARGS__) \ +DO_624(MACRO, __VA_ARGS__) + + +#define DO_626(MACRO, ...) \ +MACRO(626, __VA_ARGS__) \ +DO_625(MACRO, __VA_ARGS__) + + +#define DO_627(MACRO, ...) \ +MACRO(627, __VA_ARGS__) \ +DO_626(MACRO, __VA_ARGS__) + + +#define DO_628(MACRO, ...) \ +MACRO(628, __VA_ARGS__) \ +DO_627(MACRO, __VA_ARGS__) + + +#define DO_629(MACRO, ...) \ +MACRO(629, __VA_ARGS__) \ +DO_628(MACRO, __VA_ARGS__) + + +#define DO_630(MACRO, ...) \ +MACRO(630, __VA_ARGS__) \ +DO_629(MACRO, __VA_ARGS__) + + +#define DO_631(MACRO, ...) \ +MACRO(631, __VA_ARGS__) \ +DO_630(MACRO, __VA_ARGS__) + + +#define DO_632(MACRO, ...) \ +MACRO(632, __VA_ARGS__) \ +DO_631(MACRO, __VA_ARGS__) + + +#define DO_633(MACRO, ...) \ +MACRO(633, __VA_ARGS__) \ +DO_632(MACRO, __VA_ARGS__) + + +#define DO_634(MACRO, ...) \ +MACRO(634, __VA_ARGS__) \ +DO_633(MACRO, __VA_ARGS__) + + +#define DO_635(MACRO, ...) \ +MACRO(635, __VA_ARGS__) \ +DO_634(MACRO, __VA_ARGS__) + + +#define DO_636(MACRO, ...) \ +MACRO(636, __VA_ARGS__) \ +DO_635(MACRO, __VA_ARGS__) + + +#define DO_637(MACRO, ...) \ +MACRO(637, __VA_ARGS__) \ +DO_636(MACRO, __VA_ARGS__) + + +#define DO_638(MACRO, ...) \ +MACRO(638, __VA_ARGS__) \ +DO_637(MACRO, __VA_ARGS__) + + +#define DO_639(MACRO, ...) \ +MACRO(639, __VA_ARGS__) \ +DO_638(MACRO, __VA_ARGS__) + + +#define DO_640(MACRO, ...) \ +MACRO(640, __VA_ARGS__) \ +DO_639(MACRO, __VA_ARGS__) + + +#define DO_641(MACRO, ...) \ +MACRO(641, __VA_ARGS__) \ +DO_640(MACRO, __VA_ARGS__) + + +#define DO_642(MACRO, ...) \ +MACRO(642, __VA_ARGS__) \ +DO_641(MACRO, __VA_ARGS__) + + +#define DO_643(MACRO, ...) \ +MACRO(643, __VA_ARGS__) \ +DO_642(MACRO, __VA_ARGS__) + + +#define DO_644(MACRO, ...) \ +MACRO(644, __VA_ARGS__) \ +DO_643(MACRO, __VA_ARGS__) + + +#define DO_645(MACRO, ...) \ +MACRO(645, __VA_ARGS__) \ +DO_644(MACRO, __VA_ARGS__) + + +#define DO_646(MACRO, ...) \ +MACRO(646, __VA_ARGS__) \ +DO_645(MACRO, __VA_ARGS__) + + +#define DO_647(MACRO, ...) \ +MACRO(647, __VA_ARGS__) \ +DO_646(MACRO, __VA_ARGS__) + + +#define DO_648(MACRO, ...) \ +MACRO(648, __VA_ARGS__) \ +DO_647(MACRO, __VA_ARGS__) + + +#define DO_649(MACRO, ...) \ +MACRO(649, __VA_ARGS__) \ +DO_648(MACRO, __VA_ARGS__) + + +#define DO_650(MACRO, ...) \ +MACRO(650, __VA_ARGS__) \ +DO_649(MACRO, __VA_ARGS__) + + +#define DO_651(MACRO, ...) \ +MACRO(651, __VA_ARGS__) \ +DO_650(MACRO, __VA_ARGS__) + + +#define DO_652(MACRO, ...) \ +MACRO(652, __VA_ARGS__) \ +DO_651(MACRO, __VA_ARGS__) + + +#define DO_653(MACRO, ...) \ +MACRO(653, __VA_ARGS__) \ +DO_652(MACRO, __VA_ARGS__) + + +#define DO_654(MACRO, ...) \ +MACRO(654, __VA_ARGS__) \ +DO_653(MACRO, __VA_ARGS__) + + +#define DO_655(MACRO, ...) \ +MACRO(655, __VA_ARGS__) \ +DO_654(MACRO, __VA_ARGS__) + + +#define DO_656(MACRO, ...) \ +MACRO(656, __VA_ARGS__) \ +DO_655(MACRO, __VA_ARGS__) + + +#define DO_657(MACRO, ...) \ +MACRO(657, __VA_ARGS__) \ +DO_656(MACRO, __VA_ARGS__) + + +#define DO_658(MACRO, ...) \ +MACRO(658, __VA_ARGS__) \ +DO_657(MACRO, __VA_ARGS__) + + +#define DO_659(MACRO, ...) \ +MACRO(659, __VA_ARGS__) \ +DO_658(MACRO, __VA_ARGS__) + + +#define DO_660(MACRO, ...) \ +MACRO(660, __VA_ARGS__) \ +DO_659(MACRO, __VA_ARGS__) + + +#define DO_661(MACRO, ...) \ +MACRO(661, __VA_ARGS__) \ +DO_660(MACRO, __VA_ARGS__) + + +#define DO_662(MACRO, ...) \ +MACRO(662, __VA_ARGS__) \ +DO_661(MACRO, __VA_ARGS__) + + +#define DO_663(MACRO, ...) \ +MACRO(663, __VA_ARGS__) \ +DO_662(MACRO, __VA_ARGS__) + + +#define DO_664(MACRO, ...) \ +MACRO(664, __VA_ARGS__) \ +DO_663(MACRO, __VA_ARGS__) + + +#define DO_665(MACRO, ...) \ +MACRO(665, __VA_ARGS__) \ +DO_664(MACRO, __VA_ARGS__) + + +#define DO_666(MACRO, ...) \ +MACRO(666, __VA_ARGS__) \ +DO_665(MACRO, __VA_ARGS__) + + +#define DO_667(MACRO, ...) \ +MACRO(667, __VA_ARGS__) \ +DO_666(MACRO, __VA_ARGS__) + + +#define DO_668(MACRO, ...) \ +MACRO(668, __VA_ARGS__) \ +DO_667(MACRO, __VA_ARGS__) + + +#define DO_669(MACRO, ...) \ +MACRO(669, __VA_ARGS__) \ +DO_668(MACRO, __VA_ARGS__) + + +#define DO_670(MACRO, ...) \ +MACRO(670, __VA_ARGS__) \ +DO_669(MACRO, __VA_ARGS__) + + +#define DO_671(MACRO, ...) \ +MACRO(671, __VA_ARGS__) \ +DO_670(MACRO, __VA_ARGS__) + + +#define DO_672(MACRO, ...) \ +MACRO(672, __VA_ARGS__) \ +DO_671(MACRO, __VA_ARGS__) + + +#define DO_673(MACRO, ...) \ +MACRO(673, __VA_ARGS__) \ +DO_672(MACRO, __VA_ARGS__) + + +#define DO_674(MACRO, ...) \ +MACRO(674, __VA_ARGS__) \ +DO_673(MACRO, __VA_ARGS__) + + +#define DO_675(MACRO, ...) \ +MACRO(675, __VA_ARGS__) \ +DO_674(MACRO, __VA_ARGS__) + + +#define DO_676(MACRO, ...) \ +MACRO(676, __VA_ARGS__) \ +DO_675(MACRO, __VA_ARGS__) + + +#define DO_677(MACRO, ...) \ +MACRO(677, __VA_ARGS__) \ +DO_676(MACRO, __VA_ARGS__) + + +#define DO_678(MACRO, ...) \ +MACRO(678, __VA_ARGS__) \ +DO_677(MACRO, __VA_ARGS__) + + +#define DO_679(MACRO, ...) \ +MACRO(679, __VA_ARGS__) \ +DO_678(MACRO, __VA_ARGS__) + + +#define DO_680(MACRO, ...) \ +MACRO(680, __VA_ARGS__) \ +DO_679(MACRO, __VA_ARGS__) + + +#define DO_681(MACRO, ...) \ +MACRO(681, __VA_ARGS__) \ +DO_680(MACRO, __VA_ARGS__) + + +#define DO_682(MACRO, ...) \ +MACRO(682, __VA_ARGS__) \ +DO_681(MACRO, __VA_ARGS__) + + +#define DO_683(MACRO, ...) \ +MACRO(683, __VA_ARGS__) \ +DO_682(MACRO, __VA_ARGS__) + + +#define DO_684(MACRO, ...) \ +MACRO(684, __VA_ARGS__) \ +DO_683(MACRO, __VA_ARGS__) + + +#define DO_685(MACRO, ...) \ +MACRO(685, __VA_ARGS__) \ +DO_684(MACRO, __VA_ARGS__) + + +#define DO_686(MACRO, ...) \ +MACRO(686, __VA_ARGS__) \ +DO_685(MACRO, __VA_ARGS__) + + +#define DO_687(MACRO, ...) \ +MACRO(687, __VA_ARGS__) \ +DO_686(MACRO, __VA_ARGS__) + + +#define DO_688(MACRO, ...) \ +MACRO(688, __VA_ARGS__) \ +DO_687(MACRO, __VA_ARGS__) + + +#define DO_689(MACRO, ...) \ +MACRO(689, __VA_ARGS__) \ +DO_688(MACRO, __VA_ARGS__) + + +#define DO_690(MACRO, ...) \ +MACRO(690, __VA_ARGS__) \ +DO_689(MACRO, __VA_ARGS__) + + +#define DO_691(MACRO, ...) \ +MACRO(691, __VA_ARGS__) \ +DO_690(MACRO, __VA_ARGS__) + + +#define DO_692(MACRO, ...) \ +MACRO(692, __VA_ARGS__) \ +DO_691(MACRO, __VA_ARGS__) + + +#define DO_693(MACRO, ...) \ +MACRO(693, __VA_ARGS__) \ +DO_692(MACRO, __VA_ARGS__) + + +#define DO_694(MACRO, ...) \ +MACRO(694, __VA_ARGS__) \ +DO_693(MACRO, __VA_ARGS__) + + +#define DO_695(MACRO, ...) \ +MACRO(695, __VA_ARGS__) \ +DO_694(MACRO, __VA_ARGS__) + + +#define DO_696(MACRO, ...) \ +MACRO(696, __VA_ARGS__) \ +DO_695(MACRO, __VA_ARGS__) + + +#define DO_697(MACRO, ...) \ +MACRO(697, __VA_ARGS__) \ +DO_696(MACRO, __VA_ARGS__) + + +#define DO_698(MACRO, ...) \ +MACRO(698, __VA_ARGS__) \ +DO_697(MACRO, __VA_ARGS__) + + +#define DO_699(MACRO, ...) \ +MACRO(699, __VA_ARGS__) \ +DO_698(MACRO, __VA_ARGS__) + + +#define DO_700(MACRO, ...) \ +MACRO(700, __VA_ARGS__) \ +DO_699(MACRO, __VA_ARGS__) + + +#define DO_701(MACRO, ...) \ +MACRO(701, __VA_ARGS__) \ +DO_700(MACRO, __VA_ARGS__) + + +#define DO_702(MACRO, ...) \ +MACRO(702, __VA_ARGS__) \ +DO_701(MACRO, __VA_ARGS__) + + +#define DO_703(MACRO, ...) \ +MACRO(703, __VA_ARGS__) \ +DO_702(MACRO, __VA_ARGS__) + + +#define DO_704(MACRO, ...) \ +MACRO(704, __VA_ARGS__) \ +DO_703(MACRO, __VA_ARGS__) + + +#define DO_705(MACRO, ...) \ +MACRO(705, __VA_ARGS__) \ +DO_704(MACRO, __VA_ARGS__) + + +#define DO_706(MACRO, ...) \ +MACRO(706, __VA_ARGS__) \ +DO_705(MACRO, __VA_ARGS__) + + +#define DO_707(MACRO, ...) \ +MACRO(707, __VA_ARGS__) \ +DO_706(MACRO, __VA_ARGS__) + + +#define DO_708(MACRO, ...) \ +MACRO(708, __VA_ARGS__) \ +DO_707(MACRO, __VA_ARGS__) + + +#define DO_709(MACRO, ...) \ +MACRO(709, __VA_ARGS__) \ +DO_708(MACRO, __VA_ARGS__) + + +#define DO_710(MACRO, ...) \ +MACRO(710, __VA_ARGS__) \ +DO_709(MACRO, __VA_ARGS__) + + +#define DO_711(MACRO, ...) \ +MACRO(711, __VA_ARGS__) \ +DO_710(MACRO, __VA_ARGS__) + + +#define DO_712(MACRO, ...) \ +MACRO(712, __VA_ARGS__) \ +DO_711(MACRO, __VA_ARGS__) + + +#define DO_713(MACRO, ...) \ +MACRO(713, __VA_ARGS__) \ +DO_712(MACRO, __VA_ARGS__) + + +#define DO_714(MACRO, ...) \ +MACRO(714, __VA_ARGS__) \ +DO_713(MACRO, __VA_ARGS__) + + +#define DO_715(MACRO, ...) \ +MACRO(715, __VA_ARGS__) \ +DO_714(MACRO, __VA_ARGS__) + + +#define DO_716(MACRO, ...) \ +MACRO(716, __VA_ARGS__) \ +DO_715(MACRO, __VA_ARGS__) + + +#define DO_717(MACRO, ...) \ +MACRO(717, __VA_ARGS__) \ +DO_716(MACRO, __VA_ARGS__) + + +#define DO_718(MACRO, ...) \ +MACRO(718, __VA_ARGS__) \ +DO_717(MACRO, __VA_ARGS__) + + +#define DO_719(MACRO, ...) \ +MACRO(719, __VA_ARGS__) \ +DO_718(MACRO, __VA_ARGS__) + + +#define DO_720(MACRO, ...) \ +MACRO(720, __VA_ARGS__) \ +DO_719(MACRO, __VA_ARGS__) + + +#define DO_721(MACRO, ...) \ +MACRO(721, __VA_ARGS__) \ +DO_720(MACRO, __VA_ARGS__) + + +#define DO_722(MACRO, ...) \ +MACRO(722, __VA_ARGS__) \ +DO_721(MACRO, __VA_ARGS__) + + +#define DO_723(MACRO, ...) \ +MACRO(723, __VA_ARGS__) \ +DO_722(MACRO, __VA_ARGS__) + + +#define DO_724(MACRO, ...) \ +MACRO(724, __VA_ARGS__) \ +DO_723(MACRO, __VA_ARGS__) + + +#define DO_725(MACRO, ...) \ +MACRO(725, __VA_ARGS__) \ +DO_724(MACRO, __VA_ARGS__) + + +#define DO_726(MACRO, ...) \ +MACRO(726, __VA_ARGS__) \ +DO_725(MACRO, __VA_ARGS__) + + +#define DO_727(MACRO, ...) \ +MACRO(727, __VA_ARGS__) \ +DO_726(MACRO, __VA_ARGS__) + + +#define DO_728(MACRO, ...) \ +MACRO(728, __VA_ARGS__) \ +DO_727(MACRO, __VA_ARGS__) + + +#define DO_729(MACRO, ...) \ +MACRO(729, __VA_ARGS__) \ +DO_728(MACRO, __VA_ARGS__) + + +#define DO_730(MACRO, ...) \ +MACRO(730, __VA_ARGS__) \ +DO_729(MACRO, __VA_ARGS__) + + +#define DO_731(MACRO, ...) \ +MACRO(731, __VA_ARGS__) \ +DO_730(MACRO, __VA_ARGS__) + + +#define DO_732(MACRO, ...) \ +MACRO(732, __VA_ARGS__) \ +DO_731(MACRO, __VA_ARGS__) + + +#define DO_733(MACRO, ...) \ +MACRO(733, __VA_ARGS__) \ +DO_732(MACRO, __VA_ARGS__) + + +#define DO_734(MACRO, ...) \ +MACRO(734, __VA_ARGS__) \ +DO_733(MACRO, __VA_ARGS__) + + +#define DO_735(MACRO, ...) \ +MACRO(735, __VA_ARGS__) \ +DO_734(MACRO, __VA_ARGS__) + + +#define DO_736(MACRO, ...) \ +MACRO(736, __VA_ARGS__) \ +DO_735(MACRO, __VA_ARGS__) + + +#define DO_737(MACRO, ...) \ +MACRO(737, __VA_ARGS__) \ +DO_736(MACRO, __VA_ARGS__) + + +#define DO_738(MACRO, ...) \ +MACRO(738, __VA_ARGS__) \ +DO_737(MACRO, __VA_ARGS__) + + +#define DO_739(MACRO, ...) \ +MACRO(739, __VA_ARGS__) \ +DO_738(MACRO, __VA_ARGS__) + + +#define DO_740(MACRO, ...) \ +MACRO(740, __VA_ARGS__) \ +DO_739(MACRO, __VA_ARGS__) + + +#define DO_741(MACRO, ...) \ +MACRO(741, __VA_ARGS__) \ +DO_740(MACRO, __VA_ARGS__) + + +#define DO_742(MACRO, ...) \ +MACRO(742, __VA_ARGS__) \ +DO_741(MACRO, __VA_ARGS__) + + +#define DO_743(MACRO, ...) \ +MACRO(743, __VA_ARGS__) \ +DO_742(MACRO, __VA_ARGS__) + + +#define DO_744(MACRO, ...) \ +MACRO(744, __VA_ARGS__) \ +DO_743(MACRO, __VA_ARGS__) + + +#define DO_745(MACRO, ...) \ +MACRO(745, __VA_ARGS__) \ +DO_744(MACRO, __VA_ARGS__) + + +#define DO_746(MACRO, ...) \ +MACRO(746, __VA_ARGS__) \ +DO_745(MACRO, __VA_ARGS__) + + +#define DO_747(MACRO, ...) \ +MACRO(747, __VA_ARGS__) \ +DO_746(MACRO, __VA_ARGS__) + + +#define DO_748(MACRO, ...) \ +MACRO(748, __VA_ARGS__) \ +DO_747(MACRO, __VA_ARGS__) + + +#define DO_749(MACRO, ...) \ +MACRO(749, __VA_ARGS__) \ +DO_748(MACRO, __VA_ARGS__) + + +#define DO_750(MACRO, ...) \ +MACRO(750, __VA_ARGS__) \ +DO_749(MACRO, __VA_ARGS__) + + +#define DO_751(MACRO, ...) \ +MACRO(751, __VA_ARGS__) \ +DO_750(MACRO, __VA_ARGS__) + + +#define DO_752(MACRO, ...) \ +MACRO(752, __VA_ARGS__) \ +DO_751(MACRO, __VA_ARGS__) + + +#define DO_753(MACRO, ...) \ +MACRO(753, __VA_ARGS__) \ +DO_752(MACRO, __VA_ARGS__) + + +#define DO_754(MACRO, ...) \ +MACRO(754, __VA_ARGS__) \ +DO_753(MACRO, __VA_ARGS__) + + +#define DO_755(MACRO, ...) \ +MACRO(755, __VA_ARGS__) \ +DO_754(MACRO, __VA_ARGS__) + + +#define DO_756(MACRO, ...) \ +MACRO(756, __VA_ARGS__) \ +DO_755(MACRO, __VA_ARGS__) + + +#define DO_757(MACRO, ...) \ +MACRO(757, __VA_ARGS__) \ +DO_756(MACRO, __VA_ARGS__) + + +#define DO_758(MACRO, ...) \ +MACRO(758, __VA_ARGS__) \ +DO_757(MACRO, __VA_ARGS__) + + +#define DO_759(MACRO, ...) \ +MACRO(759, __VA_ARGS__) \ +DO_758(MACRO, __VA_ARGS__) + + +#define DO_760(MACRO, ...) \ +MACRO(760, __VA_ARGS__) \ +DO_759(MACRO, __VA_ARGS__) + + +#define DO_761(MACRO, ...) \ +MACRO(761, __VA_ARGS__) \ +DO_760(MACRO, __VA_ARGS__) + + +#define DO_762(MACRO, ...) \ +MACRO(762, __VA_ARGS__) \ +DO_761(MACRO, __VA_ARGS__) + + +#define DO_763(MACRO, ...) \ +MACRO(763, __VA_ARGS__) \ +DO_762(MACRO, __VA_ARGS__) + + +#define DO_764(MACRO, ...) \ +MACRO(764, __VA_ARGS__) \ +DO_763(MACRO, __VA_ARGS__) + + +#define DO_765(MACRO, ...) \ +MACRO(765, __VA_ARGS__) \ +DO_764(MACRO, __VA_ARGS__) + + +#define DO_766(MACRO, ...) \ +MACRO(766, __VA_ARGS__) \ +DO_765(MACRO, __VA_ARGS__) + + +#define DO_767(MACRO, ...) \ +MACRO(767, __VA_ARGS__) \ +DO_766(MACRO, __VA_ARGS__) + + +#define DO_768(MACRO, ...) \ +MACRO(768, __VA_ARGS__) \ +DO_767(MACRO, __VA_ARGS__) + + +#define DO_769(MACRO, ...) \ +MACRO(769, __VA_ARGS__) \ +DO_768(MACRO, __VA_ARGS__) + + +#define DO_770(MACRO, ...) \ +MACRO(770, __VA_ARGS__) \ +DO_769(MACRO, __VA_ARGS__) + + +#define DO_771(MACRO, ...) \ +MACRO(771, __VA_ARGS__) \ +DO_770(MACRO, __VA_ARGS__) + + +#define DO_772(MACRO, ...) \ +MACRO(772, __VA_ARGS__) \ +DO_771(MACRO, __VA_ARGS__) + + +#define DO_773(MACRO, ...) \ +MACRO(773, __VA_ARGS__) \ +DO_772(MACRO, __VA_ARGS__) + + +#define DO_774(MACRO, ...) \ +MACRO(774, __VA_ARGS__) \ +DO_773(MACRO, __VA_ARGS__) + + +#define DO_775(MACRO, ...) \ +MACRO(775, __VA_ARGS__) \ +DO_774(MACRO, __VA_ARGS__) + + +#define DO_776(MACRO, ...) \ +MACRO(776, __VA_ARGS__) \ +DO_775(MACRO, __VA_ARGS__) + + +#define DO_777(MACRO, ...) \ +MACRO(777, __VA_ARGS__) \ +DO_776(MACRO, __VA_ARGS__) + + +#define DO_778(MACRO, ...) \ +MACRO(778, __VA_ARGS__) \ +DO_777(MACRO, __VA_ARGS__) + + +#define DO_779(MACRO, ...) \ +MACRO(779, __VA_ARGS__) \ +DO_778(MACRO, __VA_ARGS__) + + +#define DO_780(MACRO, ...) \ +MACRO(780, __VA_ARGS__) \ +DO_779(MACRO, __VA_ARGS__) + + +#define DO_781(MACRO, ...) \ +MACRO(781, __VA_ARGS__) \ +DO_780(MACRO, __VA_ARGS__) + + +#define DO_782(MACRO, ...) \ +MACRO(782, __VA_ARGS__) \ +DO_781(MACRO, __VA_ARGS__) + + +#define DO_783(MACRO, ...) \ +MACRO(783, __VA_ARGS__) \ +DO_782(MACRO, __VA_ARGS__) + + +#define DO_784(MACRO, ...) \ +MACRO(784, __VA_ARGS__) \ +DO_783(MACRO, __VA_ARGS__) + + +#define DO_785(MACRO, ...) \ +MACRO(785, __VA_ARGS__) \ +DO_784(MACRO, __VA_ARGS__) + + +#define DO_786(MACRO, ...) \ +MACRO(786, __VA_ARGS__) \ +DO_785(MACRO, __VA_ARGS__) + + +#define DO_787(MACRO, ...) \ +MACRO(787, __VA_ARGS__) \ +DO_786(MACRO, __VA_ARGS__) + + +#define DO_788(MACRO, ...) \ +MACRO(788, __VA_ARGS__) \ +DO_787(MACRO, __VA_ARGS__) + + +#define DO_789(MACRO, ...) \ +MACRO(789, __VA_ARGS__) \ +DO_788(MACRO, __VA_ARGS__) + + +#define DO_790(MACRO, ...) \ +MACRO(790, __VA_ARGS__) \ +DO_789(MACRO, __VA_ARGS__) + + +#define DO_791(MACRO, ...) \ +MACRO(791, __VA_ARGS__) \ +DO_790(MACRO, __VA_ARGS__) + + +#define DO_792(MACRO, ...) \ +MACRO(792, __VA_ARGS__) \ +DO_791(MACRO, __VA_ARGS__) + + +#define DO_793(MACRO, ...) \ +MACRO(793, __VA_ARGS__) \ +DO_792(MACRO, __VA_ARGS__) + + +#define DO_794(MACRO, ...) \ +MACRO(794, __VA_ARGS__) \ +DO_793(MACRO, __VA_ARGS__) + + +#define DO_795(MACRO, ...) \ +MACRO(795, __VA_ARGS__) \ +DO_794(MACRO, __VA_ARGS__) + + +#define DO_796(MACRO, ...) \ +MACRO(796, __VA_ARGS__) \ +DO_795(MACRO, __VA_ARGS__) + + +#define DO_797(MACRO, ...) \ +MACRO(797, __VA_ARGS__) \ +DO_796(MACRO, __VA_ARGS__) + + +#define DO_798(MACRO, ...) \ +MACRO(798, __VA_ARGS__) \ +DO_797(MACRO, __VA_ARGS__) + + +#define DO_799(MACRO, ...) \ +MACRO(799, __VA_ARGS__) \ +DO_798(MACRO, __VA_ARGS__) + + +#define DO_800(MACRO, ...) \ +MACRO(800, __VA_ARGS__) \ +DO_799(MACRO, __VA_ARGS__) + + +#define DO_801(MACRO, ...) \ +MACRO(801, __VA_ARGS__) \ +DO_800(MACRO, __VA_ARGS__) + + +#define DO_802(MACRO, ...) \ +MACRO(802, __VA_ARGS__) \ +DO_801(MACRO, __VA_ARGS__) + + +#define DO_803(MACRO, ...) \ +MACRO(803, __VA_ARGS__) \ +DO_802(MACRO, __VA_ARGS__) + + +#define DO_804(MACRO, ...) \ +MACRO(804, __VA_ARGS__) \ +DO_803(MACRO, __VA_ARGS__) + + +#define DO_805(MACRO, ...) \ +MACRO(805, __VA_ARGS__) \ +DO_804(MACRO, __VA_ARGS__) + + +#define DO_806(MACRO, ...) \ +MACRO(806, __VA_ARGS__) \ +DO_805(MACRO, __VA_ARGS__) + + +#define DO_807(MACRO, ...) \ +MACRO(807, __VA_ARGS__) \ +DO_806(MACRO, __VA_ARGS__) + + +#define DO_808(MACRO, ...) \ +MACRO(808, __VA_ARGS__) \ +DO_807(MACRO, __VA_ARGS__) + + +#define DO_809(MACRO, ...) \ +MACRO(809, __VA_ARGS__) \ +DO_808(MACRO, __VA_ARGS__) + + +#define DO_810(MACRO, ...) \ +MACRO(810, __VA_ARGS__) \ +DO_809(MACRO, __VA_ARGS__) + + +#define DO_811(MACRO, ...) \ +MACRO(811, __VA_ARGS__) \ +DO_810(MACRO, __VA_ARGS__) + + +#define DO_812(MACRO, ...) \ +MACRO(812, __VA_ARGS__) \ +DO_811(MACRO, __VA_ARGS__) + + +#define DO_813(MACRO, ...) \ +MACRO(813, __VA_ARGS__) \ +DO_812(MACRO, __VA_ARGS__) + + +#define DO_814(MACRO, ...) \ +MACRO(814, __VA_ARGS__) \ +DO_813(MACRO, __VA_ARGS__) + + +#define DO_815(MACRO, ...) \ +MACRO(815, __VA_ARGS__) \ +DO_814(MACRO, __VA_ARGS__) + + +#define DO_816(MACRO, ...) \ +MACRO(816, __VA_ARGS__) \ +DO_815(MACRO, __VA_ARGS__) + + +#define DO_817(MACRO, ...) \ +MACRO(817, __VA_ARGS__) \ +DO_816(MACRO, __VA_ARGS__) + + +#define DO_818(MACRO, ...) \ +MACRO(818, __VA_ARGS__) \ +DO_817(MACRO, __VA_ARGS__) + + +#define DO_819(MACRO, ...) \ +MACRO(819, __VA_ARGS__) \ +DO_818(MACRO, __VA_ARGS__) + + +#define DO_820(MACRO, ...) \ +MACRO(820, __VA_ARGS__) \ +DO_819(MACRO, __VA_ARGS__) + + +#define DO_821(MACRO, ...) \ +MACRO(821, __VA_ARGS__) \ +DO_820(MACRO, __VA_ARGS__) + + +#define DO_822(MACRO, ...) \ +MACRO(822, __VA_ARGS__) \ +DO_821(MACRO, __VA_ARGS__) + + +#define DO_823(MACRO, ...) \ +MACRO(823, __VA_ARGS__) \ +DO_822(MACRO, __VA_ARGS__) + + +#define DO_824(MACRO, ...) \ +MACRO(824, __VA_ARGS__) \ +DO_823(MACRO, __VA_ARGS__) + + +#define DO_825(MACRO, ...) \ +MACRO(825, __VA_ARGS__) \ +DO_824(MACRO, __VA_ARGS__) + + +#define DO_826(MACRO, ...) \ +MACRO(826, __VA_ARGS__) \ +DO_825(MACRO, __VA_ARGS__) + + +#define DO_827(MACRO, ...) \ +MACRO(827, __VA_ARGS__) \ +DO_826(MACRO, __VA_ARGS__) + + +#define DO_828(MACRO, ...) \ +MACRO(828, __VA_ARGS__) \ +DO_827(MACRO, __VA_ARGS__) + + +#define DO_829(MACRO, ...) \ +MACRO(829, __VA_ARGS__) \ +DO_828(MACRO, __VA_ARGS__) + + +#define DO_830(MACRO, ...) \ +MACRO(830, __VA_ARGS__) \ +DO_829(MACRO, __VA_ARGS__) + + +#define DO_831(MACRO, ...) \ +MACRO(831, __VA_ARGS__) \ +DO_830(MACRO, __VA_ARGS__) + + +#define DO_832(MACRO, ...) \ +MACRO(832, __VA_ARGS__) \ +DO_831(MACRO, __VA_ARGS__) + + +#define DO_833(MACRO, ...) \ +MACRO(833, __VA_ARGS__) \ +DO_832(MACRO, __VA_ARGS__) + + +#define DO_834(MACRO, ...) \ +MACRO(834, __VA_ARGS__) \ +DO_833(MACRO, __VA_ARGS__) + + +#define DO_835(MACRO, ...) \ +MACRO(835, __VA_ARGS__) \ +DO_834(MACRO, __VA_ARGS__) + + +#define DO_836(MACRO, ...) \ +MACRO(836, __VA_ARGS__) \ +DO_835(MACRO, __VA_ARGS__) + + +#define DO_837(MACRO, ...) \ +MACRO(837, __VA_ARGS__) \ +DO_836(MACRO, __VA_ARGS__) + + +#define DO_838(MACRO, ...) \ +MACRO(838, __VA_ARGS__) \ +DO_837(MACRO, __VA_ARGS__) + + +#define DO_839(MACRO, ...) \ +MACRO(839, __VA_ARGS__) \ +DO_838(MACRO, __VA_ARGS__) + + +#define DO_840(MACRO, ...) \ +MACRO(840, __VA_ARGS__) \ +DO_839(MACRO, __VA_ARGS__) + + +#define DO_841(MACRO, ...) \ +MACRO(841, __VA_ARGS__) \ +DO_840(MACRO, __VA_ARGS__) + + +#define DO_842(MACRO, ...) \ +MACRO(842, __VA_ARGS__) \ +DO_841(MACRO, __VA_ARGS__) + + +#define DO_843(MACRO, ...) \ +MACRO(843, __VA_ARGS__) \ +DO_842(MACRO, __VA_ARGS__) + + +#define DO_844(MACRO, ...) \ +MACRO(844, __VA_ARGS__) \ +DO_843(MACRO, __VA_ARGS__) + + +#define DO_845(MACRO, ...) \ +MACRO(845, __VA_ARGS__) \ +DO_844(MACRO, __VA_ARGS__) + + +#define DO_846(MACRO, ...) \ +MACRO(846, __VA_ARGS__) \ +DO_845(MACRO, __VA_ARGS__) + + +#define DO_847(MACRO, ...) \ +MACRO(847, __VA_ARGS__) \ +DO_846(MACRO, __VA_ARGS__) + + +#define DO_848(MACRO, ...) \ +MACRO(848, __VA_ARGS__) \ +DO_847(MACRO, __VA_ARGS__) + + +#define DO_849(MACRO, ...) \ +MACRO(849, __VA_ARGS__) \ +DO_848(MACRO, __VA_ARGS__) + + +#define DO_850(MACRO, ...) \ +MACRO(850, __VA_ARGS__) \ +DO_849(MACRO, __VA_ARGS__) + + +#define DO_851(MACRO, ...) \ +MACRO(851, __VA_ARGS__) \ +DO_850(MACRO, __VA_ARGS__) + + +#define DO_852(MACRO, ...) \ +MACRO(852, __VA_ARGS__) \ +DO_851(MACRO, __VA_ARGS__) + + +#define DO_853(MACRO, ...) \ +MACRO(853, __VA_ARGS__) \ +DO_852(MACRO, __VA_ARGS__) + + +#define DO_854(MACRO, ...) \ +MACRO(854, __VA_ARGS__) \ +DO_853(MACRO, __VA_ARGS__) + + +#define DO_855(MACRO, ...) \ +MACRO(855, __VA_ARGS__) \ +DO_854(MACRO, __VA_ARGS__) + + +#define DO_856(MACRO, ...) \ +MACRO(856, __VA_ARGS__) \ +DO_855(MACRO, __VA_ARGS__) + + +#define DO_857(MACRO, ...) \ +MACRO(857, __VA_ARGS__) \ +DO_856(MACRO, __VA_ARGS__) + + +#define DO_858(MACRO, ...) \ +MACRO(858, __VA_ARGS__) \ +DO_857(MACRO, __VA_ARGS__) + + +#define DO_859(MACRO, ...) \ +MACRO(859, __VA_ARGS__) \ +DO_858(MACRO, __VA_ARGS__) + + +#define DO_860(MACRO, ...) \ +MACRO(860, __VA_ARGS__) \ +DO_859(MACRO, __VA_ARGS__) + + +#define DO_861(MACRO, ...) \ +MACRO(861, __VA_ARGS__) \ +DO_860(MACRO, __VA_ARGS__) + + +#define DO_862(MACRO, ...) \ +MACRO(862, __VA_ARGS__) \ +DO_861(MACRO, __VA_ARGS__) + + +#define DO_863(MACRO, ...) \ +MACRO(863, __VA_ARGS__) \ +DO_862(MACRO, __VA_ARGS__) + + +#define DO_864(MACRO, ...) \ +MACRO(864, __VA_ARGS__) \ +DO_863(MACRO, __VA_ARGS__) + + +#define DO_865(MACRO, ...) \ +MACRO(865, __VA_ARGS__) \ +DO_864(MACRO, __VA_ARGS__) + + +#define DO_866(MACRO, ...) \ +MACRO(866, __VA_ARGS__) \ +DO_865(MACRO, __VA_ARGS__) + + +#define DO_867(MACRO, ...) \ +MACRO(867, __VA_ARGS__) \ +DO_866(MACRO, __VA_ARGS__) + + +#define DO_868(MACRO, ...) \ +MACRO(868, __VA_ARGS__) \ +DO_867(MACRO, __VA_ARGS__) + + +#define DO_869(MACRO, ...) \ +MACRO(869, __VA_ARGS__) \ +DO_868(MACRO, __VA_ARGS__) + + +#define DO_870(MACRO, ...) \ +MACRO(870, __VA_ARGS__) \ +DO_869(MACRO, __VA_ARGS__) + + +#define DO_871(MACRO, ...) \ +MACRO(871, __VA_ARGS__) \ +DO_870(MACRO, __VA_ARGS__) + + +#define DO_872(MACRO, ...) \ +MACRO(872, __VA_ARGS__) \ +DO_871(MACRO, __VA_ARGS__) + + +#define DO_873(MACRO, ...) \ +MACRO(873, __VA_ARGS__) \ +DO_872(MACRO, __VA_ARGS__) + + +#define DO_874(MACRO, ...) \ +MACRO(874, __VA_ARGS__) \ +DO_873(MACRO, __VA_ARGS__) + + +#define DO_875(MACRO, ...) \ +MACRO(875, __VA_ARGS__) \ +DO_874(MACRO, __VA_ARGS__) + + +#define DO_876(MACRO, ...) \ +MACRO(876, __VA_ARGS__) \ +DO_875(MACRO, __VA_ARGS__) + + +#define DO_877(MACRO, ...) \ +MACRO(877, __VA_ARGS__) \ +DO_876(MACRO, __VA_ARGS__) + + +#define DO_878(MACRO, ...) \ +MACRO(878, __VA_ARGS__) \ +DO_877(MACRO, __VA_ARGS__) + + +#define DO_879(MACRO, ...) \ +MACRO(879, __VA_ARGS__) \ +DO_878(MACRO, __VA_ARGS__) + + +#define DO_880(MACRO, ...) \ +MACRO(880, __VA_ARGS__) \ +DO_879(MACRO, __VA_ARGS__) + + +#define DO_881(MACRO, ...) \ +MACRO(881, __VA_ARGS__) \ +DO_880(MACRO, __VA_ARGS__) + + +#define DO_882(MACRO, ...) \ +MACRO(882, __VA_ARGS__) \ +DO_881(MACRO, __VA_ARGS__) + + +#define DO_883(MACRO, ...) \ +MACRO(883, __VA_ARGS__) \ +DO_882(MACRO, __VA_ARGS__) + + +#define DO_884(MACRO, ...) \ +MACRO(884, __VA_ARGS__) \ +DO_883(MACRO, __VA_ARGS__) + + +#define DO_885(MACRO, ...) \ +MACRO(885, __VA_ARGS__) \ +DO_884(MACRO, __VA_ARGS__) + + +#define DO_886(MACRO, ...) \ +MACRO(886, __VA_ARGS__) \ +DO_885(MACRO, __VA_ARGS__) + + +#define DO_887(MACRO, ...) \ +MACRO(887, __VA_ARGS__) \ +DO_886(MACRO, __VA_ARGS__) + + +#define DO_888(MACRO, ...) \ +MACRO(888, __VA_ARGS__) \ +DO_887(MACRO, __VA_ARGS__) + + +#define DO_889(MACRO, ...) \ +MACRO(889, __VA_ARGS__) \ +DO_888(MACRO, __VA_ARGS__) + + +#define DO_890(MACRO, ...) \ +MACRO(890, __VA_ARGS__) \ +DO_889(MACRO, __VA_ARGS__) + + +#define DO_891(MACRO, ...) \ +MACRO(891, __VA_ARGS__) \ +DO_890(MACRO, __VA_ARGS__) + + +#define DO_892(MACRO, ...) \ +MACRO(892, __VA_ARGS__) \ +DO_891(MACRO, __VA_ARGS__) + + +#define DO_893(MACRO, ...) \ +MACRO(893, __VA_ARGS__) \ +DO_892(MACRO, __VA_ARGS__) + + +#define DO_894(MACRO, ...) \ +MACRO(894, __VA_ARGS__) \ +DO_893(MACRO, __VA_ARGS__) + + +#define DO_895(MACRO, ...) \ +MACRO(895, __VA_ARGS__) \ +DO_894(MACRO, __VA_ARGS__) + + +#define DO_896(MACRO, ...) \ +MACRO(896, __VA_ARGS__) \ +DO_895(MACRO, __VA_ARGS__) + + +#define DO_897(MACRO, ...) \ +MACRO(897, __VA_ARGS__) \ +DO_896(MACRO, __VA_ARGS__) + + +#define DO_898(MACRO, ...) \ +MACRO(898, __VA_ARGS__) \ +DO_897(MACRO, __VA_ARGS__) + + +#define DO_899(MACRO, ...) \ +MACRO(899, __VA_ARGS__) \ +DO_898(MACRO, __VA_ARGS__) + + +#define DO_900(MACRO, ...) \ +MACRO(900, __VA_ARGS__) \ +DO_899(MACRO, __VA_ARGS__) + + +#define DO_901(MACRO, ...) \ +MACRO(901, __VA_ARGS__) \ +DO_900(MACRO, __VA_ARGS__) + + +#define DO_902(MACRO, ...) \ +MACRO(902, __VA_ARGS__) \ +DO_901(MACRO, __VA_ARGS__) + + +#define DO_903(MACRO, ...) \ +MACRO(903, __VA_ARGS__) \ +DO_902(MACRO, __VA_ARGS__) + + +#define DO_904(MACRO, ...) \ +MACRO(904, __VA_ARGS__) \ +DO_903(MACRO, __VA_ARGS__) + + +#define DO_905(MACRO, ...) \ +MACRO(905, __VA_ARGS__) \ +DO_904(MACRO, __VA_ARGS__) + + +#define DO_906(MACRO, ...) \ +MACRO(906, __VA_ARGS__) \ +DO_905(MACRO, __VA_ARGS__) + + +#define DO_907(MACRO, ...) \ +MACRO(907, __VA_ARGS__) \ +DO_906(MACRO, __VA_ARGS__) + + +#define DO_908(MACRO, ...) \ +MACRO(908, __VA_ARGS__) \ +DO_907(MACRO, __VA_ARGS__) + + +#define DO_909(MACRO, ...) \ +MACRO(909, __VA_ARGS__) \ +DO_908(MACRO, __VA_ARGS__) + + +#define DO_910(MACRO, ...) \ +MACRO(910, __VA_ARGS__) \ +DO_909(MACRO, __VA_ARGS__) + + +#define DO_911(MACRO, ...) \ +MACRO(911, __VA_ARGS__) \ +DO_910(MACRO, __VA_ARGS__) + + +#define DO_912(MACRO, ...) \ +MACRO(912, __VA_ARGS__) \ +DO_911(MACRO, __VA_ARGS__) + + +#define DO_913(MACRO, ...) \ +MACRO(913, __VA_ARGS__) \ +DO_912(MACRO, __VA_ARGS__) + + +#define DO_914(MACRO, ...) \ +MACRO(914, __VA_ARGS__) \ +DO_913(MACRO, __VA_ARGS__) + + +#define DO_915(MACRO, ...) \ +MACRO(915, __VA_ARGS__) \ +DO_914(MACRO, __VA_ARGS__) + + +#define DO_916(MACRO, ...) \ +MACRO(916, __VA_ARGS__) \ +DO_915(MACRO, __VA_ARGS__) + + +#define DO_917(MACRO, ...) \ +MACRO(917, __VA_ARGS__) \ +DO_916(MACRO, __VA_ARGS__) + + +#define DO_918(MACRO, ...) \ +MACRO(918, __VA_ARGS__) \ +DO_917(MACRO, __VA_ARGS__) + + +#define DO_919(MACRO, ...) \ +MACRO(919, __VA_ARGS__) \ +DO_918(MACRO, __VA_ARGS__) + + +#define DO_920(MACRO, ...) \ +MACRO(920, __VA_ARGS__) \ +DO_919(MACRO, __VA_ARGS__) + + +#define DO_921(MACRO, ...) \ +MACRO(921, __VA_ARGS__) \ +DO_920(MACRO, __VA_ARGS__) + + +#define DO_922(MACRO, ...) \ +MACRO(922, __VA_ARGS__) \ +DO_921(MACRO, __VA_ARGS__) + + +#define DO_923(MACRO, ...) \ +MACRO(923, __VA_ARGS__) \ +DO_922(MACRO, __VA_ARGS__) + + +#define DO_924(MACRO, ...) \ +MACRO(924, __VA_ARGS__) \ +DO_923(MACRO, __VA_ARGS__) + + +#define DO_925(MACRO, ...) \ +MACRO(925, __VA_ARGS__) \ +DO_924(MACRO, __VA_ARGS__) + + +#define DO_926(MACRO, ...) \ +MACRO(926, __VA_ARGS__) \ +DO_925(MACRO, __VA_ARGS__) + + +#define DO_927(MACRO, ...) \ +MACRO(927, __VA_ARGS__) \ +DO_926(MACRO, __VA_ARGS__) + + +#define DO_928(MACRO, ...) \ +MACRO(928, __VA_ARGS__) \ +DO_927(MACRO, __VA_ARGS__) + + +#define DO_929(MACRO, ...) \ +MACRO(929, __VA_ARGS__) \ +DO_928(MACRO, __VA_ARGS__) + + +#define DO_930(MACRO, ...) \ +MACRO(930, __VA_ARGS__) \ +DO_929(MACRO, __VA_ARGS__) + + +#define DO_931(MACRO, ...) \ +MACRO(931, __VA_ARGS__) \ +DO_930(MACRO, __VA_ARGS__) + + +#define DO_932(MACRO, ...) \ +MACRO(932, __VA_ARGS__) \ +DO_931(MACRO, __VA_ARGS__) + + +#define DO_933(MACRO, ...) \ +MACRO(933, __VA_ARGS__) \ +DO_932(MACRO, __VA_ARGS__) + + +#define DO_934(MACRO, ...) \ +MACRO(934, __VA_ARGS__) \ +DO_933(MACRO, __VA_ARGS__) + + +#define DO_935(MACRO, ...) \ +MACRO(935, __VA_ARGS__) \ +DO_934(MACRO, __VA_ARGS__) + + +#define DO_936(MACRO, ...) \ +MACRO(936, __VA_ARGS__) \ +DO_935(MACRO, __VA_ARGS__) + + +#define DO_937(MACRO, ...) \ +MACRO(937, __VA_ARGS__) \ +DO_936(MACRO, __VA_ARGS__) + + +#define DO_938(MACRO, ...) \ +MACRO(938, __VA_ARGS__) \ +DO_937(MACRO, __VA_ARGS__) + + +#define DO_939(MACRO, ...) \ +MACRO(939, __VA_ARGS__) \ +DO_938(MACRO, __VA_ARGS__) + + +#define DO_940(MACRO, ...) \ +MACRO(940, __VA_ARGS__) \ +DO_939(MACRO, __VA_ARGS__) + + +#define DO_941(MACRO, ...) \ +MACRO(941, __VA_ARGS__) \ +DO_940(MACRO, __VA_ARGS__) + + +#define DO_942(MACRO, ...) \ +MACRO(942, __VA_ARGS__) \ +DO_941(MACRO, __VA_ARGS__) + + +#define DO_943(MACRO, ...) \ +MACRO(943, __VA_ARGS__) \ +DO_942(MACRO, __VA_ARGS__) + + +#define DO_944(MACRO, ...) \ +MACRO(944, __VA_ARGS__) \ +DO_943(MACRO, __VA_ARGS__) + + +#define DO_945(MACRO, ...) \ +MACRO(945, __VA_ARGS__) \ +DO_944(MACRO, __VA_ARGS__) + + +#define DO_946(MACRO, ...) \ +MACRO(946, __VA_ARGS__) \ +DO_945(MACRO, __VA_ARGS__) + + +#define DO_947(MACRO, ...) \ +MACRO(947, __VA_ARGS__) \ +DO_946(MACRO, __VA_ARGS__) + + +#define DO_948(MACRO, ...) \ +MACRO(948, __VA_ARGS__) \ +DO_947(MACRO, __VA_ARGS__) + + +#define DO_949(MACRO, ...) \ +MACRO(949, __VA_ARGS__) \ +DO_948(MACRO, __VA_ARGS__) + + +#define DO_950(MACRO, ...) \ +MACRO(950, __VA_ARGS__) \ +DO_949(MACRO, __VA_ARGS__) + + +#define DO_951(MACRO, ...) \ +MACRO(951, __VA_ARGS__) \ +DO_950(MACRO, __VA_ARGS__) + + +#define DO_952(MACRO, ...) \ +MACRO(952, __VA_ARGS__) \ +DO_951(MACRO, __VA_ARGS__) + + +#define DO_953(MACRO, ...) \ +MACRO(953, __VA_ARGS__) \ +DO_952(MACRO, __VA_ARGS__) + + +#define DO_954(MACRO, ...) \ +MACRO(954, __VA_ARGS__) \ +DO_953(MACRO, __VA_ARGS__) + + +#define DO_955(MACRO, ...) \ +MACRO(955, __VA_ARGS__) \ +DO_954(MACRO, __VA_ARGS__) + + +#define DO_956(MACRO, ...) \ +MACRO(956, __VA_ARGS__) \ +DO_955(MACRO, __VA_ARGS__) + + +#define DO_957(MACRO, ...) \ +MACRO(957, __VA_ARGS__) \ +DO_956(MACRO, __VA_ARGS__) + + +#define DO_958(MACRO, ...) \ +MACRO(958, __VA_ARGS__) \ +DO_957(MACRO, __VA_ARGS__) + + +#define DO_959(MACRO, ...) \ +MACRO(959, __VA_ARGS__) \ +DO_958(MACRO, __VA_ARGS__) + + +#define DO_960(MACRO, ...) \ +MACRO(960, __VA_ARGS__) \ +DO_959(MACRO, __VA_ARGS__) + + +#define DO_961(MACRO, ...) \ +MACRO(961, __VA_ARGS__) \ +DO_960(MACRO, __VA_ARGS__) + + +#define DO_962(MACRO, ...) \ +MACRO(962, __VA_ARGS__) \ +DO_961(MACRO, __VA_ARGS__) + + +#define DO_963(MACRO, ...) \ +MACRO(963, __VA_ARGS__) \ +DO_962(MACRO, __VA_ARGS__) + + +#define DO_964(MACRO, ...) \ +MACRO(964, __VA_ARGS__) \ +DO_963(MACRO, __VA_ARGS__) + + +#define DO_965(MACRO, ...) \ +MACRO(965, __VA_ARGS__) \ +DO_964(MACRO, __VA_ARGS__) + + +#define DO_966(MACRO, ...) \ +MACRO(966, __VA_ARGS__) \ +DO_965(MACRO, __VA_ARGS__) + + +#define DO_967(MACRO, ...) \ +MACRO(967, __VA_ARGS__) \ +DO_966(MACRO, __VA_ARGS__) + + +#define DO_968(MACRO, ...) \ +MACRO(968, __VA_ARGS__) \ +DO_967(MACRO, __VA_ARGS__) + + +#define DO_969(MACRO, ...) \ +MACRO(969, __VA_ARGS__) \ +DO_968(MACRO, __VA_ARGS__) + + +#define DO_970(MACRO, ...) \ +MACRO(970, __VA_ARGS__) \ +DO_969(MACRO, __VA_ARGS__) + + +#define DO_971(MACRO, ...) \ +MACRO(971, __VA_ARGS__) \ +DO_970(MACRO, __VA_ARGS__) + + +#define DO_972(MACRO, ...) \ +MACRO(972, __VA_ARGS__) \ +DO_971(MACRO, __VA_ARGS__) + + +#define DO_973(MACRO, ...) \ +MACRO(973, __VA_ARGS__) \ +DO_972(MACRO, __VA_ARGS__) + + +#define DO_974(MACRO, ...) \ +MACRO(974, __VA_ARGS__) \ +DO_973(MACRO, __VA_ARGS__) + + +#define DO_975(MACRO, ...) \ +MACRO(975, __VA_ARGS__) \ +DO_974(MACRO, __VA_ARGS__) + + +#define DO_976(MACRO, ...) \ +MACRO(976, __VA_ARGS__) \ +DO_975(MACRO, __VA_ARGS__) + + +#define DO_977(MACRO, ...) \ +MACRO(977, __VA_ARGS__) \ +DO_976(MACRO, __VA_ARGS__) + + +#define DO_978(MACRO, ...) \ +MACRO(978, __VA_ARGS__) \ +DO_977(MACRO, __VA_ARGS__) + + +#define DO_979(MACRO, ...) \ +MACRO(979, __VA_ARGS__) \ +DO_978(MACRO, __VA_ARGS__) + + +#define DO_980(MACRO, ...) \ +MACRO(980, __VA_ARGS__) \ +DO_979(MACRO, __VA_ARGS__) + + +#define DO_981(MACRO, ...) \ +MACRO(981, __VA_ARGS__) \ +DO_980(MACRO, __VA_ARGS__) + + +#define DO_982(MACRO, ...) \ +MACRO(982, __VA_ARGS__) \ +DO_981(MACRO, __VA_ARGS__) + + +#define DO_983(MACRO, ...) \ +MACRO(983, __VA_ARGS__) \ +DO_982(MACRO, __VA_ARGS__) + + +#define DO_984(MACRO, ...) \ +MACRO(984, __VA_ARGS__) \ +DO_983(MACRO, __VA_ARGS__) + + +#define DO_985(MACRO, ...) \ +MACRO(985, __VA_ARGS__) \ +DO_984(MACRO, __VA_ARGS__) + + +#define DO_986(MACRO, ...) \ +MACRO(986, __VA_ARGS__) \ +DO_985(MACRO, __VA_ARGS__) + + +#define DO_987(MACRO, ...) \ +MACRO(987, __VA_ARGS__) \ +DO_986(MACRO, __VA_ARGS__) + + +#define DO_988(MACRO, ...) \ +MACRO(988, __VA_ARGS__) \ +DO_987(MACRO, __VA_ARGS__) + + +#define DO_989(MACRO, ...) \ +MACRO(989, __VA_ARGS__) \ +DO_988(MACRO, __VA_ARGS__) + + +#define DO_990(MACRO, ...) \ +MACRO(990, __VA_ARGS__) \ +DO_989(MACRO, __VA_ARGS__) + + +#define DO_991(MACRO, ...) \ +MACRO(991, __VA_ARGS__) \ +DO_990(MACRO, __VA_ARGS__) + + +#define DO_992(MACRO, ...) \ +MACRO(992, __VA_ARGS__) \ +DO_991(MACRO, __VA_ARGS__) + + +#define DO_993(MACRO, ...) \ +MACRO(993, __VA_ARGS__) \ +DO_992(MACRO, __VA_ARGS__) + + +#define DO_994(MACRO, ...) \ +MACRO(994, __VA_ARGS__) \ +DO_993(MACRO, __VA_ARGS__) + + +#define DO_995(MACRO, ...) \ +MACRO(995, __VA_ARGS__) \ +DO_994(MACRO, __VA_ARGS__) + + +#define DO_996(MACRO, ...) \ +MACRO(996, __VA_ARGS__) \ +DO_995(MACRO, __VA_ARGS__) + + +#define DO_997(MACRO, ...) \ +MACRO(997, __VA_ARGS__) \ +DO_996(MACRO, __VA_ARGS__) + + +#define DO_998(MACRO, ...) \ +MACRO(998, __VA_ARGS__) \ +DO_997(MACRO, __VA_ARGS__) + + +#define DO_999(MACRO, ...) \ +MACRO(999, __VA_ARGS__) \ +DO_998(MACRO, __VA_ARGS__) + + +#define DO_1000(MACRO, ...) \ +MACRO(1000, __VA_ARGS__) \ +DO_999(MACRO, __VA_ARGS__) + + +#define DO_1001(MACRO, ...) \ +MACRO(1001, __VA_ARGS__) \ +DO_1000(MACRO, __VA_ARGS__) + + +#define DO_1002(MACRO, ...) \ +MACRO(1002, __VA_ARGS__) \ +DO_1001(MACRO, __VA_ARGS__) + + +#define DO_1003(MACRO, ...) \ +MACRO(1003, __VA_ARGS__) \ +DO_1002(MACRO, __VA_ARGS__) + + +#define DO_1004(MACRO, ...) \ +MACRO(1004, __VA_ARGS__) \ +DO_1003(MACRO, __VA_ARGS__) + + +#define DO_1005(MACRO, ...) \ +MACRO(1005, __VA_ARGS__) \ +DO_1004(MACRO, __VA_ARGS__) + + +#define DO_1006(MACRO, ...) \ +MACRO(1006, __VA_ARGS__) \ +DO_1005(MACRO, __VA_ARGS__) + + +#define DO_1007(MACRO, ...) \ +MACRO(1007, __VA_ARGS__) \ +DO_1006(MACRO, __VA_ARGS__) + + +#define DO_1008(MACRO, ...) \ +MACRO(1008, __VA_ARGS__) \ +DO_1007(MACRO, __VA_ARGS__) + + +#define DO_1009(MACRO, ...) \ +MACRO(1009, __VA_ARGS__) \ +DO_1008(MACRO, __VA_ARGS__) + + +#define DO_1010(MACRO, ...) \ +MACRO(1010, __VA_ARGS__) \ +DO_1009(MACRO, __VA_ARGS__) + + +#define DO_1011(MACRO, ...) \ +MACRO(1011, __VA_ARGS__) \ +DO_1010(MACRO, __VA_ARGS__) + + +#define DO_1012(MACRO, ...) \ +MACRO(1012, __VA_ARGS__) \ +DO_1011(MACRO, __VA_ARGS__) + + +#define DO_1013(MACRO, ...) \ +MACRO(1013, __VA_ARGS__) \ +DO_1012(MACRO, __VA_ARGS__) + + +#define DO_1014(MACRO, ...) \ +MACRO(1014, __VA_ARGS__) \ +DO_1013(MACRO, __VA_ARGS__) + + +#define DO_1015(MACRO, ...) \ +MACRO(1015, __VA_ARGS__) \ +DO_1014(MACRO, __VA_ARGS__) + + +#define DO_1016(MACRO, ...) \ +MACRO(1016, __VA_ARGS__) \ +DO_1015(MACRO, __VA_ARGS__) + + +#define DO_1017(MACRO, ...) \ +MACRO(1017, __VA_ARGS__) \ +DO_1016(MACRO, __VA_ARGS__) + + +#define DO_1018(MACRO, ...) \ +MACRO(1018, __VA_ARGS__) \ +DO_1017(MACRO, __VA_ARGS__) + + +#define DO_1019(MACRO, ...) \ +MACRO(1019, __VA_ARGS__) \ +DO_1018(MACRO, __VA_ARGS__) + + +#define DO_1020(MACRO, ...) \ +MACRO(1020, __VA_ARGS__) \ +DO_1019(MACRO, __VA_ARGS__) + + +#define DO_1021(MACRO, ...) \ +MACRO(1021, __VA_ARGS__) \ +DO_1020(MACRO, __VA_ARGS__) + + +#define DO_1022(MACRO, ...) \ +MACRO(1022, __VA_ARGS__) \ +DO_1021(MACRO, __VA_ARGS__) + + +#define DO_1023(MACRO, ...) \ +MACRO(1023, __VA_ARGS__) \ +DO_1022(MACRO, __VA_ARGS__) + + +#define DO_1024(MACRO, ...) \ +MACRO(1024, __VA_ARGS__) \ +DO_1023(MACRO, __VA_ARGS__) + + + +#define DO(TIMES, MACRO, ...) C2(DO_, TIMES)(MACRO, __VA_ARGS__) + + +/* we need some sort of macro that does: +IF(0, "true", "false") => "false" +IF(1, "true", "false") => "true" +IF(X, "true", "false") => "true" +*/ + +#define INTERNALIF(x) INTERNALIF##x +#define INTERNALIF0 + +#define ISZERO(x) COUNT_ARG(INTERNALIF(x)) + +#define IF(condition, trueBranch, falseBranch) C2(IF,ISZERO(condition))(trueBranch, falseBranch) +#define IF0(trueBranch, falseBranch) falseBranch +#define IF1(trueBranch, falseBranch) trueBranch + + + +#define DEFINE_ENUMERATION_CONSTANT(x) x, +/*DEFINE_ENUM goes to header*/ +#define DEFINE_ENUM(enumName, ...) typedef enum C2(enumName, _TAG) { FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; \ + extern const char* C2(enumName,Strings)(enumName value); \ + extern int C2(enumName, _FromString)(const char* enumAsString, enumName* destination); + + +#define DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING(x) C2(L, TOSTRING(x)) , +#define DEFINE_ENUMERATION_CONSTANT_AS_STRING(x) TOSTRING(x) , +/*DEFINE_ENUM_STRINGS goes to .c*/ +#define DEFINE_ENUM_STRINGS(enumName, ...) const char* C2(enumName, StringStorage)[COUNT_ARG(__VA_ARGS__)] = {FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_STRING, __VA_ARGS__)}; \ +const char* C2(enumName,Strings)(enumName value) \ +{ \ + if((int)value<0 || (int)value>=COUNT_ARG(__VA_ARGS__)) \ + { \ + /*this is an error case*/ \ + return NULL; \ + } \ + else \ + { \ + return C2(enumName, StringStorage)[value]; \ + } \ +} \ +int C2(enumName, _FromString)(const char* enumAsString, enumName* destination) \ +{ \ + if( \ + (enumAsString==NULL) || (destination==NULL) \ + ) \ + { \ + return __FAILURE__; \ + } \ + else \ + { \ + size_t i; \ + for(i=0;i<COUNT_ARG(__VA_ARGS__);i++) \ + { \ + if(strcmp(enumAsString, C2(enumName, StringStorage)[i])==0) \ + { \ + *destination = (enumName)i; \ + return 0; \ + } \ + } \ + return __FAILURE__; \ + } \ +} \ + +#define ENUM_TO_STRING(enumName, enumValue) C2(enumName, Strings)(enumValue) +#define STRING_TO_ENUM(stringValue, enumName, addressOfEnumVariable) C2(enumName, _FromString)(stringValue, addressOfEnumVariable) + +#define DEFINE_MICROMOCK_ENUM_TO_STRING(type, ...) MICROMOCK_ENUM_TO_STRING(type, FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING, __VA_ARGS__)); + +#define EMPTY() +#define MACRO_UTILS_DELAY(id) id EMPTY LPAREN ) + +#endif /*MACRO_UTILS_H*/ + +// This portion of macro_utils.h is designed to be included multiple times to +// redefine DEFINE_LOCAL_ENUM in situations where ENUM_TO_STRING is never called +// thus avoiding the following warning +// +// warning: function "XXXXXXStrings" was declared but never referenced +// +// For Example, iothubtransport_amqp_common.c defines AMQP_TRANSPORT_STATE +// but never calls ENUM_TO_STRING. To silence this warning you would do the +// following: +// +// // Suppress unused function warning for AMQP_TRANSPORT_STATEstrings +// #define ENUM_TO_STRING_UNUSED +// #include "azure_c_shared_utility/macro_utils.h" +// +// DEFINE_LOCAL_ENUM(AMQP_TRANSPORT_STATE, AMQP_TRANSPORT_STATE_STRINGS); +// + +#ifdef DEFINE_LOCAL_ENUM +#undef DEFINE_LOCAL_ENUM +#endif + +#ifndef ENUM_TO_STRING_UNUSED + +#define DEFINE_LOCAL_ENUM(enumName, ...) typedef enum C2(enumName, _TAG) { FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; \ +static const char* C2(enumName, StringStorage)[COUNT_ARG(__VA_ARGS__)] = {FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_STRING, __VA_ARGS__)}; \ +static const char* C2(enumName,Strings)(enumName value) \ +{ \ + if((int)value<0 || (int)value>=COUNT_ARG(__VA_ARGS__)) \ + { \ + /*this is an error case*/ \ + return NULL; \ + } \ + else \ + { \ + return C2(enumName, StringStorage)[value]; \ + } \ +} + +#define ENUM_TO_STRING(enumName, enumValue) C2(enumName, Strings)(enumValue) +#define STRING_TO_ENUM(stringValue, enumName, addressOfEnumVariable) C2(enumName, _FromString)(stringValue, addressOfEnumVariable) + +#define DEFINE_MICROMOCK_ENUM_TO_STRING(type, ...) MICROMOCK_ENUM_TO_STRING(type, FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING, __VA_ARGS__)); + +#define EMPTY() +#define DELAY(id) id EMPTY LPAREN ) + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/map.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file map.h +* @brief Map is a module that implements a dictionary of @c STRING_HANDLE +* keys to @c STRING_HANDLE values. +*/ + +#ifndef MAP_H +#define MAP_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#define MAP_RESULT_VALUES \ + MAP_OK, \ + MAP_ERROR, \ + MAP_INVALIDARG, \ + MAP_KEYEXISTS, \ + MAP_KEYNOTFOUND, \ + MAP_FILTER_REJECT + +/** @brief Enumeration specifying the status of calls to various APIs in this + * module. + */ +DEFINE_ENUM(MAP_RESULT, MAP_RESULT_VALUES); + +typedef struct MAP_HANDLE_DATA_TAG* MAP_HANDLE; + +typedef int (*MAP_FILTER_CALLBACK)(const char* mapProperty, const char* mapValue); + +/** + * @brief Creates a new, empty map. + * + * @param mapFilterFunc A callback function supplied by the caller that is + * invoked during calls to ::Map_Add and + * ::Map_AddOrUpdate to provide the caller an + * opportunity to indicate whether the new entry or + * the update to an existing entry can be made or not. + * The callback function can request that the operation + * be canceled by returning a non-zero value from the + * callback. + * + * @return A valid @c MAP_HANDLE or @c NULL in case an error occurs. + */ +MOCKABLE_FUNCTION(, MAP_HANDLE, Map_Create, MAP_FILTER_CALLBACK, mapFilterFunc); + +/** + * @brief Release all resources associated with the map. + * + * @param handle The handle to an existing map. + */ +MOCKABLE_FUNCTION(, void, Map_Destroy, MAP_HANDLE, handle); + +/** + * @brief Creates a copy of the map indicated by @p handle and returns a + * handle to it. + * + * @param handle The handle to an existing map. + * + * @return A valid @c MAP_HANDLE to the cloned copy of the map or @c NULL + * in case an error occurs. + */ +MOCKABLE_FUNCTION(, MAP_HANDLE, Map_Clone, MAP_HANDLE, handle); + +/** + * @brief Adds a key/value pair to the map. + * + * @param handle The handle to an existing map. + * @param key The @c key to be used for this map entry. + * @param value The @c value to be associated with @p key. + * + * If a non-NULL pointer to a callback function was supplied + * via the @c mapFilterFunc parameter when ::Map_Create was + * called then that callback is invoked when a new entry is + * added and if the callback returns a non-zero value then + * the function cancels the add operation and returns + * @c MAP_FILTER_REJECT. + * + * @return If any of the input parameters are @c NULL then this function + * returns @c MAP_INVALID_ARG. If the key already exists in the + * map then @c MAP_KEYEXISTS is returned. If the filter function + * associated with the map rejects the entry then + * @c MAP_FILTER_REJECT is returned. In case an error occurs when + * the new key is added to the map the function returns @c MAP_ERROR. + * If everything goes well then @c MAP_OK is returned. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_Add, MAP_HANDLE, handle, const char*, key, const char*, value); + +/** + * @brief Adds/updates a key/value pair to the map. + * + * @param handle The handle to an existing map. + * @param key The @c key to be used for this map entry. + * @param value The @c value to be associated with @p key. + * + * This function behaves exactly like ::Map_Add except that if the key + * already exists in the map then it overwrites the value with the + * supplied value instead of returning an error. If a non-NULL pointer + * to a callback function was supplied via the @c mapFilterFunc parameter + * when ::Map_Create was called then that callback is invoked when a new + * entry is added or when an existing entry is updated and if the + * callback returns a non-zero value then the function cancels the + * add/update operation and returns @c MAP_FILTER_REJECT. + * + * @return If any of the input parameters are @c NULL then this function + * returns @c MAP_INVALID_ARG. If the filter function associated + * with the map rejects the entry then @c MAP_FILTER_REJECT is + * returned. In case an error occurs when the new key is + * added/updated in the map the function returns @c MAP_ERROR. If + * everything goes well then @c MAP_OK is returned. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value); + +/** + * @brief Removes a key and its associated value from the map. + * + * @param handle The handle to an existing map. + * @param key The @c key of the item to be deleted. + * + * @return Returns @c MAP_OK if the key was deleted successfully or an + * error code otherwise. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_Delete, MAP_HANDLE, handle, const char*, key); + +/** + * @brief This function returns a boolean value in @p keyExists if the map + * contains a key with the same value the parameter @p key. + * + * @param handle The handle to an existing map. + * @param key The key that the caller wants checked. + * @param keyExists The function writes @c true at the address pointed at + * by this parameter if the key exists in the map and + * @c false otherwise. + * + * @return Returns @c MAP_OK if the check was performed successfully or an + * error code otherwise. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_ContainsKey, MAP_HANDLE, handle, const char*, key, bool*, keyExists); + +/** + * @brief This function returns @c true in @p valueExists if at + * least one <key,value> pair exists in the map where the entry's + * value is equal to the parameter @c value. + * + * @param handle The handle to an existing map. + * @param value The value that the caller wants checked. + * @param valueExists The function writes @c true at the address pointed at + * by this parameter if the value exists in the map and + * @c false otherwise. + * + * @return Returns @c MAP_OK if the check was performed successfully or an + * error code otherwise. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_ContainsValue, MAP_HANDLE, handle, const char*, value, bool*, valueExists); + +/** + * @brief Retrieves the value of a stored key. + * + * @param handle The handle to an existing map. + * @param key The key to be looked up in the map. + * + * @return Returns @c NULL in case the input arguments are @c NULL or if the + * requested key is not found in the map. Returns a pointer to the + * key's value otherwise. + */ +MOCKABLE_FUNCTION(, const char*, Map_GetValueFromKey, MAP_HANDLE, handle, const char*, key); + +/** + * @brief Retrieves the complete list of keys and values from the map + * in @p values and @p keys. Also writes the size of the list + * in @p count. + * + * @param handle The handle to an existing map. + * @param keys The location where the list of keys is to be written. + * @param values The location where the list of values is to be written. + * @param count The number of stored keys and values is written at the + * location indicated by this pointer. + * + * @return Returns @c MAP_OK if the keys and values are retrieved and written + * successfully or an error code otherwise. + */ +MOCKABLE_FUNCTION(, MAP_RESULT, Map_GetInternals, MAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count); + +/*this API creates a JSON object from the content of the map*/ +MOCKABLE_FUNCTION(, STRING_HANDLE, Map_ToJSON, MAP_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* MAP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/optimize_size.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef OPTIMIZE_SIZE_H +#define OPTIMIZE_SIZE_H + +#if (defined __MSP430FR5969__) + #define OPTIMIZE_RETURN_CODES + #define NO_VERBOSE_OUTPUT + #ifdef DEBUG + #define MINIMAL_LOGERROR + #else + #define NO_LOGGING + #endif +#endif + +#if (defined OPTIMIZE_RETURN_CODES) + #define __FAILURE__ 1 +#else + #define __FAILURE__ __LINE__ +#endif + +#endif // OPTIMIZE_SIZE_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/optionhandler.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef OPTIONHANDLER_H +#define OPTIONHANDLER_H + +#include "azure_c_shared_utility/macro_utils.h" + +#define OPTIONHANDLER_RESULT_VALUES \ +OPTIONHANDLER_OK, \ +OPTIONHANDLER_ERROR, \ +OPTIONHANDLER_INVALIDARG + +DEFINE_ENUM(OPTIONHANDLER_RESULT, OPTIONHANDLER_RESULT_VALUES) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct OPTIONHANDLER_HANDLE_DATA_TAG* OPTIONHANDLER_HANDLE; + +/*the following function pointer points to a function that produces a clone of the option specified by name and value (that is, a clone of void* value)*/ +/*returns NULL if it failed to produce a clone, otherwise returns a non-NULL value*/ +/*to be implemented by every module*/ +typedef void* (*pfCloneOption)(const char* name, const void* value); + +/*the following function pointer points to a function that frees resources allocated for an option*/ +/*to be implemented by every module*/ +typedef void (*pfDestroyOption)(const char* name, const void* value); + +/*the following function pointer points to a function that sets an option for a module*/ +/*to be implemented by every module*/ +/*returns 0 if _SetOption succeeded, any other value is error, if the option is not intended for that module, returns 0*/ +typedef int (*pfSetOption)(void* handle, const char* name, const void* value); + +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, OptionHandler_Create, pfCloneOption, cloneOption, pfDestroyOption, destroyOption, pfSetOption, setOption); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, OptionHandler_Clone, OPTIONHANDLER_HANDLE, handler); +MOCKABLE_FUNCTION(, OPTIONHANDLER_RESULT, OptionHandler_AddOption, OPTIONHANDLER_HANDLE, handle, const char*, name, const void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_RESULT, OptionHandler_FeedOptions, OPTIONHANDLER_HANDLE, handle, void*, destinationHandle); +MOCKABLE_FUNCTION(, void, OptionHandler_Destroy, OPTIONHANDLER_HANDLE, handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*OPTIONHANDLER*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/platform.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + MOCKABLE_FUNCTION(, int, platform_init); + MOCKABLE_FUNCTION(, void, platform_deinit); + MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, platform_get_default_tlsio); + MOCKABLE_FUNCTION(, STRING_HANDLE, platform_get_platform_info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PLATFORM_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/refcount.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +/*this header contains macros for ref_counting a variable. + +There are no upper bound checks related to uint32_t overflow because we expect that bigger issues are in +the system when more than 4 billion references exist to the same variable. In the case when such an overflow +occurs, the object's ref count will reach zero (while still having 0xFFFFFFFF references) and likely the +controlling code will take the decision to free the object's resources. Then, any of the 0xFFFFFFFF references +will interact with deallocated memory / resources resulting in an undefined behavior. +*/ + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstdlib> +#include <cstdint> +extern "C" +{ +#else +#include <stdlib.h> +#include <stdint.h> +#endif + +// Include the platform-specific file that defines atomic functionality +#include "refcount_os.h" + +#define REFCOUNT_TYPE(type) \ +struct C2(C2(REFCOUNT_, type), _TAG) + +#define REFCOUNT_SHORT_TYPE(type) \ +C2(REFCOUNT_, type) + +#define REFCOUNT_TYPE_DECLARE_CREATE(type) C2(REFCOUNT_SHORT_TYPE(type), _Create) +#define REFCOUNT_TYPE_CREATE(type) C2(REFCOUNT_SHORT_TYPE(type), _Create)() + +/*this introduces a new refcount'd type based on another type */ +/*and an initializer for that new type that also sets the ref count to 1. The type must not have a flexible array*/ +/*the newly allocated memory shall be free'd by free()*/ +/*and the ref counting is handled internally by the type in the _Create/ _Clone /_Destroy functions */ + +#define DEFINE_REFCOUNT_TYPE(type) \ +REFCOUNT_TYPE(type) \ +{ \ + type counted; \ + COUNT_TYPE count; \ +}; \ +static type* REFCOUNT_TYPE_DECLARE_CREATE(type) (void) \ +{ \ + REFCOUNT_TYPE(type)* result = (REFCOUNT_TYPE(type)*)malloc(sizeof(REFCOUNT_TYPE(type))); \ + if (result != NULL) \ + { \ + result->count = 1; \ + } \ + return (type*)result; \ +} \ + +#ifndef DEC_RETURN_ZERO +#error refcount_os.h does not define DEC_RETURN_ZERO +#endif // !DEC_RETURN_ZERO +#ifndef INC_REF +#error refcount_os.h does not define INC_REF +#endif // !INC_REF +#ifndef DEC_REF +#error refcount_os.h does not define DEC_REF +#endif // !DEC_REF +#ifndef INIT_REF +#error refcount_os.h does not define INIT_REF +#endif // !INIT_REF + +#ifdef __cplusplus +} +#endif + +#endif /*REFCOUNT_H*/ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/sastoken.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASTOKEN_H +#define SASTOKEN_H + +#include "azure_c_shared_utility/strings.h" +#include <stdbool.h> +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + + MOCKABLE_FUNCTION(, bool, SASToken_Validate, STRING_HANDLE, sasToken); + MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_Create, STRING_HANDLE, key, STRING_HANDLE, scope, STRING_HANDLE, keyName, size_t, expiry); + MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_CreateString, const char*, key, const char*, scope, const char*, keyName, size_t, expiry); + +#ifdef __cplusplus +} +#endif + +#endif /* SASTOKEN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/sha-private.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*************************** sha-private.h ***************************/ +/********************** See RFC 4634 for details *********************/ +#ifndef _SHA_PRIVATE__H +#define _SHA_PRIVATE__H +/* +* These definitions are defined in FIPS-180-2, section 4.1. +* Ch() and Maj() are defined identically in sections 4.1.1, +* 4.1.2 and 4.1.3. +* +* The definitions used in FIPS-180-2 are as follows: +*/ + +#ifndef USE_MODIFIED_MACROS +#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#else /* USE_MODIFIED_MACROS */ +/* +* The following definitions are equivalent and potentially faster. +*/ + +#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) +#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) + +#endif /* USE_MODIFIED_MACROS */ + +#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) + +#endif /* _SHA_PRIVATE__H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/sha.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,277 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/**************************** sha.h ****************************/ +/******************* See RFC 4634 for details ******************/ +#ifndef _SHA_H_ +#define _SHA_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * Description: + * This file implements the Secure Hash Signature Standard + * algorithms as defined in the National Institute of Standards + * and Technology Federal Information Processing Standards + * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 + * published on August 1, 2002, and the FIPS PUB 180-2 Change + * Notice published on February 28, 2004. + * + * A combined document showing all algorithms is available at + * http://csrc.nist.gov/publications/fips/ + * fips180-2/fips180-2withchangenotice.pdf + * + * The five hashes are defined in these sizes: + * SHA-1 20 byte / 160 bit + * SHA-224 28 byte / 224 bit + * SHA-256 32 byte / 256 bit + * SHA-384 48 byte / 384 bit + * SHA-512 64 byte / 512 bit + */ + +#include <stdint.h> +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typedef the following: + * name meaning + * uint64_t unsigned 64 bit integer + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +/* + * All SHA functions return one of these values. + */ +enum { + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError, /* called Input after FinalBits or Result */ + shaBadParam /* passed a bad parameter */ +}; +#endif /* _SHA_enum_ */ + +/* + * These constants hold size information for each of the SHA + * hashing operations + */ +enum { + SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64, + SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128, + SHA512_Message_Block_Size = 128, + USHA_Max_Message_Block_Size = SHA512_Message_Block_Size, + + SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32, + SHA384HashSize = 48, SHA512HashSize = 64, + USHAMaxHashSize = SHA512HashSize, + + SHA1HashSizeBits = 160, SHA224HashSizeBits = 224, + SHA256HashSizeBits = 256, SHA384HashSizeBits = 384, + SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits +}; + +/* + * These constants are used in the USHA (unified sha) functions. + */ +typedef enum SHAversion { + SHA1, SHA224, SHA256, SHA384, SHA512 +} SHAversion; + +/* + * This structure will hold context information for the SHA-1 + * hashing operation. + */ +typedef struct SHA1Context { + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA1_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA1Context; + +/* + * This structure will hold context information for the SHA-256 + * hashing operation. + */ +typedef struct SHA256Context { + uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA256_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA256Context; + +/* + * This structure will hold context information for the SHA-512 + * hashing operation. + */ +typedef struct SHA512Context { +#ifdef USE_32BIT_ONLY + uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */ + uint32_t Length[4]; /* Message length in bits */ +#else /* !USE_32BIT_ONLY */ + uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */ + uint64_t Length_Low, Length_High; /* Message length in bits */ +#endif /* USE_32BIT_ONLY */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 1024-bit message blocks */ + uint8_t Message_Block[SHA512_Message_Block_Size]; + + int Computed; /* Is the digest computed?*/ + int Corrupted; /* Is the digest corrupted? */ +} SHA512Context; + +/* + * This structure will hold context information for the SHA-224 + * hashing operation. It uses the SHA-256 structure for computation. + */ +typedef struct SHA256Context SHA224Context; + +/* + * This structure will hold context information for the SHA-384 + * hashing operation. It uses the SHA-512 structure for computation. + */ +typedef struct SHA512Context SHA384Context; + +/* + * This structure holds context information for all SHA + * hashing operations. + */ +typedef struct USHAContext { + int whichSha; /* which SHA is being used */ + union { + SHA1Context sha1Context; + SHA224Context sha224Context; SHA256Context sha256Context; + SHA384Context sha384Context; SHA512Context sha512Context; + } ctx; +} USHAContext; + +/* + * This structure will hold context information for the HMAC + * keyed hashing operation. + */ +typedef struct HMACContext { + int whichSha; /* which SHA is being used */ + int hashSize; /* hash size of SHA being used */ + int blockSize; /* block size of SHA being used */ + USHAContext shaContext; /* SHA context */ + unsigned char k_opad[USHA_Max_Message_Block_Size]; + /* outer padding - key XORd with opad */ +} HMACContext; + + +/* + * Function Prototypes + */ + +/* SHA-1 */ +extern int SHA1Reset(SHA1Context *); +extern int SHA1Input(SHA1Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA1FinalBits(SHA1Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA1Result(SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +/* SHA-224 */ +extern int SHA224Reset(SHA224Context *); +extern int SHA224Input(SHA224Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA224FinalBits(SHA224Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA224Result(SHA224Context *, + uint8_t Message_Digest[SHA224HashSize]); + +/* SHA-256 */ +extern int SHA256Reset(SHA256Context *); +extern int SHA256Input(SHA256Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA256FinalBits(SHA256Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA256Result(SHA256Context *, + uint8_t Message_Digest[SHA256HashSize]); + +/* SHA-384 */ +extern int SHA384Reset(SHA384Context *); +extern int SHA384Input(SHA384Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA384FinalBits(SHA384Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA384Result(SHA384Context *, + uint8_t Message_Digest[SHA384HashSize]); + +/* SHA-512 */ +extern int SHA512Reset(SHA512Context *); +extern int SHA512Input(SHA512Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA512FinalBits(SHA512Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA512Result(SHA512Context *, + uint8_t Message_Digest[SHA512HashSize]); + +/* Unified SHA functions, chosen by whichSha */ +extern int USHAReset(USHAContext *, SHAversion whichSha); +extern int USHAInput(USHAContext *, + const uint8_t *bytes, unsigned int bytecount); +extern int USHAFinalBits(USHAContext *, + const uint8_t bits, unsigned int bitcount); +extern int USHAResult(USHAContext *, + uint8_t Message_Digest[USHAMaxHashSize]); +extern int USHABlockSize(enum SHAversion whichSha); +extern int USHAHashSize(enum SHAversion whichSha); +extern int USHAHashSizeBits(enum SHAversion whichSha); + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC2104, + * for all SHAs. + * This interface allows a fixed-length text input to be used. + */ +extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */ + const unsigned char *text, /* pointer to data stream */ + int text_len, /* length of data stream */ + const unsigned char *key, /* pointer to authentication key */ + int key_len, /* length of authentication key */ + uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC2104, + * for all SHAs. + * This interface allows any length of text input to be used. + */ +extern int hmacReset(HMACContext *ctx, enum SHAversion whichSha, + const unsigned char *key, int key_len); +extern int hmacInput(HMACContext *ctx, const unsigned char *text, + int text_len); + +extern int hmacFinalBits(HMACContext *ctx, const uint8_t bits, + unsigned int bitcount); +extern int hmacResult(HMACContext *ctx, + uint8_t digest[USHAMaxHashSize]); + + +#ifdef __cplusplus +} +#endif + +#endif /* _SHA_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/shared_util_options.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SHARED_UTIL_OPTIONS_H +#define SHARED_UTIL_OPTIONS_H + +#include "azure_c_shared_utility/const_defines.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct HTTP_PROXY_OPTIONS_TAG + { + const char* host_address; + int port; + const char* username; + const char* password; + } HTTP_PROXY_OPTIONS; + + static STATIC_VAR_UNUSED const char* const OPTION_HTTP_PROXY = "proxy_data"; + static STATIC_VAR_UNUSED const char* const OPTION_HTTP_TIMEOUT = "timeout"; + + static STATIC_VAR_UNUSED const char* const OPTION_TRUSTED_CERT = "TrustedCerts"; + + // Clients should not use OPTION_OPENSSL_CIPHER_SUITE except for very specialized scenarios. + // They instead should rely on the underlying client TLS stack and service to negotiate an appropriate cipher. + static STATIC_VAR_UNUSED const char* const OPTION_OPENSSL_CIPHER_SUITE = "CipherSuite"; + + static STATIC_VAR_UNUSED const char* const SU_OPTION_X509_CERT = "x509certificate"; + static STATIC_VAR_UNUSED const char* const SU_OPTION_X509_PRIVATE_KEY = "x509privatekey"; + + static STATIC_VAR_UNUSED const char* const OPTION_X509_ECC_CERT = "x509EccCertificate"; + static STATIC_VAR_UNUSED const char* const OPTION_X509_ECC_KEY = "x509EccAliasKey"; + + static STATIC_VAR_UNUSED const char* const OPTION_CURL_LOW_SPEED_LIMIT = "CURLOPT_LOW_SPEED_LIMIT"; + static STATIC_VAR_UNUSED const char* const OPTION_CURL_LOW_SPEED_TIME = "CURLOPT_LOW_SPEED_TIME"; + static STATIC_VAR_UNUSED const char* const OPTION_CURL_FRESH_CONNECT = "CURLOPT_FRESH_CONNECT"; + static STATIC_VAR_UNUSED const char* const OPTION_CURL_FORBID_REUSE = "CURLOPT_FORBID_REUSE"; + static STATIC_VAR_UNUSED const char* const OPTION_CURL_VERBOSE = "CURLOPT_VERBOSE"; + + static STATIC_VAR_UNUSED const char* const OPTION_NET_INT_MAC_ADDRESS = "net_interface_mac_address"; + + static STATIC_VAR_UNUSED const char* const OPTION_TLS_VERSION = "tls_version"; + + static STATIC_VAR_UNUSED const char* const OPTION_ADDRESS_TYPE = "ADDRESS_TYPE"; + static STATIC_VAR_UNUSED const char* const OPTION_ADDRESS_TYPE_DOMAIN_SOCKET = "DOMAIN_SOCKET"; + static STATIC_VAR_UNUSED const char* const OPTION_ADDRESS_TYPE_IP_SOCKET = "IP_SOCKET"; +#ifdef __cplusplus +} +#endif + +#endif /* SHARED_UTIL_OPTIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/singlylinkedlist.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SINGLYLINKEDLIST_H +#define SINGLYLINKEDLIST_H + +#ifdef __cplusplus +extern "C" { +#else +#include "stdbool.h" +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct SINGLYLINKEDLIST_INSTANCE_TAG* SINGLYLINKEDLIST_HANDLE; +typedef struct LIST_ITEM_INSTANCE_TAG* LIST_ITEM_HANDLE; + +/** +* @brief Function passed to singlylinkedlist_find, which returns whichever first list item that matches it. +* @param list_item Current list node being evaluated. +* @param match_context Context passed to singlylinkedlist_find. +* @returns True to indicate that the current list node is the one to be returned, or false to continue traversing the list. +*/ +typedef bool (*LIST_MATCH_FUNCTION)(LIST_ITEM_HANDLE list_item, const void* match_context); +/** +* @brief Function passed to singlylinkedlist_remove_if, which is used to define if an item of the list should be removed or not. +* @param item Value of the current list node being evaluated for removal. +* @param match_context Context passed to singlylinkedlist_remove_if. +* @param continue_processing Indicates if singlylinkedlist_remove_if shall continue iterating through the next nodes of the list or stop. +* @returns True to indicate that the current list node shall be removed, or false to not to. +*/ +typedef bool (*LIST_CONDITION_FUNCTION)(const void* item, const void* match_context, bool* continue_processing); +/** +* @brief Function passed to singlylinkedlist_foreach, which is called for the value of each node of the list. +* @param item Value of the current list node being processed. +* @param action_context Context passed to singlylinkedlist_foreach. +* @param continue_processing Indicates if singlylinkedlist_foreach shall continue iterating through the next nodes of the list or stop. +*/ +typedef void (*LIST_ACTION_FUNCTION)(const void* item, const void* action_context, bool* continue_processing); + +MOCKABLE_FUNCTION(, SINGLYLINKEDLIST_HANDLE, singlylinkedlist_create); +MOCKABLE_FUNCTION(, void, singlylinkedlist_destroy, SINGLYLINKEDLIST_HANDLE, list); +MOCKABLE_FUNCTION(, LIST_ITEM_HANDLE, singlylinkedlist_add, SINGLYLINKEDLIST_HANDLE, list, const void*, item); +MOCKABLE_FUNCTION(, int, singlylinkedlist_remove, SINGLYLINKEDLIST_HANDLE, list, LIST_ITEM_HANDLE, item_handle); +MOCKABLE_FUNCTION(, LIST_ITEM_HANDLE, singlylinkedlist_get_head_item, SINGLYLINKEDLIST_HANDLE, list); +MOCKABLE_FUNCTION(, LIST_ITEM_HANDLE, singlylinkedlist_get_next_item, LIST_ITEM_HANDLE, item_handle); +MOCKABLE_FUNCTION(, LIST_ITEM_HANDLE, singlylinkedlist_find, SINGLYLINKEDLIST_HANDLE, list, LIST_MATCH_FUNCTION, match_function, const void*, match_context); +MOCKABLE_FUNCTION(, const void*, singlylinkedlist_item_get_value, LIST_ITEM_HANDLE, item_handle); +MOCKABLE_FUNCTION(, int, singlylinkedlist_remove_if, SINGLYLINKEDLIST_HANDLE, list, LIST_CONDITION_FUNCTION, condition_function, const void*, match_context); +MOCKABLE_FUNCTION(, int, singlylinkedlist_foreach, SINGLYLINKEDLIST_HANDLE, list, LIST_ACTION_FUNCTION, action_function, const void*, action_context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SINGLYLINKEDLIST_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/socketio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SOCKETIO_H +#define SOCKETIO_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +typedef struct SOCKETIO_CONFIG_TAG +{ + const char* hostname; + int port; + void* accepted_socket; +} SOCKETIO_CONFIG; + +#define RECEIVE_BYTES_VALUE 64 + +MOCKABLE_FUNCTION(, CONCRETE_IO_HANDLE, socketio_create, void*, io_create_parameters); +MOCKABLE_FUNCTION(, void, socketio_destroy, CONCRETE_IO_HANDLE, socket_io); +MOCKABLE_FUNCTION(, int, socketio_open, CONCRETE_IO_HANDLE, socket_io, ON_IO_OPEN_COMPLETE, on_io_open_complete, void*, on_io_open_complete_context, ON_BYTES_RECEIVED, on_bytes_received, void*, on_bytes_received_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); +MOCKABLE_FUNCTION(, int, socketio_close, CONCRETE_IO_HANDLE, socket_io, ON_IO_CLOSE_COMPLETE, on_io_close_complete, void*, callback_context); +MOCKABLE_FUNCTION(, int, socketio_send, CONCRETE_IO_HANDLE, socket_io, const void*, buffer, size_t, size, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, socketio_dowork, CONCRETE_IO_HANDLE, socket_io); +MOCKABLE_FUNCTION(, int, socketio_setoption, CONCRETE_IO_HANDLE, socket_io, const char*, optionName, const void*, value); + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, socketio_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SOCKETIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/stdint_ce6.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/string_tokenizer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef STRING_TOKENIZER_H +#define STRING_TOKENIZER_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/string_tokenizer_types.h" + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + +MOCKABLE_FUNCTION(, STRING_TOKENIZER_HANDLE, STRING_TOKENIZER_create, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, STRING_TOKENIZER_HANDLE, STRING_TOKENIZER_create_from_char, const char*, input); +MOCKABLE_FUNCTION(, int, STRING_TOKENIZER_get_next_token, STRING_TOKENIZER_HANDLE, t, STRING_HANDLE, output, const char*, delimiters); +MOCKABLE_FUNCTION(, void, STRING_TOKENIZER_destroy, STRING_TOKENIZER_HANDLE, t); + +#ifdef __cplusplus +} +#else +#endif + +#endif /*STRING_TOKENIZER_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/string_tokenizer_types.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef STRING_TOKENIZER_TYPES_H +#define STRING_TOKENIZER_TYPES_H + +typedef struct STRING_TOKEN_TAG* STRING_TOKENIZER_HANDLE; + +#endif /*STRING_TOKENIZER_TYPES_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/strings.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef STRINGS_H +#define STRINGS_H + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/strings_types.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_new); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_clone, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_construct, const char*, psz); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_construct_n, const char*, psz, size_t, n); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_new_with_memory, const char*, memory); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_new_quoted, const char*, source); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_new_JSON, const char*, source); +MOCKABLE_FUNCTION(, STRING_HANDLE, STRING_from_byte_array, const unsigned char*, source, size_t, size); +MOCKABLE_FUNCTION(, void, STRING_delete, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, int, STRING_concat, STRING_HANDLE, handle, const char*, s2); +MOCKABLE_FUNCTION(, int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2); +MOCKABLE_FUNCTION(, int, STRING_quote, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, int, STRING_copy, STRING_HANDLE, s1, const char*, s2); +MOCKABLE_FUNCTION(, int, STRING_copy_n, STRING_HANDLE, s1, const char*, s2, size_t, n); +MOCKABLE_FUNCTION(, const char*, STRING_c_str, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, int, STRING_empty, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, size_t, STRING_length, STRING_HANDLE, handle); +MOCKABLE_FUNCTION(, int, STRING_compare, STRING_HANDLE, s1, STRING_HANDLE, s2); +MOCKABLE_FUNCTION(, int, STRING_replace, STRING_HANDLE, handle, char, target, char, replace); + +extern STRING_HANDLE STRING_construct_sprintf(const char* format, ...); +extern int STRING_sprintf(STRING_HANDLE s1, const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /*STRINGS_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/strings_types.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef STRINGS_TYPES_H +#define STRINGS_TYPES_H + +typedef struct STRING_TAG* STRING_HANDLE; + +#endif /*STRINGS_TYPES_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tcpsocketconnection_c.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TCPSOCKETCONNECTION_C_H +#define TCPSOCKETCONNECTION_C_H + +#include "umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void* TCPSOCKETCONNECTION_HANDLE; + + TCPSOCKETCONNECTION_HANDLE tcpsocketconnection_create(void); + void tcpsocketconnection_set_blocking(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, bool blocking, unsigned int timeout); + void tcpsocketconnection_destroy(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle); + int tcpsocketconnection_connect(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* host, const int port); + bool tcpsocketconnection_is_connected(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle); + void tcpsocketconnection_close(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle); + int tcpsocketconnection_send(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* data, int length); + int tcpsocketconnection_send_all(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, const char* data, int length); + int tcpsocketconnection_receive(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, char* data, int length); + int tcpsocketconnection_receive_all(TCPSOCKETCONNECTION_HANDLE tcpSocketConnectionHandle, char* data, int length); + +#ifdef __cplusplus +} +#endif + +#endif /* TCPSOCKETCONNECTION_C_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/threadapi.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file threadapi.h + * @brief This module implements support for creating new threads, + * terminating threads and sleeping threads. + */ + +#ifndef THREADAPI_H +#define THREADAPI_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(*THREAD_START_FUNC)(void *); + +#define THREADAPI_RESULT_VALUES \ + THREADAPI_OK, \ + THREADAPI_INVALID_ARG, \ + THREADAPI_NO_MEMORY, \ + THREADAPI_ERROR + +/** @brief Enumeration specifying the possible return values for the APIs in + * this module. + */ +DEFINE_ENUM(THREADAPI_RESULT, THREADAPI_RESULT_VALUES); + +typedef void* THREAD_HANDLE; + +/** + * @brief Creates a thread with the entry point specified by the @p func + * argument. + * + * @param threadHandle The handle to the new thread is returned in this + * pointer. + * @param func A function pointer that indicates the entry point + * to the new thread. + * @param arg A void pointer that must be passed to the function + * pointed to by @p func. + * + * @return @c THREADAPI_OK if the API call is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, THREADAPI_RESULT, ThreadAPI_Create, THREAD_HANDLE*, threadHandle, THREAD_START_FUNC, func, void*, arg); + +/** + * @brief Blocks the calling thread by waiting on the thread identified by + * the @p threadHandle argument to complete. + * + * @param threadHandle The handle of the thread to wait for completion. + * @param res The result returned by the thread which is passed + * to the ::ThreadAPI_Exit function. + * + * When the @p threadHandle thread completes, all resources associated + * with the thread must be released and the thread handle will no + * longer be valid. + * + * @return @c THREADAPI_OK if the API call is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, THREADAPI_RESULT, ThreadAPI_Join, THREAD_HANDLE, threadHandle, int*, res); + +/** + * @brief This function is called by a thread when the thread exits. + * + * @param res An integer that represents the exit status of the thread. + * + * This function is called by a thread when the thread exits in order + * to return a result value to the caller of the ::ThreadAPI_Join + * function. The @p res value must be copied into the @p res out + * argument passed to the ::ThreadAPI_Join function. + */ +MOCKABLE_FUNCTION(, void, ThreadAPI_Exit, int, res); + +/** + * @brief Sleeps the current thread for the given number of milliseconds. + * + * @param milliseconds The number of milliseconds to sleep. + */ +MOCKABLE_FUNCTION(, void, ThreadAPI_Sleep, unsigned int, milliseconds); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADAPI_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tickcounter.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TICKCOUNTER_H +#define TICKCOUNTER_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <stdint.h> + +#include "azure_c_shared_utility/umock_c_prod.h" + +#if _WIN32 + typedef uint_fast64_t tickcounter_ms_t; // Use 64-bit because of QueryPerformanceCounter call +#else + typedef uint_fast32_t tickcounter_ms_t; +#endif + + typedef struct TICK_COUNTER_INSTANCE_TAG* TICK_COUNTER_HANDLE; + + MOCKABLE_FUNCTION(, TICK_COUNTER_HANDLE, tickcounter_create); + MOCKABLE_FUNCTION(, void, tickcounter_destroy, TICK_COUNTER_HANDLE, tick_counter); + MOCKABLE_FUNCTION(, int, tickcounter_get_current_ms, TICK_COUNTER_HANDLE, tick_counter, tickcounter_ms_t*, current_ms); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TICKCOUNTER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tls_config.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,10 @@ +#ifndef __TLS_CONFIG_H__ +#define __TLS_CONFIG_H__ + +// DEPRECATED: This file will be removed from the tree. + +// WolfSSL or mbedTLS +//#define USE_WOLF_SSL +#define USE_MBED_TLS + +#endif // __TLS_CONFIG_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_H +#define TLSIO_H + +#include "xio.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct TLSIO_CONFIG_TAG +{ + const char* hostname; + int port; + const IO_INTERFACE_DESCRIPTION* underlying_io_interface; + void* underlying_io_parameters; +} TLSIO_CONFIG; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_cyclonessl.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_CYCLONESSL_H +#define TLSIO_CYCLONESSL_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_cyclonessl_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_CYCLONESSL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_cyclonessl_socket.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_CYCLONESSL_SOCKET_H +#define TLSIO_CYCLONESSL_SOCKET_H + +#include "tls.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +MOCKABLE_FUNCTION(, int, tlsio_cyclonessl_socket_create, const char*, hostname, unsigned int, port, TlsSocket*, new_socket); +MOCKABLE_FUNCTION(, void, tlsio_cyclonessl_socket_destroy, TlsSocket, socket); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_CYCLONESSL_SOCKET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_mbedtls.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_MBEDTLS_H +#define TLSIO_MBEDTLS_H + +// DEPRECATED: tls_config will be removed from the tree. +#include "azure_c_shared_utility/tls_config.h" + +// DEPRECATED: the USE_MBED_TLS #define is deprecated. +#ifdef USE_MBED_TLS + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/optionhandler.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +// DEPRECATED: the functions below do not neet to be exposed. +extern CONCRETE_IO_HANDLE tlsio_mbedtls_create(void* io_create_parameters); +extern void tlsio_mbedtls_destroy(CONCRETE_IO_HANDLE tls_io); +extern int tlsio_mbedtls_open(CONCRETE_IO_HANDLE tls_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context); +extern int tlsio_mbedtls_close(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); +extern int tlsio_mbedtls_send(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); +extern void tlsio_mbedtls_dowork(CONCRETE_IO_HANDLE tls_io); +extern int tlsio_mbedtls_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value); +extern OPTIONHANDLER_HANDLE tlsio_mbedtls_retrieveoptions(CONCRETE_IO_HANDLE handle); +// DEPRECATED: the functions above do not neet to be exposed. + +extern const IO_INTERFACE_DESCRIPTION* tlsio_mbedtls_get_interface_description(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +// DEPRECATED: the USE_MBED_TLS #define is deprecated. +#endif /* USE_MBED_TLS */ + +#endif /* TLSIO_MBEDTLS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_openssl.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_OPENSSL_H +#define TLSIO_OPENSSL_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +MOCKABLE_FUNCTION(, int, tlsio_openssl_init); +MOCKABLE_FUNCTION(, void, tlsio_openssl_deinit); + +MOCKABLE_FUNCTION(, CONCRETE_IO_HANDLE, tlsio_openssl_create, void*, io_create_parameters); +MOCKABLE_FUNCTION(, void, tlsio_openssl_destroy, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_openssl_open, CONCRETE_IO_HANDLE, tls_io, ON_IO_OPEN_COMPLETE, on_io_open_complete, void*, on_io_open_complete_context, ON_BYTES_RECEIVED, on_bytes_received, void*, on_bytes_received_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); +MOCKABLE_FUNCTION(, int, tlsio_openssl_close, CONCRETE_IO_HANDLE, tls_io, ON_IO_CLOSE_COMPLETE, on_io_close_complete, void*, callback_context); +MOCKABLE_FUNCTION(, int, tlsio_openssl_send, CONCRETE_IO_HANDLE, tls_io, const void*, buffer, size_t, size, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, tlsio_openssl_dowork, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_openssl_setoption, CONCRETE_IO_HANDLE, tls_io, const char*, optionName, const void*, value); + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_openssl_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_OPENSSL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_options.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// tlsio_options handles only the most commonly-used options for tlsio adapters. Options +// not supported by this component may be handled in the tlsio adapter instead. + +#ifndef TLSIO_OPTIONS_H +#define TLSIO_OPTIONS_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + // This enum identifies individual options + typedef enum TLSIO_OPTION_BIT_TAG + { + TLSIO_OPTION_BIT_NONE = 0x00, + TLSIO_OPTION_BIT_TRUSTED_CERTS = 0x01, + TLSIO_OPTION_BIT_x509_RSA_CERT = 0x02, + TLSIO_OPTION_BIT_x509_ECC_CERT = 0x04, + } TLSIO_OPTION_BIT; + + typedef enum TLSIO_OPTIONS_x509_TYPE_TAG + { + TLSIO_OPTIONS_x509_TYPE_UNSPECIFIED = TLSIO_OPTION_BIT_NONE, + TLSIO_OPTIONS_x509_TYPE_RSA = TLSIO_OPTION_BIT_x509_RSA_CERT, + TLSIO_OPTIONS_x509_TYPE_ECC = TLSIO_OPTION_BIT_x509_ECC_CERT + } TLSIO_OPTIONS_x509_TYPE; + + typedef enum TLSIO_OPTIONS_RESULT_TAG + { + TLSIO_OPTIONS_RESULT_SUCCESS, + TLSIO_OPTIONS_RESULT_NOT_HANDLED, + TLSIO_OPTIONS_RESULT_ERROR + } TLSIO_OPTIONS_RESULT; + + + // This struct contains the commonly-used options which + // are supported by tlsio_options. + typedef struct TLSIO_OPTIONS_TAG + { + int supported_options; + const char* trusted_certs; + TLSIO_OPTIONS_x509_TYPE x509_type; + const char* x509_cert; + const char* x509_key; + } TLSIO_OPTIONS; + + // Initialize the TLSIO_OPTIONS struct and specify which options are supported as a bit-or'ed + // set of TLSIO_OPTION_BIT. For the x509 certs, only the *_CERT bit need be specified; the *_KEY + // is understood to go with the cert. + void tlsio_options_initialize(TLSIO_OPTIONS* options, int option_caps); + + // This should be called in the tlsio destructor + void tlsio_options_release_resources(TLSIO_OPTIONS* options); + + // xio requires the implementation of four functions: xio_setoption, xio_retrieveoptions, + // an anonymous clone_option, and an anonymous destroy_option. + + // The helper for xio_setoption + TLSIO_OPTIONS_RESULT tlsio_options_set(TLSIO_OPTIONS* options, + const char* optionName, const void* value); + + // Use this helper for xio_retrieveoptions if this helper covers all your tlsio options + OPTIONHANDLER_HANDLE tlsio_options_retrieve_options(TLSIO_OPTIONS* options, pfSetOption setOption); + + // Use this helper for xio_retrieveoptions if your tlsio supports more options than this helper does + OPTIONHANDLER_HANDLE tlsio_options_retrieve_options_ex(TLSIO_OPTIONS* options, + pfCloneOption cloneOption, pfDestroyOption destroyOption, pfSetOption setOption); + + // The helper for the anonymous clone_option -- needed only to support extra options + TLSIO_OPTIONS_RESULT tlsio_options_clone_option(const char* name, const void* value, void** out_value); + + // The helper for the anonymous destroy_option -- needed only to support extra options + TLSIO_OPTIONS_RESULT tlsio_options_destroy_option(const char* name, const void* value); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_OPTIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_schannel.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_SCHANNEL_H +#define TLSIO_SCHANNEL_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +MOCKABLE_FUNCTION(, CONCRETE_IO_HANDLE, tlsio_schannel_create, void*, io_create_parameters); +MOCKABLE_FUNCTION(, void, tlsio_schannel_destroy, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_schannel_open, CONCRETE_IO_HANDLE, tls_io, ON_IO_OPEN_COMPLETE, on_io_open_complete, void*, on_io_open_complete_context, ON_BYTES_RECEIVED, on_bytes_received, void*, on_bytes_received_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); +MOCKABLE_FUNCTION(, int, tlsio_schannel_close, CONCRETE_IO_HANDLE, tls_io, ON_IO_CLOSE_COMPLETE, on_io_close_complete, void*, callback_context); +MOCKABLE_FUNCTION(, int, tlsio_schannel_send, CONCRETE_IO_HANDLE, tls_io, const void*, buffer, size_t, size, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, tlsio_schannel_dowork, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_schannel_setoption, CONCRETE_IO_HANDLE, tls_io, const char*, optionName, const void*, value); + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_schannel_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_SCHANNEL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/tlsio_wolfssl.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef TLSIO_WOLFSSL_H +#define TLSIO_WOLFSSL_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/const_defines.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +extern const char* const OPTION_WOLFSSL_SET_DEVICE_ID; + +MOCKABLE_FUNCTION(, CONCRETE_IO_HANDLE, tlsio_wolfssl_create, void*, io_create_parameters); +MOCKABLE_FUNCTION(, void, tlsio_wolfssl_destroy, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_wolfssl_open, CONCRETE_IO_HANDLE, tls_io, ON_IO_OPEN_COMPLETE, on_io_open_complete, void*, on_io_open_complete_context, ON_BYTES_RECEIVED, on_bytes_received, void*, on_bytes_received_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); +MOCKABLE_FUNCTION(, int, tlsio_wolfssl_close, CONCRETE_IO_HANDLE, tls_io, ON_IO_CLOSE_COMPLETE, on_io_close_complete, void*, callback_context); +MOCKABLE_FUNCTION(, int, tlsio_wolfssl_send, CONCRETE_IO_HANDLE, tls_io, const void*, buffer, size_t, size, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, tlsio_wolfssl_dowork, CONCRETE_IO_HANDLE, tls_io); +MOCKABLE_FUNCTION(, int, tlsio_wolfssl_setoption, CONCRETE_IO_HANDLE, tls_io, const char*, optionName, const void*, value); + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_wolfssl_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TLSIO_WOLFSSL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/umock_c_prod.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#undef MOCKABLE_FUNCTION + +/* This header is meant to be included by production code headers, so that the MOCKABLE_FUNCTION gets enabled. */ +/* + If you are porting to a new platform and do not want to build the tests, but only the production code, + simply make sure that this file is in the include path (either by copying it to your inc folder or + by adjusting the include paths). +*/ + +#ifdef ENABLE_MOCKS + +/* Codes_SRS_UMOCK_C_LIB_01_001: [MOCKABLE_FUNCTION shall be used to wrap function definition allowing the user to declare a function that can be mocked.]*/ +#define MOCKABLE_FUNCTION(modifiers, result, function, ...) \ + MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK(modifiers, result, function, __VA_ARGS__) + +#include "umock_c.h" + +#else + +#include "azure_c_shared_utility/macro_utils.h" + +#define UMOCK_C_PROD_ARG_IN_SIGNATURE(count, arg_type, arg_name) arg_type arg_name IFCOMMA(count) + +/* Codes_SRS_UMOCK_C_LIB_01_002: [The macro shall generate a function signature in case ENABLE_MOCKS is not defined.] */ +/* Codes_SRS_UMOCK_C_LIB_01_005: [**If ENABLE_MOCKS is not defined, MOCKABLE_FUNCTION shall only generate a declaration for the function.] */ +/* Codes_SRS_UMOCK_C_LIB_01_001: [MOCKABLE_FUNCTION shall be used to wrap function definition allowing the user to declare a function that can be mocked.]*/ +#define MOCKABLE_FUNCTION(modifiers, result, function, ...) \ + result modifiers function(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(UMOCK_C_PROD_ARG_IN_SIGNATURE, __VA_ARGS__)); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/uniqueid.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UNIQUEID_H +#define UNIQUEID_H + +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +#define UNIQUEID_RESULT_VALUES \ + UNIQUEID_OK, \ + UNIQUEID_INVALID_ARG, \ + UNIQUEID_ERROR + + DEFINE_ENUM(UNIQUEID_RESULT, UNIQUEID_RESULT_VALUES) + + MOCKABLE_FUNCTION(, UNIQUEID_RESULT, UniqueId_Generate, char*, uid, size_t, bufferSize); + +#ifdef __cplusplus +} +#endif + +#endif /* UNIQUEID_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/urlencode.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef URLENCODE_H +#define URLENCODE_H + +#include "azure_c_shared_utility/strings.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /* @brief URL Encode (aka percent encode) a string. + * Please note that while the URL encoder accepts the full range of 8-bit extended ASCII, + * it has unpredictable behavior beyond the 7-bit ASCII standard. This function does NOT + * guarantee correctness of output for characters outside this range. + * + * @param URL Encode can be done on a const char* or a STRING_HANDLE, use the appropriate + * function depending on input type, they both behave the same way. + * + * @return Returns a STRING_HANDLE with the encoded string, or NULL on failure. + */ + MOCKABLE_FUNCTION(, STRING_HANDLE, URL_Encode, STRING_HANDLE, input); + MOCKABLE_FUNCTION(, STRING_HANDLE, URL_EncodeString, const char*, textEncode); + + /* @brief URL Decode (aka percent decode) a string. + * Please note that the URL decoder only supports decoding characters that fall within the + * 7-bit ASCII range. It does NOT support 8-bit extended ASCII, and will fail if you try. + * + * @param URL Decode can be done on a const char* or a STRING_HANDLE, use the appropriate + * function depending on input type, they both behave the same way. + * + * @return Returns a STRING_HANDLE with the decoded string, or NULL on failure. + */ + MOCKABLE_FUNCTION(, STRING_HANDLE, URL_Decode, STRING_HANDLE, input); + MOCKABLE_FUNCTION(, STRING_HANDLE, URL_DecodeString, const char*, textDecode); + +#ifdef __cplusplus +} +#endif + +#endif /* URLENCODE_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/utf8_checker.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UTF8_CHECKER_H +#define UTF8_CHECKER_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stdbool.h> +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(, bool, utf8_checker_is_valid_utf8, const unsigned char*, utf8_str, size_t, length); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UTF8_CHECKER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/uuid.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UUID_H +#define UUID_H + +#ifdef __cplusplus +#include <cstddef> +#include <cstdint> +extern "C" { +#else +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef unsigned char UUID[16]; + +/* @brief Generates a true UUID +* @param uuid A pre-allocated buffer for the bytes of the generated UUID +* @returns Zero if no failures occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, UUID_generate, UUID*, uuid); + +/* @brief Gets the UUID value (byte sequence) of an well-formed UUID string. +* @param uuid_string A null-terminated well-formed UUID string (e.g., "7f907d75-5e13-44cf-a1a3-19a01a2b4528"). +* @param uuid Sequence of bytes representing an UUID. +* @returns Zero if no failures occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, UUID_from_string, const char*, uuid_string, UUID*, uuid); + +/* @brief Gets the string representation of the UUID value. +* @param uuid Sequence of bytes representing an UUID. +* @returns A null-terminated string representation of the UUID value provided (e.g., "7f907d75-5e13-44cf-a1a3-19a01a2b4528"). +*/ +MOCKABLE_FUNCTION(, char*, UUID_to_string, UUID*, uuid); + +#ifdef __cplusplus +} +#endif + +#endif /* UUID_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/uws_client.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UWS_CLIENT_H +#define UWS_CLIENT_H + +#include "xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdint> +extern "C" { +#else +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#endif + +typedef struct UWS_CLIENT_INSTANCE_TAG* UWS_CLIENT_HANDLE; + +#define WS_SEND_FRAME_RESULT_VALUES \ + WS_SEND_FRAME_OK, \ + WS_SEND_FRAME_ERROR, \ + WS_SEND_FRAME_CANCELLED + +DEFINE_ENUM(WS_SEND_FRAME_RESULT, WS_SEND_FRAME_RESULT_VALUES); + +#define WS_OPEN_RESULT_VALUES \ + WS_OPEN_OK, \ + WS_OPEN_ERROR_UNDERLYING_IO_OPEN_FAILED, \ + WS_OPEN_ERROR_UNDERLYING_IO_OPEN_CANCELLED, \ + WS_OPEN_ERROR_NOT_ENOUGH_MEMORY, \ + WS_OPEN_ERROR_CANNOT_CONSTRUCT_UPGRADE_REQUEST, \ + WS_OPEN_ERROR_CANNOT_SEND_UPGRADE_REQUEST, \ + WS_OPEN_ERROR_MULTIPLE_UNDERLYING_IO_OPEN_EVENTS, \ + WS_OPEN_ERROR_CONSTRUCTING_UPGRADE_REQUEST, \ + WS_OPEN_ERROR_INVALID_BYTES_RECEIVED_ARGUMENTS, \ + WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN, \ + WS_OPEN_CANCELLED, \ + WS_OPEN_ERROR_UNDERLYING_IO_ERROR, \ + WS_OPEN_ERROR_BAD_UPGRADE_RESPONSE, \ + WS_OPEN_ERROR_BAD_RESPONSE_STATUS, \ + WS_OPEN_ERROR_BASE64_ENCODE_FAILED + +DEFINE_ENUM(WS_OPEN_RESULT, WS_OPEN_RESULT_VALUES); + +#define WS_ERROR_VALUES \ + WS_ERROR_NOT_ENOUGH_MEMORY, \ + WS_ERROR_BAD_FRAME_RECEIVED, \ + WS_ERROR_CANNOT_REMOVE_SENT_ITEM_FROM_LIST, \ + WS_ERROR_UNDERLYING_IO_ERROR, \ + WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO + +DEFINE_ENUM(WS_ERROR, WS_ERROR_VALUES); + +#define WS_FRAME_TYPE_UNKNOWN 0x00 +#define WS_FRAME_TYPE_TEXT 0x01 +#define WS_FRAME_TYPE_BINARY 0x02 + +/* Codes_SRS_UWS_CLIENT_01_324: [ 1000 indicates a normal closure, meaning that the purpose for which the connection was established has been fulfilled. ]*/ +/* Codes_SRS_UWS_CLIENT_01_325: [ 1001 indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page. ]*/ +/* Codes_SRS_UWS_CLIENT_01_326: [ 1002 indicates that an endpoint is terminating the connection due to a protocol error. ]*/ +/* Codes_SRS_UWS_CLIENT_01_327: [ 1003 indicates that an endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message). ]*/ +/* Codes_SRS_UWS_CLIENT_01_328: [ Reserved. The specific meaning might be defined in the future. ]*/ +/* Codes_SRS_UWS_CLIENT_01_329: [ 1005 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. ]*/ +/* Codes_SRS_UWS_CLIENT_01_330: [ 1006 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. ]*/ +/* Codes_SRS_UWS_CLIENT_01_331: [ 1007 indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [RFC3629] data within a text message). ]*/ +/* Codes_SRS_UWS_CLIENT_01_332: [ 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. ]*/ +/* Codes_SRS_UWS_CLIENT_01_333: [ 1009 indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process. ]*/ +/* Codes_SRS_UWS_CLIENT_01_334: [ 1010 indicates that an endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake. ]*/ +/* Codes_SRS_UWS_CLIENT_01_336: [ 1011 indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request. ]*/ +/* Codes_SRS_UWS_CLIENT_01_337: [ 1015 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. ]*/ +/* */ +#define CLOSE_NORMAL 1000 +#define CLOSE_GOING_AWAY 1001 +#define CLOSE_PROTOCOL_ERROR 1002 +#define CLOSE_CANNOT_ACCEPT_DATA_TYPE 1003 +#define CLOSE_UNDEFINED_1004 1004 +#define CLOSE_RESERVED_1005 1005 +#define CLOSE_RESERVED_1006 1006 +#define CLOSE_INCONSISTENT_DATA_IN_MESSAGE 1007 +#define CLOSE_POLICY_VIOLATION 1008 +#define CLOSE_MESSAGE_TOO_BIG 1009 +#define CLOSE_UNSUPPORTED_EXTENSION_LIST 1010 +#define CLOSE_UNEXPECTED_CONDITION 1011 +#define CLOSE_RESERVED_1015 1015 + +typedef void(*ON_WS_FRAME_RECEIVED)(void* context, unsigned char frame_type, const unsigned char* buffer, size_t size); +typedef void(*ON_WS_SEND_FRAME_COMPLETE)(void* context, WS_SEND_FRAME_RESULT ws_send_frame_result); +typedef void(*ON_WS_OPEN_COMPLETE)(void* context, WS_OPEN_RESULT ws_open_result); +typedef void(*ON_WS_CLOSE_COMPLETE)(void* context); +typedef void(*ON_WS_PEER_CLOSED)(void* context, uint16_t* close_code, const unsigned char* extra_data, size_t extra_data_length); +typedef void(*ON_WS_ERROR)(void* context, WS_ERROR error_code); + +typedef struct WS_PROTOCOL_TAG +{ + const char* protocol; +} WS_PROTOCOL; + +MOCKABLE_FUNCTION(, UWS_CLIENT_HANDLE, uws_client_create, const char*, hostname, unsigned int, port, const char*, resource_name, bool, use_ssl, const WS_PROTOCOL*, protocols, size_t, protocol_count); +MOCKABLE_FUNCTION(, UWS_CLIENT_HANDLE, uws_client_create_with_io, const IO_INTERFACE_DESCRIPTION*, io_interface, void*, io_create_parameters, const char*, hostname, unsigned int, port, const char*, resource_name, const WS_PROTOCOL*, protocols, size_t, protocol_count) +MOCKABLE_FUNCTION(, void, uws_client_destroy, UWS_CLIENT_HANDLE, uws_client); +MOCKABLE_FUNCTION(, int, uws_client_open_async, UWS_CLIENT_HANDLE, uws_client, ON_WS_OPEN_COMPLETE, on_ws_open_complete, void*, on_ws_open_complete_context, ON_WS_FRAME_RECEIVED, on_ws_frame_received, void*, on_ws_frame_received_context, ON_WS_PEER_CLOSED, on_ws_peer_closed, void*, on_ws_peer_closed_context, ON_WS_ERROR, on_ws_error, void*, on_ws_error_context); +MOCKABLE_FUNCTION(, int, uws_client_close_async, UWS_CLIENT_HANDLE, uws_client, ON_WS_CLOSE_COMPLETE, on_ws_close_complete, void*, on_ws_close_complete_context); +MOCKABLE_FUNCTION(, int, uws_client_close_handshake_async, UWS_CLIENT_HANDLE, uws_client, uint16_t, close_code, const char*, close_reason, ON_WS_CLOSE_COMPLETE, on_ws_close_complete, void*, on_ws_close_complete_context); +MOCKABLE_FUNCTION(, int, uws_client_send_frame_async, UWS_CLIENT_HANDLE, uws_client, unsigned char, frame_type, const unsigned char*, buffer, size_t, size, bool, is_final, ON_WS_SEND_FRAME_COMPLETE, on_ws_send_frame_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, uws_client_dowork, UWS_CLIENT_HANDLE, uws_client); + +MOCKABLE_FUNCTION(, int, uws_client_set_option, UWS_CLIENT_HANDLE, uws_client, const char*, option_name, const void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, uws_client_retrieve_options, UWS_CLIENT_HANDLE, uws_client); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UWS_CLIENT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/uws_frame_encoder.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UWS_FRAME_ENCODER_H +#define UWS_FRAME_ENCODER_H + +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stdbool.h> +#include <stddef.h> +#endif + +#define RESERVED_1 0x04 +#define RESERVED_2 0x02 +#define RESERVED_3 0x01 + +#define WS_FRAME_TYPE_VALUES \ + WS_CONTINUATION_FRAME, \ + WS_TEXT_FRAME, \ + WS_BINARY_FRAME, \ + WS_RESERVED_NON_CONTROL_FRAME_3, \ + WS_RESERVED_NON_CONTROL_FRAME_4, \ + WS_RESERVED_NON_CONTROL_FRAME_5, \ + WS_RESERVED_NON_CONTROL_FRAME_6, \ + WS_RESERVED_NON_CONTROL_FRAME_7, \ + WS_CLOSE_FRAME, \ + WS_PING_FRAME, \ + WS_PONG_FRAME, \ + WS_RESERVED_CONTROL_FRAME_B, \ + WS_RESERVED_CONTROL_FRAME_C, \ + WS_RESERVED_CONTROL_FRAME_D, \ + WS_RESERVED_CONTROL_FRAME_E, \ + WS_RESERVED_CONTROL_FRAME_F + +DEFINE_ENUM(WS_FRAME_TYPE, WS_FRAME_TYPE_VALUES); + +MOCKABLE_FUNCTION(, BUFFER_HANDLE, uws_frame_encoder_encode, WS_FRAME_TYPE, opcode, const unsigned char*, payload, size_t, length, bool, is_masked, bool, is_final, unsigned char, reserved); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UWS_FRAME_ENCODER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/vector.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef VECTOR_H +#define VECTOR_H + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/vector_types.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#include <stdbool.h> +#endif + +/* creation */ +MOCKABLE_FUNCTION(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +MOCKABLE_FUNCTION(, VECTOR_HANDLE, VECTOR_move, VECTOR_HANDLE, handle); +MOCKABLE_FUNCTION(, void, VECTOR_destroy, VECTOR_HANDLE, handle); + +/* insertion */ +MOCKABLE_FUNCTION(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); + +/* removal */ +MOCKABLE_FUNCTION(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements); +MOCKABLE_FUNCTION(, void, VECTOR_clear, VECTOR_HANDLE, handle); + +/* access */ +MOCKABLE_FUNCTION(, void*, VECTOR_element, VECTOR_HANDLE, handle, size_t, index); +MOCKABLE_FUNCTION(, void*, VECTOR_front, VECTOR_HANDLE, handle); +MOCKABLE_FUNCTION(, void*, VECTOR_back, VECTOR_HANDLE, handle); +MOCKABLE_FUNCTION(, void*, VECTOR_find_if, VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value); + +/* capacity */ +MOCKABLE_FUNCTION(, size_t, VECTOR_size, VECTOR_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* VECTOR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/vector_types.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef VECTOR_TYPES_H +#define VECTOR_TYPES_H + +#ifdef __cplusplus +extern "C" +{ +#else +#include <stdbool.h> +#endif + +typedef struct VECTOR_TAG* VECTOR_HANDLE; + +typedef bool(*PREDICATE_FUNCTION)(const void* element, const void* value); + +#ifdef __cplusplus +} +#endif + +#endif /* VECTOR_TYPES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/vector_types_internal.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef VECTOR_TYPES_INTERNAL_H +#define VECTOR_TYPES_INTERNAL_H + +#ifdef __cplusplus +#include <cstddef> +#else +#include <stddef.h> +#endif + +typedef struct VECTOR_TAG +{ + void* storage; + size_t count; + size_t elementSize; +} VECTOR; + +#endif /* VECTOR_TYPES_INTERNAL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/wsio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef WSIO_H +#define WSIO_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +typedef struct WSIO_CONFIG_TAG +{ + const IO_INTERFACE_DESCRIPTION* underlying_io_interface; + void* underlying_io_parameters; + const char* hostname; + int port; + const char* resource_name; + const char* protocol; +} WSIO_CONFIG; + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, wsio_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* WSIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/x509_openssl.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef X509_OPENSSL_H +#define X509_OPENSSL_H + +#include "openssl/ssl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(,int, x509_openssl_add_certificates, SSL_CTX*, ssl_ctx, const char*, certificates); +MOCKABLE_FUNCTION(,int, x509_openssl_add_credentials, SSL_CTX*, ssl_ctx, const char*, x509certificate, const char*, x509privatekey); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/x509_schannel.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef X509_SCHANNEL_H +#define X509_SCHANNEL_H + +#include "windows.h" + +#ifdef __cplusplus +extern "C" { +#else +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct X509_SCHANNEL_HANDLE_DATA_TAG* X509_SCHANNEL_HANDLE; + +MOCKABLE_FUNCTION(, X509_SCHANNEL_HANDLE, x509_schannel_create, const char*, x509certificate, const char*, x509privatekey); +MOCKABLE_FUNCTION(, void, x509_schannel_destroy, X509_SCHANNEL_HANDLE, x509_schannel_handle); +MOCKABLE_FUNCTION(, PCCERT_CONTEXT, x509_schannel_get_certificate_context, X509_SCHANNEL_HANDLE, x509_schannel_handle); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/xio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef XIO_H +#define XIO_H + +#include "azure_c_shared_utility/optionhandler.h" + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif /* __cplusplus */ + +typedef struct XIO_INSTANCE_TAG* XIO_HANDLE; +typedef void* CONCRETE_IO_HANDLE; + +#define IO_SEND_RESULT_VALUES \ + IO_SEND_OK, \ + IO_SEND_ERROR, \ + IO_SEND_CANCELLED + +DEFINE_ENUM(IO_SEND_RESULT, IO_SEND_RESULT_VALUES); + +#define IO_OPEN_RESULT_VALUES \ + IO_OPEN_OK, \ + IO_OPEN_ERROR, \ + IO_OPEN_CANCELLED + +DEFINE_ENUM(IO_OPEN_RESULT, IO_OPEN_RESULT_VALUES); + +typedef void(*ON_BYTES_RECEIVED)(void* context, const unsigned char* buffer, size_t size); +typedef void(*ON_SEND_COMPLETE)(void* context, IO_SEND_RESULT send_result); +typedef void(*ON_IO_OPEN_COMPLETE)(void* context, IO_OPEN_RESULT open_result); +typedef void(*ON_IO_CLOSE_COMPLETE)(void* context); +typedef void(*ON_IO_ERROR)(void* context); + +typedef OPTIONHANDLER_HANDLE (*IO_RETRIEVEOPTIONS)(CONCRETE_IO_HANDLE concrete_io); +typedef CONCRETE_IO_HANDLE(*IO_CREATE)(void* io_create_parameters); +typedef void(*IO_DESTROY)(CONCRETE_IO_HANDLE concrete_io); +typedef int(*IO_OPEN)(CONCRETE_IO_HANDLE concrete_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context); +typedef int(*IO_CLOSE)(CONCRETE_IO_HANDLE concrete_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); +typedef int(*IO_SEND)(CONCRETE_IO_HANDLE concrete_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); +typedef void(*IO_DOWORK)(CONCRETE_IO_HANDLE concrete_io); +typedef int(*IO_SETOPTION)(CONCRETE_IO_HANDLE concrete_io, const char* optionName, const void* value); + + +typedef struct IO_INTERFACE_DESCRIPTION_TAG +{ + IO_RETRIEVEOPTIONS concrete_io_retrieveoptions; + IO_CREATE concrete_io_create; + IO_DESTROY concrete_io_destroy; + IO_OPEN concrete_io_open; + IO_CLOSE concrete_io_close; + IO_SEND concrete_io_send; + IO_DOWORK concrete_io_dowork; + IO_SETOPTION concrete_io_setoption; +} IO_INTERFACE_DESCRIPTION; + +MOCKABLE_FUNCTION(, XIO_HANDLE, xio_create, const IO_INTERFACE_DESCRIPTION*, io_interface_description, const void*, io_create_parameters); +MOCKABLE_FUNCTION(, void, xio_destroy, XIO_HANDLE, xio); +MOCKABLE_FUNCTION(, int, xio_open, XIO_HANDLE, xio, ON_IO_OPEN_COMPLETE, on_io_open_complete, void*, on_io_open_complete_context, ON_BYTES_RECEIVED, on_bytes_received, void*, on_bytes_received_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); +MOCKABLE_FUNCTION(, int, xio_close, XIO_HANDLE, xio, ON_IO_CLOSE_COMPLETE, on_io_close_complete, void*, callback_context); +MOCKABLE_FUNCTION(, int, xio_send, XIO_HANDLE, xio, const void*, buffer, size_t, size, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); +MOCKABLE_FUNCTION(, void, xio_dowork, XIO_HANDLE, xio); +MOCKABLE_FUNCTION(, int, xio_setoption, XIO_HANDLE, xio, const char*, optionName, const void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, xio_retrieveoptions, XIO_HANDLE, xio); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/inc/azure_c_shared_utility/xlogging.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef XLOGGING_H +#define XLOGGING_H + +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/optimize_size.h" + +#if defined(ESP8266_RTOS) +#include "c_types.h" +#endif + +#if defined(ARDUINO_ARCH_ESP8266) +#include "esp8266/azcpgmspace.h" +#endif + +#ifdef __cplusplus +#include <cstdio> +extern "C" { +#else +#include <stdio.h> +#endif /* __cplusplus */ + +#ifdef TIZENRT +#undef LOG_INFO +#endif + +typedef enum LOG_CATEGORY_TAG +{ + AZ_LOG_ERROR, + AZ_LOG_INFO, + AZ_LOG_TRACE +} LOG_CATEGORY; + +#if defined _MSC_VER +#define FUNC_NAME __FUNCDNAME__ +#else +#define FUNC_NAME __func__ +#endif + +typedef void(*LOGGER_LOG)(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...); +typedef void(*LOGGER_LOG_GETLASTERROR)(const char* file, const char* func, int line, const char* format, ...); + +#define TEMP_BUFFER_SIZE 1024 +#define MESSAGE_BUFFER_SIZE 260 + +#define LOG_NONE 0x00 +#define LOG_LINE 0x01 + +/*no logging is useful when time and fprintf are mocked*/ +#ifdef NO_LOGGING +#define LOG(...) +#define LogInfo(...) +#define LogBinary(...) +#define LogError(...) +#define xlogging_get_log_function() NULL +#define xlogging_set_log_function(...) +#define LogErrorWinHTTPWithGetLastErrorAsString(...) +#define UNUSED(x) (void)(x) +#elif (defined MINIMAL_LOGERROR) +#define LOG(...) +#define LogInfo(...) +#define LogBinary(...) +#define LogError(...) printf("error %s: line %d\n",__FILE__,__LINE__); +#define xlogging_get_log_function() NULL +#define xlogging_set_log_function(...) +#define LogErrorWinHTTPWithGetLastErrorAsString(...) +#define UNUSED(x) (void)(x) + +#elif defined(ESP8266_RTOS) +#define LogInfo(FORMAT, ...) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = FORMAT; \ + printf(flash_str, ##__VA_ARGS__); \ + printf("\n");\ + } while((void)0,0) + +#define LogError LogInfo +#define LOG(log_category, log_options, FORMAT, ...) { \ + static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = (FORMAT); \ + printf(flash_str, ##__VA_ARGS__); \ + printf("\r\n"); \ +} + +#else /* NOT ESP8266_RTOS */ + +#if defined _MSC_VER +#define LOG(log_category, log_options, format, ...) { LOGGER_LOG l = xlogging_get_log_function(); if (l != NULL) l(log_category, __FILE__, FUNC_NAME, __LINE__, log_options, format, __VA_ARGS__); } +#else +#define LOG(log_category, log_options, format, ...) { LOGGER_LOG l = xlogging_get_log_function(); if (l != NULL) l(log_category, __FILE__, FUNC_NAME, __LINE__, log_options, format, ##__VA_ARGS__); } +#endif + +#if defined _MSC_VER +#define LogInfo(FORMAT, ...) do{LOG(AZ_LOG_INFO, LOG_LINE, FORMAT, __VA_ARGS__); }while((void)0,0) +#else +#define LogInfo(FORMAT, ...) do{LOG(AZ_LOG_INFO, LOG_LINE, FORMAT, ##__VA_ARGS__); }while((void)0,0) +#endif + +#if defined _MSC_VER + +#if !defined(WINCE) +extern void xlogging_set_log_function_GetLastError(LOGGER_LOG_GETLASTERROR log_function); +extern LOGGER_LOG_GETLASTERROR xlogging_get_log_function_GetLastError(void); +#define LogLastError(FORMAT, ...) do{ LOGGER_LOG_GETLASTERROR l = xlogging_get_log_function_GetLastError(); if(l!=NULL) l(__FILE__, FUNC_NAME, __LINE__, FORMAT, __VA_ARGS__); }while((void)0,0) +#endif + +#define LogError(FORMAT, ...) do{ LOG(AZ_LOG_ERROR, LOG_LINE, FORMAT, __VA_ARGS__); }while((void)0,0) +#define LogErrorWinHTTPWithGetLastErrorAsString(FORMAT, ...) do { \ + DWORD errorMessageID = GetLastError(); \ + char messageBuffer[MESSAGE_BUFFER_SIZE]; \ + LogError(FORMAT, __VA_ARGS__); \ + if (errorMessageID == 0) \ + {\ + LogError("GetLastError() returned 0. Make sure you are calling this right after the code that failed. "); \ + } \ + else\ + {\ + int size = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, \ + GetModuleHandle("WinHttp"), errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, MESSAGE_BUFFER_SIZE, NULL); \ + if (size == 0)\ + {\ + size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, MESSAGE_BUFFER_SIZE, NULL); \ + if (size == 0)\ + {\ + LogError("GetLastError Code: %d. ", errorMessageID); \ + }\ + else\ + {\ + LogError("GetLastError: %s.", messageBuffer); \ + }\ + }\ + else\ + {\ + LogError("GetLastError: %s.", messageBuffer); \ + }\ + }\ + } while((void)0,0) +#else +#define LogError(FORMAT, ...) do{ LOG(AZ_LOG_ERROR, LOG_LINE, FORMAT, ##__VA_ARGS__); }while((void)0,0) +#endif + +extern void LogBinary(const char* comment, const void* data, size_t size); + +extern void xlogging_set_log_function(LOGGER_LOG log_function); +extern LOGGER_LOG xlogging_get_log_function(void); + +#endif /* NOT ESP8266_RTOS */ + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + +#endif /* XLOGGING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/generic/refcount_os.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file gets included into refcount.h as a means of extending the behavior of +// atomic increment, decrement, and test. +// +// The Azure IoT C SDK does not require thread-safe refcount operations, so +// this file is appropriate for any device when using the Azure IoT C SDK. + +#ifndef REFCOUNT_OS_H__GENERIC +#define REFCOUNT_OS_H__GENERIC + +#define COUNT_TYPE uint32_t + +#define DEC_RETURN_ZERO (0) +#define INC_REF(type, var) ++((((REFCOUNT_TYPE(type)*)var)->count)) +#define DEC_REF(type, var) --((((REFCOUNT_TYPE(type)*)var)->count)) +#define INIT_REF(type, var) do { ((REFCOUNT_TYPE(type)*)var)->count = 1; } while((void)0,0) + +#endif // REFCOUNT_OS_H__GENERIC
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/inc/dns_async.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file ssl_socket.h + * @brief Implements socket creation for TLSIO adapters. + */ + +#ifndef AZURE_IOT_DNS_H +#define AZURE_IOT_DNS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef void* DNS_ASYNC_HANDLE; + + // If options are added in future, DNS_ASYNC_OPTIONS will become a struct containing the options + typedef void DNS_ASYNC_OPTIONS; + + /** + * @brief Begin the process of an asynchronous DNS lookup. + * + * @param hostname The url of the host to look up. + * + * @return @c The newly created DNS_ASYNC_HANDLE. + */ + MOCKABLE_FUNCTION(, DNS_ASYNC_HANDLE, dns_async_create, const char*, hostname, DNS_ASYNC_OPTIONS*, options); + + /** + * @brief Continue the lookup process and report its completion state. Must be polled repeatedly for completion. + * + * @param dns The DNS_ASYNC_HANDLE. + * + * @return @c A bool to indicate completion. + */ + MOCKABLE_FUNCTION(, bool, dns_async_is_lookup_complete, DNS_ASYNC_HANDLE, dns); + + /** + * @brief Return the IPv4 of a completed lookup process. Call only after dns_async_is_lookup_complete indicates completion. + * + * @param dns The DNS_ASYNC_HANDLE. + * + * @return @c A uint32_t IPv4 address. 0 indicates failure or not finished. + */ + MOCKABLE_FUNCTION(, uint32_t, dns_async_get_ipv4, DNS_ASYNC_HANDLE, dns); + + /** + * @brief Destroy the module. + * + * @param dns The DNS_ASYNC_HANDLE. + */ + MOCKABLE_FUNCTION(, void, dns_async_destroy, DNS_ASYNC_HANDLE, dns); + + +#ifdef __cplusplus +} +#endif + +#endif /* AZURE_IOT_DNS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/inc/sntp.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file threadapi.h + * @brief This module implements support for creating new threads, + * terminating threads and sleeping threads. + */ + +#ifndef AZURE_IOT_SNTP_H +#define AZURE_IOT_SNTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + +/** + * @brief Set the url for the ntp server to be used. Must be called before + * SNTP_Init. + * + * @param serverName The url of the ntp server to be used. The char array + * passed in must remain valid between the SNTP_SetServerName and + * the SNTP_Deinit calls. + * + * @return @c 0 if the API call is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, int, SNTP_SetServerName, const char*, serverName); + +/** + * @brief Performs platform-specific sntp initialization, then loops until + * system time has been set from the ntp server. + * + * + * @return @c 0 if the API call is successful or an error + * code in case it fails. + */ +MOCKABLE_FUNCTION(, int, SNTP_Init); + +/** + * @brief This function is called by a thread when the thread exits. + */ +MOCKABLE_FUNCTION(, void, SNTP_Deinit); + + +#ifdef __cplusplus +} +#endif + +#endif /* AZURE_IOT_SNTP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/inc/socket_async.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file socket_async.h + * @brief Abstracts non-blocking sockets. + */ + +#ifndef AZURE_SOCKET_ASYNC_H +#define AZURE_SOCKET_ASYNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +// socket_async exposes asynchronous socket operations while hiding OS-specifics. Committing to +// asynchronous operation also simplifies the interface compared to generic sockets. + +#define SOCKET_ASYNC_INVALID_SOCKET -1 + +typedef struct +{ + // Keepalive is disabled by default at the TCP level. If keepalive is desired, it + // is strongly recommended to use one of the higher level keepalive (ping) options rather + // than the TCP level because the higher level options provide server connection status + // in addition to keeping the connection open. + int keep_alive; // < 0 for system defaults, >= 0 to use supplied keep_alive, idle, interval, and count + int keep_idle; // seconds before first keepalive packet (ignored if keep_alive <= 0) + int keep_interval; // seconds between keepalive packets (ignored if keep_alive <= 0) + int keep_count; // number of times to try before declaring failure (ignored if keep_alive <= 0) + // It is acceptable to extend this struct by adding members for future enhancement, + // but existing members must not be altered to ensure back-compatibility. +} SOCKET_ASYNC_OPTIONS; +typedef SOCKET_ASYNC_OPTIONS* SOCKET_ASYNC_OPTIONS_HANDLE; + +typedef int SOCKET_ASYNC_HANDLE; + +/** +* @brief Create a non-blocking socket that is correctly configured for asynchronous use. +* +* @param sock Receives the created SOCKET_ASYNC_HANDLE. +* +* @param host_ipv4 The IPv4 of the SSL server to be contacted. +* +* @param port The port of the SSL server to use. +* +* @param is_UDP True for UDP, false for TCP. +* +* @param options A pointer to SOCKET_ASYNC_OPTIONS used during socket_async_create +* for TCP connections only. May be NULL. Ignored for UDP sockets. +* Need only exist for the duration of the socket_async_create call. +* +* @return @c The created and configured SOCKET_ASYNC_HANDLE if the API call is successful +* or SOCKET_ASYNC_INVALID_SOCKET in case it fails. Error logging is +* performed by the underlying concrete implementation, so no +* further error logging is necessary. +*/ +MOCKABLE_FUNCTION(, SOCKET_ASYNC_HANDLE, socket_async_create, uint32_t, host_ipv4, uint16_t, port, bool, is_UDP, SOCKET_ASYNC_OPTIONS_HANDLE, options); + +/** +* @brief Check whether a newly-created socket_async has completed its initial connection. +* +* @param sock The created SOCKET_ASYNC_HANDLE to check for connection completion. +* +* @param is_created Receives the completion state if successful, set to false on failure. +* +* @return @c 0 if the API call is successful. +* __FAILURE__ means an unexpected error has occurred and the socket must be destroyed. +*/ +MOCKABLE_FUNCTION(, int, socket_async_is_create_complete, SOCKET_ASYNC_HANDLE, sock, bool*, is_complete); + +/** +* @brief Send a message on the specified socket. +* +* @param sock The socket to be used. +* +* @param buffer The buffer containing the message to transmit. +* +* @param size The number of bytes to transmit. +* +* @param sent_count Receives the number of bytes transmitted. The N == 0 +* case means normal operation but the socket's outgoing buffer was full. +* +* @return @c 0 if successful. +* __FAILURE__ means an unexpected error has occurred and the socket must be destroyed. +*/ +MOCKABLE_FUNCTION(, int, socket_async_send, SOCKET_ASYNC_HANDLE, sock, const void*, buffer, size_t, size, size_t*, sent_count); + +/** +* @brief Receive a message on the specified socket. +* +* @param sock The socket to be used. +* +* @param buffer The buffer containing the message to receive. +* +* @param size The buffer size in bytes. +* +* @param received_count Receives the number of bytes received into the buffer. +* +* @return @c 0 if successful. +* __FAILURE__ means an unexpected error has occurred and the socket must be destroyed. +*/ +MOCKABLE_FUNCTION(, int, socket_async_receive, SOCKET_ASYNC_HANDLE, sock, void*, buffer, size_t, size, size_t*, received_count); + + +/** +* @brief Close the socket returned by socket_async_create. +* +* @param sock The socket to be destroyed (closed, in standard socket terms). +*/ +MOCKABLE_FUNCTION(, void, socket_async_destroy, SOCKET_ASYNC_HANDLE, sock); + + +#ifdef __cplusplus +} +#endif + +#endif /* AZURE_SOCKET_ASYNC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/tlsio_options.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,313 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/tlsio_options.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" + + +// Initialize the TLSIO_OPTIONS struct +void tlsio_options_initialize(TLSIO_OPTIONS* options, int supported_options) +{ + // Using static function rules, so 'options' is not checked for NULL + // + // The supported_options value does not need validation because undefined bits are + // ignored, while any valid missing bits result in an "option not supported" error + // that will show up in unit testing. + options->supported_options = supported_options; + options->trusted_certs = NULL; + options->x509_type = TLSIO_OPTIONS_x509_TYPE_UNSPECIFIED; + options->x509_cert = NULL; + options->x509_key = NULL; +} + +static int set_and_validate_x509_type(TLSIO_OPTIONS* options, TLSIO_OPTIONS_x509_TYPE x509_type) +{ + int result; + if ((options->supported_options & x509_type) == 0) + { + // This case also rejects the nonsensical TLSIO_OPTIONS_x509_TYPE_UNSPECIFIED + LogError("Unsupported x509 type: %d", x509_type); + result = __FAILURE__; + } + else if (options->x509_type == TLSIO_OPTIONS_x509_TYPE_UNSPECIFIED) + { + // Initial type setting + options->x509_type = x509_type; + result = 0; + } + else if (options->x509_type != x509_type) + { + LogError("Supplied x509 type conflicts with previously set x509"); + result = __FAILURE__; + } + else + { + // The types match okay + result = 0; + } + + return result; +} + +void tlsio_options_release_resources(TLSIO_OPTIONS* options) +{ + if (options != NULL) + { + free((void*)options->trusted_certs); + free((void*)options->x509_cert); + free((void*)options->x509_key); + } + else + { + LogError("NULL options"); + } +} + +static bool is_supported_string_option(const char* name) +{ + return + (strcmp(name, OPTION_TRUSTED_CERT) == 0) || + (strcmp(name, OPTION_OPENSSL_CIPHER_SUITE) == 0) || + (strcmp(name, SU_OPTION_X509_CERT) == 0) || + (strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0) || + (strcmp(name, OPTION_X509_ECC_CERT) == 0) || + (strcmp(name, OPTION_X509_ECC_KEY) == 0); +} + +TLSIO_OPTIONS_RESULT tlsio_options_destroy_option(const char* name, const void* value) +{ + TLSIO_OPTIONS_RESULT result; + if (name == NULL || value == NULL) + { + LogError("NULL parameter: name: %p, value: %p", name, value); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (is_supported_string_option(name)) + { + free((void*)value); + result = TLSIO_OPTIONS_RESULT_SUCCESS; + } + else + { + result = TLSIO_OPTIONS_RESULT_NOT_HANDLED; + } + return result; +} + +TLSIO_OPTIONS_RESULT tlsio_options_clone_option(const char* name, const void* value, void** out_value) +{ + TLSIO_OPTIONS_RESULT result; + + if (name == NULL || value == NULL || out_value == NULL) + { + LogError("NULL parameter: name: %p, value: %p, out_value: %p", + name, value, out_value); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (is_supported_string_option(name)) + { + *out_value = NULL; + if (mallocAndStrcpy_s((char**)out_value, value) != 0) + { + LogError("unable to mallocAndStrcpy_s option value"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else + { + result = TLSIO_OPTIONS_RESULT_SUCCESS; + } + } + else + { + result = TLSIO_OPTIONS_RESULT_NOT_HANDLED; + } + return result; +} + +TLSIO_OPTIONS_RESULT tlsio_options_set(TLSIO_OPTIONS* options, + const char* optionName, const void* value) +{ + TLSIO_OPTIONS_RESULT result; + char* copied_value = NULL; + + if (options == NULL || optionName == NULL || value == NULL) + { + LogError("NULL parameter: options: %p, optionName: %p, value: %p", + options, optionName, value); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (!is_supported_string_option(optionName)) + { + result = TLSIO_OPTIONS_RESULT_NOT_HANDLED; + } + else if(mallocAndStrcpy_s(&copied_value, value) != 0) + { + LogError("unable to mallocAndStrcpy_s option value"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) + { + if ((options->supported_options & TLSIO_OPTION_BIT_TRUSTED_CERTS) == 0) + { + LogError("Trusted certs option not supported"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (options->trusted_certs != NULL) + { + LogError("unable to set trusted cert option more than once"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else + { + options->trusted_certs = copied_value; + result = TLSIO_OPTIONS_RESULT_SUCCESS; + } + } + else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0 || strcmp(OPTION_X509_ECC_CERT, optionName) == 0) + { + TLSIO_OPTIONS_x509_TYPE this_type = (strcmp(SU_OPTION_X509_CERT, optionName) == 0) ? TLSIO_OPTIONS_x509_TYPE_RSA : TLSIO_OPTIONS_x509_TYPE_ECC; + if (options->x509_cert != NULL) + { + LogError("unable to set x509 cert more than once"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (set_and_validate_x509_type(options, this_type) != 0) + { + // Error logged by helper + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else + { + options->x509_cert = copied_value; + result = TLSIO_OPTIONS_RESULT_SUCCESS; + } + } + else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0 || strcmp(OPTION_X509_ECC_KEY, optionName) == 0) + { + TLSIO_OPTIONS_x509_TYPE this_type = (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) ? TLSIO_OPTIONS_x509_TYPE_RSA : TLSIO_OPTIONS_x509_TYPE_ECC; + if (options->x509_key != NULL) + { + LogError("unable to set x509 key more than once"); + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else if (set_and_validate_x509_type(options, this_type) != 0) + { + // Error logged by helper + result = TLSIO_OPTIONS_RESULT_ERROR; + } + else + { + options->x509_key = copied_value; + result = TLSIO_OPTIONS_RESULT_SUCCESS; + } + } + else + { + // This is logically impossible due to earlier tests, so just quiet the compiler + result = TLSIO_OPTIONS_RESULT_ERROR; + } + + if (result != TLSIO_OPTIONS_RESULT_SUCCESS) + { + free(copied_value); + } + + return result; +} + +// A helper that works if the tlsio does not use any extra options +static void* local_clone_option(const char* name, const void* value) +{ + void* result = NULL; + if (tlsio_options_clone_option(name, value, &result) != TLSIO_OPTIONS_RESULT_SUCCESS) + { + LogError("Unexpected local_clone_option failure"); + } + return result; +} + +// A helper that works if the tlsio does not use any extra options +void local_destroy_option(const char* name, const void* value) +{ + if (tlsio_options_destroy_option(name, value) != TLSIO_OPTIONS_RESULT_SUCCESS) + { + LogError("Unexpected local_destroy_option failure"); + } +} + +OPTIONHANDLER_HANDLE tlsio_options_retrieve_options(TLSIO_OPTIONS* options, pfSetOption setOption) +{ + return tlsio_options_retrieve_options_ex(options, local_clone_option, local_destroy_option, setOption); +} + + +OPTIONHANDLER_HANDLE tlsio_options_retrieve_options_ex(TLSIO_OPTIONS* options, + pfCloneOption cloneOption, pfDestroyOption destroyOption, pfSetOption setOption) +{ + OPTIONHANDLER_HANDLE result; + if (options == NULL || cloneOption == NULL || destroyOption == NULL || setOption == NULL) + { + LogError("Null parameter in options: %p, cloneOption: %p, destroyOption: %p, setOption: %p", + options, cloneOption, destroyOption, setOption); + result = NULL; + } + else + { + result = OptionHandler_Create(cloneOption, destroyOption, setOption); + if (result == NULL) + { + LogError("OptionHandler_Create failed"); + /*return as is*/ + } + else if ( + (options->trusted_certs != NULL) && + (OptionHandler_AddOption(result, OPTION_TRUSTED_CERT, options->trusted_certs) != OPTIONHANDLER_OK) + ) + { + LogError("unable to save TrustedCerts option"); + OptionHandler_Destroy(result); + result = NULL; + } + else if (options->x509_type != TLSIO_OPTIONS_x509_TYPE_UNSPECIFIED) + { + const char* x509_cert_option; + const char* x509_key_option; + if (options->x509_type == TLSIO_OPTIONS_x509_TYPE_ECC) + { + x509_cert_option = OPTION_X509_ECC_CERT; + x509_key_option = OPTION_X509_ECC_KEY; + } + else + { + x509_cert_option = SU_OPTION_X509_CERT; + x509_key_option = SU_OPTION_X509_PRIVATE_KEY; + } + if ( + (options->x509_cert != NULL) && + (OptionHandler_AddOption(result, x509_cert_option, options->x509_cert) != OPTIONHANDLER_OK) + ) + { + LogError("unable to save x509 cert option"); + OptionHandler_Destroy(result); + result = NULL; + } + else if ( + (options->x509_key != NULL) && + (OptionHandler_AddOption(result, x509_key_option, options->x509_key) != OPTIONHANDLER_OK) + ) + { + LogError("unable to save x509 key option"); + OptionHandler_Destroy(result); + result = NULL; + } + } + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/pal/windows/refcount_os.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file gets included into refcount.h as a means of extending the behavior of +// atomic increment, decrement, and test. + +// The first phase defines COUNT_TYPE +#ifndef REFCOUNT_OS_H__WINDOWS +#define REFCOUNT_OS_H__WINDOWS + +#include "windows.h" +// The Windows atomic operations work on LONG +#define COUNT_TYPE LONG + +/*if macro DEC_REF returns DEC_RETURN_ZERO that means the ref count has reached zero.*/ +#define DEC_RETURN_ZERO (0) +#define INC_REF(type, var) InterlockedIncrement(&(((REFCOUNT_TYPE(type)*)var)->count)) +#define DEC_REF(type, var) InterlockedDecrement(&(((REFCOUNT_TYPE(type)*)var)->count)) +#define INIT_REF(type, var) InterlockedExchange(&(((REFCOUNT_TYPE(type)*)var)->count), 1) + +#endif // REFCOUNT_OS_H__WINDOWS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/README.md Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,22 @@ +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +# azure-c-shared-utility/adapters + +This directory contains files for adapting specific devices to work with the Azure C Shared Utility. + +### This location is deprecated + +The files in this directory will eventually be moved to new locations in the + [azure-c-shared-utility/pal](https://github.com/Azure/azure-c-shared-utility/tree/master/pal) +directory. + +### Where did that file go? + +Files related to adapting the Azure IoT C SDK to specific devices are being moved to new locations +within the [azure-c-shared-utility/pal](https://github.com/Azure/azure-c-shared-utility/tree/master/pal) +directory and its subfolders. + +### Porting to new devices + +Instructions for porting the Azure IoT C SDK to new devices are located +[here](https://github.com/Azure/azure-c-shared-utility/blob/pal-porting/devdoc/porting_guide.md).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/aziotsharedutil.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,298 @@ +LIBRARY aziotsharedutil +EXPORTS + BUFFER_append + BUFFER_append_build + BUFFER_build + BUFFER_clone + BUFFER_content + BUFFER_create + BUFFER_delete + BUFFER_enlarge + BUFFER_length + BUFFER_new + BUFFER_pre_build + BUFFER_prepend + BUFFER_shrink + BUFFER_size + BUFFER_u_char + BUFFER_unbuild + Base64_Decoder + Base64_Encoder + Base64_Encode_Bytes + Base32_Decode + Base32_Decode_String + Base32_Encode + Base32_Encode_Bytes + COND_RESULTStringStorage + COND_RESULTStrings + COND_RESULT_FromString + CONSTBUFFER_Clone + CONSTBUFFER_Create + CONSTBUFFER_CreateFromBuffer + CONSTBUFFER_Destroy + CONSTBUFFER_GetContent + CONSTMAP_RESULTStringStorage + CONSTMAP_RESULTStrings + CONSTMAP_RESULT_FromString + Condition_Deinit + Condition_Init + Condition_Post + Condition_Wait + ConstMap_Clone + ConstMap_CloneWriteable + ConstMap_ContainsKey + ConstMap_ContainsValue + ConstMap_Create + ConstMap_Destroy + ConstMap_GetInternals + ConstMap_GetValue + DList_AppendTailList + DList_InitializeListHead + DList_InsertHeadList + DList_InsertTailList + DList_IsListEmpty + DList_RemoveEntryList + DList_RemoveHeadList + environment_get_variable + HMACSHA256_ComputeHash + HTTPAPIEX_Create + HTTPAPIEX_Destroy + HTTPAPIEX_ExecuteRequest + HTTPAPIEX_RESULTStringStorage + HTTPAPIEX_RESULTStrings + HTTPAPIEX_RESULT_FromString + HTTPAPIEX_SAS_Create + HTTPAPIEX_SAS_Destroy + HTTPAPIEX_SAS_ExecuteRequest + HTTPAPIEX_SetOption + HTTPAPI_CloneOption + HTTPAPI_CloseConnection + HTTPAPI_CreateConnection + HTTPAPI_Deinit + HTTPAPI_ExecuteRequest + HTTPAPI_Init + HTTPAPI_RESULTStringStorage + HTTPAPI_RESULTStrings + HTTPAPI_RESULT_FromString + HTTPAPI_SetOption + HTTPHeaders_AddHeaderNameValuePair + HTTPHeaders_Alloc + HTTPHeaders_Clone + HTTPHeaders_FindHeaderValue + HTTPHeaders_Free + HTTPHeaders_GetHeader + HTTPHeaders_GetHeaderCount + HTTPHeaders_ReplaceHeaderNameValuePair + HTTP_HEADERS_RESULTStringStorage + HTTP_HEADERS_RESULTStrings + HTTP_HEADERS_RESULT_FromString + Lock + Lock_Deinit + Lock_Init + MAP_RESULTStringStorage + MAP_RESULTStrings + MAP_RESULT_FromString + Map_Add + Map_AddOrUpdate + Map_Clone + Map_ContainsKey + Map_ContainsValue + Map_Create + Map_Delete + Map_Destroy + Map_GetInternals + Map_GetValueFromKey + Map_ToJSON + OptionHandler_AddOption + OptionHandler_Clone + OptionHandler_Create + OptionHandler_Destroy + OptionHandler_FeedOptions + SASToken_Create + SASToken_CreateString + SASToken_Validate + SHA1FinalBits + SHA1Input + SHA1Reset + SHA1Result + SHA224FinalBits + SHA224Input + SHA224Reset + SHA224Result + SHA256FinalBits + SHA256Input + SHA256Reset + SHA256Result + SHA384FinalBits + SHA384Input + SHA384Reset + SHA384Result + SHA512FinalBits + SHA512Input + SHA512Reset + SHA512Result + STRING_TOKENIZER_create + STRING_TOKENIZER_create_from_char + STRING_TOKENIZER_destroy + STRING_TOKENIZER_get_next_token + STRING_c_str + STRING_clone + STRING_compare + STRING_concat + STRING_concat_with_STRING + STRING_construct + STRING_construct_n + STRING_construct_sprintf + STRING_copy + STRING_copy_n + STRING_delete + STRING_empty + STRING_from_byte_array + STRING_length + STRING_new + STRING_new_JSON + STRING_new_quoted + STRING_new_with_memory + STRING_quote + STRING_sprintf + STRING_replace + THREADAPI_RESULTStringStorage + THREADAPI_RESULTStrings + THREADAPI_RESULT_FromString + TLSIO_STATE_FromString + TLSIO_STATEStrings + ThreadAPI_Create + ThreadAPI_Exit + ThreadAPI_Join + ThreadAPI_Sleep + UNIQUEID_RESULTStringStorage + UNIQUEID_RESULTStrings + UNIQUEID_RESULT_FromString + URL_Encode + URL_EncodeString + URL_Decode + URL_DecodeString + USHABlockSize + USHAFinalBits + USHAHashSize + USHAHashSizeBits + USHAInput + USHAReset + USHAResult + UniqueId_Generate + Unlock + UUID_generate + UUID_from_string + UUID_to_string + VECTOR_back + VECTOR_clear + VECTOR_create + VECTOR_destroy + VECTOR_element + VECTOR_erase + VECTOR_find_if + VECTOR_front + VECTOR_move + VECTOR_push_back + VECTOR_size + connectionstringparser_parse + connectionstringparser_parse_from_char + connectionstringparser_splitHostName + connectionstringparser_splitHostName_from_char + consolelogger_log + consolelogger_log_with_GetLastError + gb_rand + gballoc_calloc + gballoc_deinit + gballoc_free + gballoc_getCurrentMemoryUsed + gballoc_getMaximumMemoryUsed + gballoc_init + gballoc_malloc + gballoc_realloc + gbnetwork_init + gbnetwork_deinit + get_ctime + get_difftime + get_gmtime + get_mktime + get_time + global_log_function + hmac + hmacFinalBits + hmacInput + hmacReset + hmacResult + http_proxy_io_get_interface_description + mallocAndStrcpy_s + platform_deinit + platform_get_default_tlsio + platform_get_platform_info + platform_init + singlylinkedlist_add + singlylinkedlist_create + singlylinkedlist_destroy + singlylinkedlist_find + singlylinkedlist_get_head_item + singlylinkedlist_get_next_item + singlylinkedlist_item_get_value + singlylinkedlist_remove + singlylinkedlist_remove_if + singlylinkedlist_foreach + size_tToString + socketio_close + socketio_create + socketio_destroy + socketio_dowork + socketio_get_interface_description + socketio_open + socketio_send + socketio_setoption + tickcounter_create + tickcounter_destroy + tickcounter_get_current_ms + tlsio_schannel_close + tlsio_schannel_create + tlsio_schannel_destroy + tlsio_schannel_dowork + tlsio_schannel_get_interface_description + tlsio_schannel_open + tlsio_schannel_send + tlsio_schannel_setoption + unsignedIntToString + utf8_checker_is_valid_utf8 + uws_client_close_async + uws_client_close_handshake_async + uws_client_create + uws_client_create_with_io + uws_client_destroy + uws_client_dowork + uws_client_open_async + uws_client_retrieve_options + uws_client_send_frame_async + uws_client_set_option + uws_frame_encoder_encode + wsio_close + wsio_create + wsio_destroy + wsio_dowork + wsio_get_interface_description + wsio_open + wsio_retrieveoptions + wsio_send + wsio_setoption + x509_schannel_create + x509_schannel_destroy + x509_schannel_get_certificate_context + xio_close + xio_create + xio_destroy + xio_dowork + xio_open + xio_retrieveoptions + xio_send + xio_setoption + xlogging_get_log_function + xlogging_get_log_function_GetLastError + xlogging_set_log_function + xlogging_set_log_function_GetLastError
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/aziotsharedutil_http.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,34 @@ + HTTPHeaders_AddHeaderNameValuePair + HTTPHeaders_Alloc + HTTPHeaders_Clone + HTTPHeaders_FindHeaderValue + HTTPHeaders_Free + HTTPHeaders_GetHeader + HTTPHeaders_GetHeaderCount + HTTPHeaders_ReplaceHeaderNameValuePair + HTTP_HEADERS_RESULTStringStorage + HTTP_HEADERS_RESULTStrings + HTTP_HEADERS_RESULT_FromString + + HTTPAPIEX_Create + HTTPAPIEX_Destroy + HTTPAPIEX_ExecuteRequest + HTTPAPIEX_RESULTStringStorage + HTTPAPIEX_RESULTStrings + HTTPAPIEX_RESULT_FromString + HTTPAPIEX_SAS_Create + HTTPAPIEX_SAS_Create_From_String + HTTPAPIEX_SAS_Destroy + HTTPAPIEX_SAS_ExecuteRequest + HTTPAPIEX_SetOption + + HTTPAPI_CloneOption + HTTPAPI_CloseConnection + HTTPAPI_CreateConnection + HTTPAPI_Deinit + HTTPAPI_ExecuteRequest + HTTPAPI_Init + HTTPAPI_RESULTStringStorage + HTTPAPI_RESULTStrings + HTTPAPI_RESULT_FromString + HTTPAPI_SetOption
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/aziotsharedutil_wsio.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,21 @@ + utf8_checker_is_valid_utf8 + uws_client_close_async + uws_client_close_handshake_async + uws_client_create + uws_client_create_with_io + uws_client_destroy + uws_client_dowork + uws_client_open_async + uws_client_retrieve_options + uws_client_send_frame_async + uws_client_set_option + uws_frame_encoder_encode + wsio_close + wsio_create + wsio_destroy + wsio_dowork + wsio_get_interface_description + wsio_open + wsio_retrieveoptions + wsio_send + wsio_setoption
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/base32.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,392 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <ctype.h> +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/strings.h" + +#include "azure_c_shared_utility/base32.h" + +static const unsigned char BASE32_EQUAL_SIGN = 32; + +static const char BASE32_VALUES[] = "abcdefghijklmnopqrstuvwxyz234567="; +#define TARGET_BLOCK_SIZE 5 +#define INVALID_CHAR_POS 260 + +#define BASE32_INPUT_SIZE 8 + +#define ASCII_VALUE_MAX 0x80 + +static size_t base32_encoding_length(size_t src_len) +{ + return (((src_len + TARGET_BLOCK_SIZE - 1) / TARGET_BLOCK_SIZE) * 8); +} + +static size_t base32_decoding_length(size_t src_len) +{ + return ((src_len*TARGET_BLOCK_SIZE) / 8); +} + +static unsigned char convert_value_to_base32_char(unsigned char value) +{ + unsigned char result; + + if (value >= 50 && value <= 55) + { + result = 0x1a+(value-50); + } + else if (value == 61) + { + result = 0x20; + } + else if ((value <= 49) || (value >= 56 && value <= 64)) + { + result = 0xFF; + } + else if (value >= 65 && value <= 90) + { + result = 0x00 + (value - 65); + } + else if (value >= 91 && value <= 96) + { + result = 0xFF; + } + else if (value >= 97 && value <= 123) + { + result = 0x00 + (value - 97); + } + else // value > 123 + { + result = 0xFF; + } + return result; +} + +static char* base32_encode_impl(const unsigned char* source, size_t src_size) +{ + char* result; + + // Allocate target buffer + size_t output_len = base32_encoding_length(src_size); + /* Codes_SRS_BASE32_07_009: [ base32_encode_impl shall allocate the buffer to the size of the encoding value. ] */ + if ((result = (char*)malloc(output_len + 1)) == NULL) + { + LogError("Failure allocating output buffer"); + } + else + { + const unsigned char* iterator = source; + size_t block_len = 0; + size_t result_len = 0; + unsigned char pos1 = 0; + unsigned char pos2 = 0; + unsigned char pos3 = 0; + unsigned char pos4 = 0; + unsigned char pos5 = 0; + unsigned char pos6 = 0; + unsigned char pos7 = 0; + unsigned char pos8 = 0; + + memset(result, 0, output_len + 1); + + // Go through the source buffer sectioning off blocks of 5 + /* Codes_SRS_BASE32_07_010: [ base32_encode_impl shall look through source and separate each block into 5 bit chunks ] */ + while (src_size >= 1 && result != NULL) + { + pos1 = pos2 = pos3 = pos4 = pos5 = pos6 = pos7 = pos8 = 0; + block_len = src_size > TARGET_BLOCK_SIZE ? TARGET_BLOCK_SIZE : src_size; + // Fall through switch block to process the 5 (or smaller) block + switch (block_len) + { + case 5: + pos8 = (iterator[4] & 0x1f); + pos7 = ((iterator[4] & 0xe0) >> 5); + // fall through + case 4: + pos7 |= ((iterator[3] & 0x03) << 3); + pos6 = ((iterator[3] & 0x7c) >> 2); + pos5 = ((iterator[3] & 0x80) >> 7); + // fall through + case 3: + pos5 |= ((iterator[2] & 0x0f) << 1); + pos4 = ((iterator[2] & 0xf0) >> 4); + // fall through + case 2: + pos4 |= ((iterator[1] & 0x01) << 4); + pos3 = ((iterator[1] & 0x3e) >> 1); + pos2 = ((iterator[1] & 0xc0) >> 6); + // fall through + case 1: + pos2 |= ((iterator[0] & 0x07) << 2); + pos1 = ((iterator[0] & 0xf8) >> 3); + break; + } + // Move the iterator the block size + iterator += block_len; + // and decrement the src_size; + src_size -= block_len; + + /* Codes_SRS_BASE32_07_012: [ If the src_size is not divisible by 8, base32_encode_impl shall pad the remaining places with =. ] */ + switch (block_len) + { + case 1: pos3 = pos4 = 32; // fall through + case 2: pos5 = 32; // fall through + case 3: pos6 = pos7 = 32; // fall through + case 4: pos8 = 32; // fall through + case 5: + break; + } + + /* Codes_SRS_BASE32_07_011: [ base32_encode_impl shall then map the 5 bit chunks into one of the BASE32 values (a-z,2,3,4,5,6,7) values. ] */ + result[result_len++] = BASE32_VALUES[pos1]; + result[result_len++] = BASE32_VALUES[pos2]; + result[result_len++] = BASE32_VALUES[pos3]; + result[result_len++] = BASE32_VALUES[pos4]; + result[result_len++] = BASE32_VALUES[pos5]; + result[result_len++] = BASE32_VALUES[pos6]; + result[result_len++] = BASE32_VALUES[pos7]; + result[result_len++] = BASE32_VALUES[pos8]; + } + } + return result; +} + +static BUFFER_HANDLE base32_decode_impl(const char* source) +{ + BUFFER_HANDLE result; + + size_t src_length = strlen(source); + if (src_length % BASE32_INPUT_SIZE != 0) + { + /* Codes_SRS_BASE32_07_021: [ If the source length is not evenly divisible by 8, base32_decode_impl shall return NULL. ] */ + LogError("Failure invalid input length %lu", src_length); + result = NULL; + } + else + { + size_t dest_size = 0; + unsigned char* temp_buffer; + unsigned char* dest_buff; + bool continue_processing = true; + unsigned char input[8]; + const char* iterator = source; + + /* Codes_SRS_BASE32_07_022: [ base32_decode_impl shall allocate a temp buffer to store the in process value. ] */ + size_t allocation_len = base32_decoding_length(src_length); + if ((temp_buffer = (unsigned char*)malloc(allocation_len)) == NULL) + { + /* Codes_SRS_BASE32_07_023: [ If an error is encountered, base32_decode_impl shall return NULL. ] */ + LogError("Failure allocating buffer"); + result = NULL; + } + else + { + dest_buff = temp_buffer; + while (*iterator != '\0') + { + size_t index = 0; + /* Codes_SRS_BASE32_07_024: [ base32_decode_impl shall loop through and collect 8 characters from the source variable. ] */ + for (index = 0; index < BASE32_INPUT_SIZE; index++) + { + input[index] = *iterator; + iterator++; + if (input[index] >= ASCII_VALUE_MAX) + { + LogError("Failure source encoding"); + continue_processing = false; + break; + } + + input[index] = convert_value_to_base32_char(input[index]); + } + + if (!continue_processing) + { + result = NULL; + break; + } + else if ((dest_size + TARGET_BLOCK_SIZE) > allocation_len) + { + LogError("Failure target length exceeded"); + result = NULL; + continue_processing = false; + break; + } + else + { + // Codes_SRS_BASE32_07_025: [ base32_decode_impl shall group 5 bytes at a time into the temp buffer. ] + *dest_buff++ = ((input[0] & 0x1f) << 3) | ((input[1] & 0x1c) >> 2); + *dest_buff++ = ((input[1] & 0x03) << 6) | ((input[2] & 0x1f) << 1) | ((input[3] & 0x10) >> 4); + *dest_buff++ = ((input[3] & 0x0f) << 4) | ((input[4] & 0x1e) >> 1); + *dest_buff++ = ((input[4] & 0x01) << 7) | ((input[5] & 0x1f) << 2) | ((input[6] & 0x18) >> 3); + *dest_buff++ = ((input[6] & 0x07) << 5) | (input[7] & 0x1f); + dest_size += TARGET_BLOCK_SIZE; + // If there is padding remove it + // Because we are packing 5 bytes into an 8 byte variable we need to check every other + // variable for padding + if (input[7] == BASE32_EQUAL_SIGN) + { + --dest_size; + if (input[5] == BASE32_EQUAL_SIGN) + { + --dest_size; + if (input[4] == BASE32_EQUAL_SIGN) + { + --dest_size; + if (input[2] == BASE32_EQUAL_SIGN) + { + --dest_size; + } + } + } + } + } + } + + if (!continue_processing) + { + result = NULL; + } + else + { + /* Codes_SRS_BASE32_07_026: [ Once base32_decode_impl is complete it shall create a BUFFER with the temp buffer. ] */ + result = BUFFER_create(temp_buffer, dest_size); + if (result == NULL) + { + LogError("Failure: BUFFER_create failed to create decoded buffer"); + } + } + free(temp_buffer); + } + } + return result; +} + +BUFFER_HANDLE Base32_Decode(STRING_HANDLE handle) +{ + BUFFER_HANDLE result; + if (handle == NULL) + { + /* Codes_SRS_BASE32_07_016: [ If source is NULL Base32_Decoder shall return NULL. ] */ + LogError("invalid parameter handle"); + result = NULL; + } + else + { + const char* str_source = STRING_c_str(handle); + if (str_source == NULL) + { + /* Codes_SRS_BASE32_07_027: [ If the string in source value is NULL, Base32_Decoder shall return NULL. ] */ + LogError("NULL value specified in string"); + result = NULL; + } + else + { + /* Codes_SRS_BASE32_07_018: [ Base32_Decoder shall call base32_decode_impl to decode the base64 value. ] */ + result = base32_decode_impl(str_source); + } + } + /* Codes_SRS_BASE32_07_017: [ On success Base32_Decoder shall return a BUFFER_HANDLE that contains the decoded bytes for source. ] */ + return result; +} + +BUFFER_HANDLE Base32_Decode_String(const char* source) +{ + BUFFER_HANDLE result; + if (source == NULL) + { + /* Codes_SRS_BASE32_07_008: [ If source is NULL Base32_Decoder_String shall return NULL. ] */ + LogError("invalid parameter source=NULL"); + result = NULL; + } + else + { + /* Codes_SRS_BASE32_07_020: [ Base32_Decoder_String shall call base32_decode_impl to decode the base64 value. ] */ + result = base32_decode_impl(source); + } + /* Codes_SRS_BASE32_07_019: [ On success Base32_Decoder_String shall return a BUFFER_HANDLE that contains the decoded bytes for source. ] */ + return result; +} + +char* Base32_Encode_Bytes(const unsigned char* source, size_t size) +{ + char* result; + if (source == NULL) + { + /* Codes_SRS_BASE32_07_004: [ If source is NULL Base32_Encode shall return NULL. ] */ + result = NULL; + LogError("Failure: Invalid input parameter source"); + } + else if (size == 0) + { + /* Codes_SRS_BASE32_07_005: [ If size is 0 Base32_Encode shall return an empty string. ] */ + result = malloc(1); + strcpy(result, ""); + } + else + { + /* Codes_SRS_BASE32_07_007: [ Base32_Encode_Bytes shall call into base32_Encode_impl to encode the source data. ] */ + result = base32_encode_impl(source, size); + if (result == NULL) + { + /* Codes_SRS_BASE32_07_014: [ Upon failure Base32_Encode_Bytes shall return NULL. ] */ + LogError("encoding of unsigned char failed."); + } + } + /* Codes_SRS_BASE32_07_006: [ If successful Base32_Encode shall return the base32 value of input. ] */ + return result; +} + +STRING_HANDLE Base32_Encode(BUFFER_HANDLE source) +{ + STRING_HANDLE result; + if (source == NULL) + { + /* Codes_SRS_BASE32_07_001: [ If source is NULL Base32_Encode shall return NULL. ] */ + result = NULL; + LogError("Failure: Invalid input parameter"); + } + else + { + size_t input_len = BUFFER_length(source); + const unsigned char* input_value = BUFFER_u_char(source); + if (input_value == NULL || input_len == 0) + { + /* Codes_SRS_BASE32_07_015: [ If size is 0 Base32_Encode shall return an empty string. ] */ + result = STRING_new(); + if (result == NULL) + { + LogError("Failure constructing new string."); + } + } + else + { + /* Codes_SRS_BASE32_07_003: [ Base32_Encode shall call into base32_Encode_impl to encode the source data. ] */ + char* encoded = base32_encode_impl(input_value, input_len); + if (encoded == NULL) + { + /* Codes_SRS_BASE32_07_014: [ Upon failure base32_Encode shall return NULL. ] */ + LogError("base32 encode implementation failed."); + result = NULL; + } + else + { + /* Codes_SRS_BASE32_07_012: [ base32_Encode shall wrap the base32_Encode_impl result into a STRING_HANDLE. ] */ + result = STRING_construct(encoded); + if (result == NULL) + { + /* Codes_SRS_BASE32_07_014: [ Upon failure base32_Encode shall return NULL. ] */ + LogError("string construction failed."); + } + free(encoded); + } + } + } + /* Codes_SRS_BASE32_07_002: [ If successful Base32_Encode shall return the base32 value of source. ] */ + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/base64.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include <stddef.h> +#include <stdint.h> +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/xlogging.h" + + +#define splitInt(intVal, bytePos) (char)((intVal >> (bytePos << 3)) & 0xFF) +#define joinChars(a, b, c, d) (uint32_t)((uint32_t)a + ((uint32_t)b << 8) + ((uint32_t)c << 16) + ((uint32_t)d << 24)) + +static char base64char(unsigned char val) +{ + char result; + + if (val < 26) + { + result = 'A' + (char)val; + } + else if (val < 52) + { + result = 'a' + ((char)val - 26); + } + else if (val < 62) + { + result = '0' + ((char)val - 52); + } + else if (val == 62) + { + result = '+'; + } + else + { + result = '/'; + } + + return result; +} + +static char base64b16(unsigned char val) +{ + const uint32_t base64b16values[4] = { + joinChars('A', 'E', 'I', 'M'), + joinChars('Q', 'U', 'Y', 'c'), + joinChars('g', 'k', 'o', 's'), + joinChars('w', '0', '4', '8') + }; + return splitInt(base64b16values[val >> 2], (val & 0x03)); +} + +static char base64b8(unsigned char val) +{ + const uint32_t base64b8values = joinChars('A', 'Q', 'g', 'w'); + return splitInt(base64b8values, val); +} + +static int base64toValue(char base64character, unsigned char* value) +{ + int result = 0; + if (('A' <= base64character) && (base64character <= 'Z')) + { + *value = base64character - 'A'; + } + else if (('a' <= base64character) && (base64character <= 'z')) + { + *value = ('Z' - 'A') + 1 + (base64character - 'a'); + } + else if (('0' <= base64character) && (base64character <= '9')) + { + *value = ('Z' - 'A') + 1 + ('z' - 'a') + 1 + (base64character - '0'); + } + else if ('+' == base64character) + { + *value = 62; + } + else if ('/' == base64character) + { + *value = 63; + } + else + { + *value = 0; + result = -1; + } + return result; +} + +static size_t numberOfBase64Characters(const char* encodedString) +{ + size_t length = 0; + unsigned char junkChar; + while (base64toValue(encodedString[length],&junkChar) != -1) + { + length++; + } + return length; +} + +/*returns the count of original bytes before being base64 encoded*/ +/*notice NO validation of the content of encodedString. Its length is validated to be a multiple of 4.*/ +static size_t Base64decode_len(const char *encodedString) +{ + size_t result; + size_t sourceLength = strlen(encodedString); + + if (sourceLength == 0) + { + result = 0; + } + else + { + result = sourceLength / 4 * 3; + if (encodedString[sourceLength - 1] == '=') + { + if (encodedString[sourceLength - 2] == '=') + { + result --; + } + result--; + } + } + return result; +} + +static void Base64decode(unsigned char *decodedString, const char *base64String) +{ + + size_t numberOfEncodedChars; + size_t indexOfFirstEncodedChar; + size_t decodedIndex; + + // + // We can only operate on individual bytes. If we attempt to work + // on anything larger we could get an alignment fault on some + // architectures + // + + numberOfEncodedChars = numberOfBase64Characters(base64String); + indexOfFirstEncodedChar = 0; + decodedIndex = 0; + while (numberOfEncodedChars >= 4) + { + unsigned char c1; + unsigned char c2; + unsigned char c3; + unsigned char c4; + (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 3], &c4); + decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4); + decodedIndex++; + decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2); + decodedIndex++; + decodedString[decodedIndex] = ((c3 & 0x03) << 6) | c4; + decodedIndex++; + numberOfEncodedChars -= 4; + indexOfFirstEncodedChar += 4; + + } + + if (numberOfEncodedChars == 2) + { + unsigned char c1; + unsigned char c2; + (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2); + decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4); + } + else if (numberOfEncodedChars == 3) + { + unsigned char c1; + unsigned char c2; + unsigned char c3; + (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2); + (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3); + decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4); + decodedIndex++; + decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2); + } +} + +BUFFER_HANDLE Base64_Decoder(const char* source) +{ + BUFFER_HANDLE result; + /*Codes_SRS_BASE64_06_008: [If source is NULL then Base64_Decode shall return NULL.]*/ + if (source == NULL) + { + LogError("invalid parameter const char* source=%p", source); + result = NULL; + } + else + { + if ((strlen(source) % 4) != 0) + { + /*Codes_SRS_BASE64_06_011: [If the source string has an invalid length for a base 64 encoded string then Base64_Decode shall return NULL.]*/ + LogError("Invalid length Base64 string!"); + result = NULL; + } + else + { + if ((result = BUFFER_new()) == NULL) + { + /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/ + LogError("Could not create a buffer to decoding."); + } + else + { + size_t sizeOfOutputBuffer = Base64decode_len(source); + /*Codes_SRS_BASE64_06_009: [If the string pointed to by source is zero length then the handle returned shall refer to a zero length buffer.]*/ + if (sizeOfOutputBuffer > 0) + { + if (BUFFER_pre_build(result, sizeOfOutputBuffer) != 0) + { + /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/ + LogError("Could not prebuild a buffer for base 64 decoding."); + BUFFER_delete(result); + result = NULL; + } + else + { + Base64decode(BUFFER_u_char(result), source); + } + } + } + } + } + return result; +} + + +static STRING_HANDLE Base64_Encode_Internal(const unsigned char* source, size_t size) +{ + STRING_HANDLE result; + size_t neededSize = 0; + char* encoded; + size_t currentPosition = 0; + neededSize += (size == 0) ? (0) : ((((size - 1) / 3) + 1) * 4); + neededSize += 1; /*+1 because \0 at the end of the string*/ + /*Codes_SRS_BASE64_06_006: [If when allocating memory to produce the encoding a failure occurs then Base64_Encoder shall return NULL.]*/ + encoded = (char*)malloc(neededSize); + if (encoded == NULL) + { + result = NULL; + LogError("Base64_Encoder:: Allocation failed."); + } + else + { + /*b0 b1(+1) b2(+2) + 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + |----c1---| |----c2---| |----c3---| |----c4---| + */ + + size_t destinationPosition = 0; + while (size - currentPosition >= 3) + { + char c1 = base64char(source[currentPosition] >> 2); + char c2 = base64char( + ((source[currentPosition] & 3) << 4) | + (source[currentPosition + 1] >> 4) + ); + char c3 = base64char( + ((source[currentPosition + 1] & 0x0F) << 2) | + ((source[currentPosition + 2] >> 6) & 3) + ); + char c4 = base64char( + source[currentPosition + 2] & 0x3F + ); + currentPosition += 3; + encoded[destinationPosition++] = c1; + encoded[destinationPosition++] = c2; + encoded[destinationPosition++] = c3; + encoded[destinationPosition++] = c4; + + } + if (size - currentPosition == 2) + { + char c1 = base64char(source[currentPosition] >> 2); + char c2 = base64char( + ((source[currentPosition] & 0x03) << 4) | + (source[currentPosition + 1] >> 4) + ); + char c3 = base64b16(source[currentPosition + 1] & 0x0F); + encoded[destinationPosition++] = c1; + encoded[destinationPosition++] = c2; + encoded[destinationPosition++] = c3; + encoded[destinationPosition++] = '='; + } + else if (size - currentPosition == 1) + { + char c1 = base64char(source[currentPosition] >> 2); + char c2 = base64b8(source[currentPosition] & 0x03); + encoded[destinationPosition++] = c1; + encoded[destinationPosition++] = c2; + encoded[destinationPosition++] = '='; + encoded[destinationPosition++] = '='; + } + + /*null terminating the string*/ + encoded[destinationPosition] = '\0'; + /*Codes_SRS_BASE64_06_007: [Otherwise Base64_Encoder shall return a pointer to STRING, that string contains the base 64 encoding of input.]*/ + result = STRING_new_with_memory(encoded); + if (result == NULL) + { + free(encoded); + LogError("Base64_Encoder:: Allocation failed for return value."); + } + } + return result; +} + +STRING_HANDLE Base64_Encode_Bytes(const unsigned char* source, size_t size) +{ + STRING_HANDLE result; + /*Codes_SRS_BASE64_02_001: [If source is NULL then Base64_Encode_Bytes shall return NULL.] */ + if (source == NULL) + { + result = NULL; + } + /*Codes_SRS_BASE64_02_002: [If source is not NULL and size is zero, then Base64_Encode_Bytes shall produce an empty STRING_HANDLE.] */ + else if (size == 0) + { + result = STRING_new(); /*empty string*/ + } + else + { + result = Base64_Encode_Internal(source, size); + } + return result; +} + +STRING_HANDLE Base64_Encoder(BUFFER_HANDLE input) +{ + STRING_HANDLE result; + /*the following will happen*/ + /*1. the "data" of the binary shall be "eaten" 3 characters at a time and produce 4 base64 encoded characters for as long as there are more than 3 characters still to process*/ + /*2. the remaining characters (1 or 2) shall be encoded.*/ + /*there's a level of assumption that 'a' corresponds to 0b000000 and that '_' corresponds to 0b111111*/ + /*the encoding will use the optional [=] or [==] at the end of the encoded string, so that other less standard aware libraries can do their work*/ + /*these are the bits of the 3 normal bytes to be encoded*/ + + /*Codes_SRS_BASE64_06_001: [If input is NULL then Base64_Encoder shall return NULL.]*/ + if (input == NULL) + { + result = NULL; + LogError("Base64_Encoder:: NULL input"); + } + else + { + size_t inputSize; + const unsigned char* inputBinary; + if ((BUFFER_content(input, &inputBinary) != 0) || + (BUFFER_size(input, &inputSize) != 0)) + { + result = NULL; + LogError("Base64_Encoder:: BUFFER_routines failure."); + } + else + { + result = Base64_Encode_Internal(inputBinary, inputSize); + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/buffer.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,634 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <stdbool.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +typedef struct BUFFER_TAG +{ + unsigned char* buffer; + size_t size; +} BUFFER; + +/* Codes_SRS_BUFFER_07_001: [BUFFER_new shall allocate a BUFFER_HANDLE that will contain a NULL unsigned char*.] */ +BUFFER_HANDLE BUFFER_new(void) +{ + BUFFER* temp = (BUFFER*)malloc(sizeof(BUFFER)); + /* Codes_SRS_BUFFER_07_002: [BUFFER_new shall return NULL on any error that occurs.] */ + if (temp != NULL) + { + temp->buffer = NULL; + temp->size = 0; + } + return (BUFFER_HANDLE)temp; +} + +static int BUFFER_safemalloc(BUFFER* handleptr, size_t size) +{ + int result; + size_t sizetomalloc = size; + if (size == 0) + { + sizetomalloc = 1; + } + handleptr->buffer = (unsigned char*)malloc(sizetomalloc); + if (handleptr->buffer == NULL) + { + /*Codes_SRS_BUFFER_02_003: [If allocating memory fails, then BUFFER_create shall return NULL.]*/ + LogError("Failure allocating data"); + result = __FAILURE__; + } + else + { + // we still consider the real buffer size is 0 + handleptr->size = size; + result = 0; + } + return result; +} + +BUFFER_HANDLE BUFFER_create(const unsigned char* source, size_t size) +{ + BUFFER* result; + /*Codes_SRS_BUFFER_02_001: [If source is NULL then BUFFER_create shall return NULL.]*/ + if (source == NULL) + { + LogError("invalid parameter source: %p", source); + result = NULL; + } + else + { + /*Codes_SRS_BUFFER_02_002: [Otherwise, BUFFER_create shall allocate memory to hold size bytes and shall copy from source size bytes into the newly allocated memory.] */ + result = (BUFFER*)malloc(sizeof(BUFFER)); + if (result == NULL) + { + /*Codes_SRS_BUFFER_02_003: [If allocating memory fails, then BUFFER_create shall return NULL.] */ + /*fallthrough*/ + LogError("Failure allocating BUFFER structure"); + } + else + { + /* Codes_SRS_BUFFER_02_005: [If size parameter is 0 then 1 byte of memory shall be allocated yet size of the buffer shall be set to 0.]*/ + if (BUFFER_safemalloc(result, size) != 0) + { + LogError("unable to BUFFER_safemalloc "); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_BUFFER_02_004: [Otherwise, BUFFER_create shall return a non-NULL handle.] */ + (void)memcpy(result->buffer, source, size); + } + } + } + return (BUFFER_HANDLE)result; +} + +/* Codes_SRS_BUFFER_07_003: [BUFFER_delete shall delete the data associated with the BUFFER_HANDLE along with the Buffer.] */ +void BUFFER_delete(BUFFER_HANDLE handle) +{ + /* Codes_SRS_BUFFER_07_004: [BUFFER_delete shall not delete any BUFFER_HANDLE that is NULL.] */ + if (handle != NULL) + { + BUFFER* b = (BUFFER*)handle; + if (b->buffer != NULL) + { + /* Codes_SRS_BUFFER_07_003: [BUFFER_delete shall delete the data associated with the BUFFER_HANDLE along with the Buffer.] */ + free(b->buffer); + } + free(b); + } +} + +/*return 0 if the buffer was copied*/ +/*else return different than zero*/ +/* Codes_SRS_BUFFER_07_008: [BUFFER_build allocates size_t bytes, copies the unsigned char* into the buffer and returns zero on success.] */ +int BUFFER_build(BUFFER_HANDLE handle, const unsigned char* source, size_t size) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_009: [BUFFER_build shall return nonzero if handle is NULL ] */ + result = __FAILURE__; + } + /* Codes_SRS_BUFFER_01_002: [The size argument can be zero, in which case the underlying buffer held by the buffer instance shall be freed.] */ + else if (size == 0) + { + /* Codes_SRS_BUFFER_01_003: [If size is zero, source can be NULL.] */ + BUFFER* b = (BUFFER*)handle; + free(b->buffer); + b->buffer = NULL; + b->size = 0; + + result = 0; + } + else + { + if (source == NULL) + { + /* Codes_SRS_BUFFER_01_001: [If size is positive and source is NULL, BUFFER_build shall return nonzero] */ + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + /* Codes_SRS_BUFFER_07_011: [BUFFER_build shall overwrite previous contents if the buffer has been previously allocated.] */ + unsigned char* newBuffer = (unsigned char*)realloc(b->buffer, size); + if (newBuffer == NULL) + { + /* Codes_SRS_BUFFER_07_010: [BUFFER_build shall return nonzero if any error is encountered.] */ + LogError("Failure reallocating buffer"); + result = __FAILURE__; + } + else + { + b->buffer = newBuffer; + b->size = size; + /* Codes_SRS_BUFFER_01_002: [The size argument can be zero, in which case nothing shall be copied from source.] */ + (void)memcpy(b->buffer, source, size); + + result = 0; + } + } + } + + return result; +} + +int BUFFER_append_build(BUFFER_HANDLE handle, const unsigned char* source, size_t size) +{ + int result; + if (handle == NULL || source == NULL || size == 0) + { + /* Codes_SRS_BUFFER_07_029: [ BUFFER_append_build shall return nonzero if handle or source are NULL or if size is 0. ] */ + LogError("BUFFER_append_build failed invalid parameter handle: %p, source: %p, size: %uz", handle, source, size); + result = __FAILURE__; + } + else + { + if (handle->buffer == NULL) + { + /* Codes_SRS_BUFFER_07_030: [ if handle->buffer is NULL BUFFER_append_build shall allocate the a buffer of size bytes... ] */ + if (BUFFER_safemalloc(handle, size) != 0) + { + /* Codes_SRS_BUFFER_07_035: [ If any error is encountered BUFFER_append_build shall return a non-null value. ] */ + LogError("Failure with BUFFER_safemalloc"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_BUFFER_07_031: [ ... and copy the contents of source to handle->buffer. ] */ + (void)memcpy(handle->buffer, source, size); + /* Codes_SRS_BUFFER_07_034: [ On success BUFFER_append_build shall return 0 ] */ + result = 0; + } + } + else + { + /* Codes_SRS_BUFFER_07_032: [ if handle->buffer is not NULL BUFFER_append_build shall realloc the buffer to be the handle->size + size ] */ + unsigned char* temp = (unsigned char*)realloc(handle->buffer, handle->size + size); + if (temp == NULL) + { + /* Codes_SRS_BUFFER_07_035: [ If any error is encountered BUFFER_append_build shall return a non-null value. ] */ + LogError("Failure reallocating temporary buffer"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_BUFFER_07_033: [ ... and copy the contents of source to the end of the buffer. ] */ + handle->buffer = temp; + // Append the BUFFER + (void)memcpy(&handle->buffer[handle->size], source, size); + handle->size += size; + /* Codes_SRS_BUFFER_07_034: [ On success BUFFER_append_build shall return 0 ] */ + result = 0; + } + } + } + return result; +} + +/*return 0 if the buffer was pre-build(that is, had its space allocated)*/ +/*else return different than zero*/ +/* Codes_SRS_BUFFER_07_005: [BUFFER_pre_build allocates size_t bytes of BUFFER_HANDLE and returns zero on success.] */ +int BUFFER_pre_build(BUFFER_HANDLE handle, size_t size) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_006: [If handle is NULL or size is 0 then BUFFER_pre_build shall return a nonzero value.] */ + result = __FAILURE__; + } + else if (size == 0) + { + /* Codes_SRS_BUFFER_07_006: [If handle is NULL or size is 0 then BUFFER_pre_build shall return a nonzero value.] */ + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + if (b->buffer != NULL) + { + /* Codes_SRS_BUFFER_07_007: [BUFFER_pre_build shall return nonzero if the buffer has been previously allocated and is not NULL.] */ + LogError("Failure buffer data is NULL"); + result = __FAILURE__; + } + else + { + if ((b->buffer = (unsigned char*)malloc(size)) == NULL) + { + /* Codes_SRS_BUFFER_07_013: [BUFFER_pre_build shall return nonzero if any error is encountered.] */ + LogError("Failure allocating buffer"); + result = __FAILURE__; + } + else + { + b->size = size; + result = 0; + } + } + } + return result; +} + +/* Codes_SRS_BUFFER_07_019: [BUFFER_content shall return the data contained within the BUFFER_HANDLE.] */ +int BUFFER_content(BUFFER_HANDLE handle, const unsigned char** content) +{ + int result; + if ((handle == NULL) || (content == NULL)) + { + /* Codes_SRS_BUFFER_07_020: [If the handle and/or content*is NULL BUFFER_content shall return nonzero.] */ + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + *content = b->buffer; + result = 0; + } + return result; +} + +/*return 0 if everything went ok and whatever was built in the buffer was unbuilt*/ +/* Codes_SRS_BUFFER_07_012: [BUFFER_unbuild shall clear the underlying unsigned char* data associated with the BUFFER_HANDLE this will return zero on success.] */ +extern int BUFFER_unbuild(BUFFER_HANDLE handle) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_014: [BUFFER_unbuild shall return a nonzero value if BUFFER_HANDLE is NULL.] */ + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + if (b->buffer != NULL) + { + LogError("Failure buffer data is NULL"); + free(b->buffer); + b->buffer = NULL; + b->size = 0; + result = 0; + } + else + { + /* Codes_SRS_BUFFER_07_015: [BUFFER_unbuild shall return a nonzero value if the unsigned char* referenced by BUFFER_HANDLE is NULL.] */ + result = __FAILURE__; + } + } + return result; +} + +/* Codes_SRS_BUFFER_07_016: [BUFFER_enlarge shall increase the size of the unsigned char* referenced by BUFFER_HANDLE.] */ +int BUFFER_enlarge(BUFFER_HANDLE handle, size_t enlargeSize) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_017: [BUFFER_enlarge shall return a nonzero result if any parameters are NULL or zero.] */ + LogError("Failure: handle is invalid."); + result = __FAILURE__; + } + else if (enlargeSize == 0) + { + /* Codes_SRS_BUFFER_07_017: [BUFFER_enlarge shall return a nonzero result if any parameters are NULL or zero.] */ + LogError("Failure: enlargeSize size is 0."); + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + unsigned char* temp = (unsigned char*)realloc(b->buffer, b->size + enlargeSize); + if (temp == NULL) + { + /* Codes_SRS_BUFFER_07_018: [BUFFER_enlarge shall return a nonzero result if any error is encountered.] */ + LogError("Failure: allocating temp buffer."); + result = __FAILURE__; + } + else + { + b->buffer = temp; + b->size += enlargeSize; + result = 0; + } + } + return result; +} + +int BUFFER_shrink(BUFFER_HANDLE handle, size_t decreaseSize, bool fromEnd) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_036: [ if handle is NULL, BUFFER_shrink shall return a non-null value ]*/ + LogError("Failure: handle is invalid."); + result = __FAILURE__; + } + else if (decreaseSize == 0) + { + /* Codes_SRS_BUFFER_07_037: [ If decreaseSize is equal zero, BUFFER_shrink shall return a non-null value ] */ + LogError("Failure: decrease size is 0."); + result = __FAILURE__; + } + else if (decreaseSize > handle->size) + { + /* Codes_SRS_BUFFER_07_038: [ If decreaseSize is less than the size of the buffer, BUFFER_shrink shall return a non-null value ] */ + LogError("Failure: decrease size is less than buffer size."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_BUFFER_07_039: [ BUFFER_shrink shall allocate a temporary buffer of existing buffer size minus decreaseSize. ] */ + size_t alloc_size = handle->size - decreaseSize; + if (alloc_size == 0) + { + /* Codes_SRS_BUFFER_07_043: [ If the decreaseSize is equal the buffer size , BUFFER_shrink shall deallocate the buffer and set the size to zero. ] */ + free(handle->buffer); + handle->buffer = NULL; + handle->size = 0; + result = 0; + } + else + { + unsigned char* tmp = malloc(alloc_size); + if (tmp == NULL) + { + /* Codes_SRS_BUFFER_07_042: [ If a failure is encountered, BUFFER_shrink shall return a non-null value ] */ + LogError("Failure: allocating temp buffer."); + result = __FAILURE__; + } + else + { + if (fromEnd) + { + /* Codes_SRS_BUFFER_07_040: [ if the fromEnd variable is true, BUFFER_shrink shall remove the end of the buffer of size decreaseSize. ] */ + memcpy(tmp, handle->buffer, alloc_size); + free(handle->buffer); + handle->buffer = tmp; + handle->size = alloc_size; + result = 0; + } + else + { + /* Codes_SRS_BUFFER_07_041: [ if the fromEnd variable is false, BUFFER_shrink shall remove the beginning of the buffer of size decreaseSize. ] */ + memcpy(tmp, handle->buffer + decreaseSize, alloc_size); + free(handle->buffer); + handle->buffer = tmp; + handle->size = alloc_size; + result = 0; + } + } + } + } + return result; +} + +/* Codes_SRS_BUFFER_07_021: [BUFFER_size shall place the size of the associated buffer in the size variable and return zero on success.] */ +int BUFFER_size(BUFFER_HANDLE handle, size_t* size) +{ + int result; + if ((handle == NULL) || (size == NULL)) + { + /* Codes_SRS_BUFFER_07_022: [BUFFER_size shall return a nonzero value for any error that is encountered.] */ + result = __FAILURE__; + } + else + { + BUFFER* b = (BUFFER*)handle; + *size = b->size; + result = 0; + } + return result; +} + +/* Codes_SRS_BUFFER_07_024: [BUFFER_append concatenates b2 onto b1 without modifying b2 and shall return zero on success.] */ +int BUFFER_append(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2) +{ + int result; + if ( (handle1 == NULL) || (handle2 == NULL) || (handle1 == handle2) ) + { + /* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __FAILURE__; + } + else + { + BUFFER* b1 = (BUFFER*)handle1; + BUFFER* b2 = (BUFFER*)handle2; + if (b1->buffer == NULL) + { + /* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __FAILURE__; + } + else if (b2->buffer == NULL) + { + /* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __FAILURE__; + } + else + { + if (b2->size ==0) + { + // b2->size = 0, whatever b1->size is, do nothing + result = 0; + } + else + { + // b2->size != 0, whatever b1->size is + unsigned char* temp = (unsigned char*)realloc(b1->buffer, b1->size + b2->size); + if (temp == NULL) + { + /* Codes_SRS_BUFFER_07_023: [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + LogError("Failure: allocating temp buffer."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_BUFFER_07_024: [BUFFER_append concatenates b2 onto b1 without modifying b2 and shall return zero on success.]*/ + b1->buffer = temp; + // Append the BUFFER + (void)memcpy(&b1->buffer[b1->size], b2->buffer, b2->size); + b1->size += b2->size; + result = 0; + } + } + } + } + return result; +} + +int BUFFER_prepend(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2) +{ + int result; + if ((handle1 == NULL) || (handle2 == NULL) || (handle1 == handle2)) + { + /* Codes_SRS_BUFFER_01_005: [ BUFFER_prepend shall return a non-zero upon value any error that is encountered. ]*/ + result = __FAILURE__; + } + else + { + BUFFER* b1 = (BUFFER*)handle1; + BUFFER* b2 = (BUFFER*)handle2; + if (b1->buffer == NULL) + { + /* Codes_SRS_BUFFER_01_005: [ BUFFER_prepend shall return a non-zero upon value any error that is encountered. ]*/ + result = __FAILURE__; + } + else if (b2->buffer == NULL) + { + /* Codes_SRS_BUFFER_01_005: [ BUFFER_prepend shall return a non-zero upon value any error that is encountered. ]*/ + result = __FAILURE__; + } + else + { + //put b2 ahead of b1: [b2][b1], return b1 + if (b2->size ==0) + { + // do nothing + result = 0; + } + else + { + // b2->size != 0 + unsigned char* temp = (unsigned char*)malloc(b1->size + b2->size); + if (temp == NULL) + { + /* Codes_SRS_BUFFER_01_005: [ BUFFER_prepend shall return a non-zero upon value any error that is encountered. ]*/ + LogError("Failure: allocating temp buffer."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_BUFFER_01_004: [ BUFFER_prepend concatenates handle1 onto handle2 without modifying handle1 and shall return zero on success. ]*/ + // Append the BUFFER + (void)memcpy(temp, b2->buffer, b2->size); + // start from b1->size to append b1 + (void)memcpy(&temp[b2->size], b1->buffer, b1->size); + free(b1->buffer); + b1->buffer = temp; + b1->size += b2->size; + result = 0; + } + } + } + } + return result; +} + +int BUFFER_fill(BUFFER_HANDLE handle, unsigned char fill_char) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_002: [ If handle is NULL BUFFER_fill shall return a non-zero value. ] */ + LogError("Invalid parameter specified, handle == NULL."); + result = __FAILURE__; + } + else + { + size_t index; + /* Codes_SRS_BUFFER_07_001: [ BUFFER_fill shall fill the supplied BUFFER_HANDLE with the supplied fill character. ] */ + BUFFER* buffer_data = (BUFFER*)handle; + for (index = 0; index < buffer_data->size; index++) + { + buffer_data->buffer[index] = fill_char; + } + result = 0; + } + return result; +} + + +/* Codes_SRS_BUFFER_07_025: [BUFFER_u_char shall return a pointer to the underlying unsigned char*.] */ +unsigned char* BUFFER_u_char(BUFFER_HANDLE handle) +{ + BUFFER* handleData = (BUFFER*)handle; + unsigned char* result; + if (handle == NULL || handleData->size == 0) + { + /* Codes_SRS_BUFFER_07_026: [BUFFER_u_char shall return NULL for any error that is encountered.] */ + /* Codes_SRS_BUFFER_07_029: [BUFFER_u_char shall return NULL if underlying buffer size is zero.] */ + result = NULL; + } + else + { + result = handleData->buffer; + } + return result; +} + +/* Codes_SRS_BUFFER_07_027: [BUFFER_length shall return the size of the underlying buffer.] */ +size_t BUFFER_length(BUFFER_HANDLE handle) +{ + size_t result; + if (handle == NULL) + { + /* Codes_SRS_BUFFER_07_028: [BUFFER_length shall return zero for any error that is encountered.] */ + result = 0; + } + else + { + BUFFER* b = (BUFFER*)handle; + result = b->size; + } + return result; +} + +BUFFER_HANDLE BUFFER_clone(BUFFER_HANDLE handle) +{ + BUFFER_HANDLE result; + if (handle == NULL) + { + result = NULL; + } + else + { + BUFFER* suppliedBuff = (BUFFER*)handle; + BUFFER* b = (BUFFER*)malloc(sizeof(BUFFER)); + if (b != NULL) + { + if (BUFFER_safemalloc(b, suppliedBuff->size) != 0) + { + LogError("Failure: allocating temp buffer."); + result = NULL; + } + else + { + (void)memcpy(b->buffer, suppliedBuff->buffer, suppliedBuff->size); + b->size = suppliedBuff->size; + result = (BUFFER_HANDLE)b; + } + } + else + { + result = NULL; + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/connection_string_parser.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/connection_string_parser.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + + +MAP_HANDLE connectionstringparser_parse_from_char(const char* connection_string) +{ + MAP_HANDLE result; + STRING_HANDLE connString = NULL; + + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_020: [connectionstringparser_parse_from_char shall create a STRING_HANDLE from the connection_string passed in as argument and parse it using the connectionstringparser_parse.]*/ + if ((connString = STRING_construct(connection_string)) == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_021: [If connectionstringparser_parse_from_char get error creating a STRING_HANDLE, it shall return NULL.]*/ + LogError("Error constructing connection String"); + result = NULL; + } + else + { + result = connectionstringparser_parse(connString); + STRING_delete(connString); + } + + return result; +} + +/* Codes_SRS_CONNECTIONSTRINGPARSER_01_001: [connectionstringparser_parse shall parse all key value pairs from the connection_string passed in as argument and return a new map that holds the key/value pairs.] */ +MAP_HANDLE connectionstringparser_parse(STRING_HANDLE connection_string) +{ + MAP_HANDLE result; + + if (connection_string == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_002: [If connection_string is NULL then connectionstringparser_parse shall fail and return NULL.] */ + result = NULL; + LogError("NULL connection string passed to tokenizer."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_003: [connectionstringparser_parse shall create a STRING tokenizer to be used for parsing the connection string, by calling STRING_TOKENIZER_create.] */ + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_004: [connectionstringparser_parse shall start scanning at the beginning of the connection string.] */ + STRING_TOKENIZER_HANDLE tokenizer = STRING_TOKENIZER_create(connection_string); + if (tokenizer == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_015: [If STRING_TOKENIZER_create fails, connectionstringparser_parse shall fail and return NULL.] */ + result = NULL; + LogError("Error creating STRING tokenizer."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_016: [2 STRINGs shall be allocated in order to hold the to be parsed key and value tokens.] */ + STRING_HANDLE token_key_string = STRING_new(); + if (token_key_string == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_017: [If allocating the STRINGs fails connectionstringparser_parse shall fail and return NULL.] */ + result = NULL; + LogError("Error creating key token STRING."); + } + else + { + STRING_HANDLE token_value_string = STRING_new(); + if (token_value_string == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_017: [If allocating the STRINGs fails connectionstringparser_parse shall fail and return NULL.] */ + result = NULL; + LogError("Error creating value token STRING."); + } + else + { + result = Map_Create(NULL); + if (result == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_018: [If creating the result map fails, then connectionstringparser_parse shall return NULL.] */ + LogError("Error creating Map."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_005: [The following actions shall be repeated until parsing is complete:] */ + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_006: [connectionstringparser_parse shall find a token (the key of the key/value pair) delimited by the `=` character, by calling STRING_TOKENIZER_get_next_token.] */ + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_007: [If STRING_TOKENIZER_get_next_token fails, parsing shall be considered complete.] */ + while (STRING_TOKENIZER_get_next_token(tokenizer, token_key_string, "=") == 0) + { + bool is_error = false; + + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_008: [connectionstringparser_parse shall find a token (the value of the key/value pair) delimited by the `;` character, by calling STRING_TOKENIZER_get_next_token.] */ + if (STRING_TOKENIZER_get_next_token(tokenizer, token_value_string, ";") != 0) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_009: [If STRING_TOKENIZER_get_next_token fails, connectionstringparser_parse shall fail and return NULL (freeing the allocated result map).] */ + is_error = true; + LogError("Error reading value token from the connection string."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_011: [The C strings for the key and value shall be extracted from the previously parsed STRINGs by using STRING_c_str.] */ + const char* token = STRING_c_str(token_key_string); + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_013: [If STRING_c_str fails then connectionstringparser_parse shall fail and return NULL (freeing the allocated result map).] */ + if ((token == NULL) || + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_019: [If the key length is zero then connectionstringparser_parse shall fail and return NULL (freeing the allocated result map).] */ + (strlen(token) == 0)) + { + is_error = true; + LogError("The key token is NULL or empty."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_011: [The C strings for the key and value shall be extracted from the previously parsed STRINGs by using STRING_c_str.] */ + const char* value = STRING_c_str(token_value_string); + if (value == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_013: [If STRING_c_str fails then connectionstringparser_parse shall fail and return NULL (freeing the allocated result map).] */ + is_error = true; + LogError("Could not get C string for value token."); + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_010: [The key and value shall be added to the result map by using Map_Add.] */ + if (Map_Add(result, token, value) != 0) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_012: [If Map_Add fails connectionstringparser_parse shall fail and return NULL (freeing the allocated result map).] */ + is_error = true; + LogError("Could not add the key/value pair to the result map."); + } + } + } + } + + if (is_error) + { + LogError("Error parsing connection string."); + Map_Destroy(result); + result = NULL; + break; + } + } + } + + STRING_delete(token_value_string); + } + + STRING_delete(token_key_string); + } + + /* Codes_SRS_CONNECTIONSTRINGPARSER_01_014: [After the parsing is complete the previously allocated STRINGs and STRING tokenizer shall be freed by calling STRING_TOKENIZER_destroy.] */ + STRING_TOKENIZER_destroy(tokenizer); + } + } + + return result; +} + +/* Codes_SRS_CONNECTIONSTRINGPARSER_21_022: [connectionstringparser_splitHostName_from_char shall split the provided hostName in name and suffix.]*/ +int connectionstringparser_splitHostName_from_char(const char* hostName, STRING_HANDLE nameString, STRING_HANDLE suffixString) +{ + int result; + const char* runHostName = hostName; + + if ((hostName == NULL) || ((*hostName) == '\0') || ((*hostName) == '.') || (nameString == NULL) || (suffixString == NULL)) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_026: [If the hostName is NULL, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_027: [If the hostName is an empty string, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_028: [If the nameString is NULL, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_029: [If the suffixString is NULL, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_030: [If the hostName is not a valid host name, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + result = __FAILURE__; + } + else + { + while ((*runHostName) != '\0') + { + if ((*runHostName) == '.') + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_023: [connectionstringparser_splitHostName_from_char shall copy all characters, from the beginning of the hostName to the first `.` to the nameString.]*/ + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_024: [connectionstringparser_splitHostName_from_char shall copy all characters, from the first `.` to the end of the hostName, to the suffixString.]*/ + runHostName++; + break; + } + runHostName++; + } + + if ((*runHostName) == '\0') + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_030: [If the hostName is not a valid host name, connectionstringparser_splitHostName_from_char shall return __FAILURE__.]*/ + result = __FAILURE__; + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_023: [connectionstringparser_splitHostName_from_char shall copy all characters, from the beginning of the hostName to the first `.` to the nameString.]*/ + if (STRING_copy_n(nameString, hostName, runHostName - hostName - 1) != 0) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_031: [If connectionstringparser_splitHostName_from_char get error copying the name to the nameString, it shall return __FAILURE__.]*/ + result = __FAILURE__; + } + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_024: [connectionstringparser_splitHostName_from_char shall copy all characters, from the first `.` to the end of the hostName, to the suffixString.]*/ + else if (STRING_copy(suffixString, runHostName) != 0) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_032: [If connectionstringparser_splitHostName_from_char get error copying the suffix to the suffixString, it shall return __FAILURE__.]*/ + result = __FAILURE__; + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_025: [If connectionstringparser_splitHostName_from_char get success splitting the hostName, it shall return 0.]*/ + result = 0; + } + } + } + + return result; +} + + +int connectionstringparser_splitHostName(STRING_HANDLE hostNameString, STRING_HANDLE nameString, STRING_HANDLE suffixString) +{ + int result; + + if (hostNameString == NULL) + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_034: [If the hostNameString is NULL, connectionstringparser_splitHostName shall return __FAILURE__.]*/ + result = __FAILURE__; + } + else + { + /* Codes_SRS_CONNECTIONSTRINGPARSER_21_033: [connectionstringparser_splitHostName shall convert the hostNameString to a connection_string passed in as argument, and call connectionstringparser_splitHostName_from_char.]*/ + result = connectionstringparser_splitHostName_from_char(STRING_c_str(hostNameString), nameString, suffixString); + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/consolelogger.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdarg.h> +#include <stdio.h> +#include <time.h> +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/consolelogger.h" + +#if (defined(_MSC_VER)) && (!(defined WINCE)) +#include "windows.h" + +/*returns a string as if printed by vprintf*/ +static char* vprintf_alloc(const char* format, va_list va) +{ + char* result; + int neededSize = vsnprintf(NULL, 0, format, va); + if (neededSize < 0) + { + result = NULL; + } + else + { + result = (char*)malloc(neededSize + 1); + if (result == NULL) + { + /*return as is*/ + } + else + { + if (vsnprintf(result, neededSize + 1, format, va) != neededSize) + { + free(result); + result = NULL; + } + } + } + return result; +} + +/*returns a string as if printed by printf*/ +static char* printf_alloc(const char* format, ...) +{ + char* result; + va_list va; + va_start(va, format); + result = vprintf_alloc(format, va); + va_end(va); + return result; +} + +/*returns NULL if it fails*/ +static char* lastErrorToString(DWORD lastError) +{ + char* result; + if (lastError == 0) + { + result = printf_alloc(""); /*no error should appear*/ + if (result == NULL) + { + (void)printf("failure in printf_alloc"); + } + else + { + /*return as is*/ + } + } + else + { + char temp[MESSAGE_BUFFER_SIZE]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), temp, MESSAGE_BUFFER_SIZE, NULL) == 0) + { + result = printf_alloc("GetLastError()=0X%x", lastError); + if (result == NULL) + { + (void)printf("failure in printf_alloc\n"); + /*return as is*/ + } + else + { + /*return as is*/ + } + } + else + { + /*eliminate the \r or \n from the string*/ + /*one replace of each is enough*/ + char* whereAreThey; + if ((whereAreThey = strchr(temp, '\r')) != NULL) + { + *whereAreThey = '\0'; + } + if ((whereAreThey = strchr(temp, '\n')) != NULL) + { + *whereAreThey = '\0'; + } + + result = printf_alloc("GetLastError()==0X%x (%s)", lastError, temp); + + if (result == NULL) + { + (void)printf("failure in printf_alloc\n"); + /*return as is*/ + } + else + { + /*return as is*/ + } + } + } + return result; +} +/*this function will use 1x printf (in the happy case) .*/ +/*more than 1x printf / function call can mean intermingled LogErrors in a multithreaded env*/ +/*the function will also attempt to produce some human readable strings for GetLastError*/ +void consolelogger_log_with_GetLastError(const char* file, const char* func, int line, const char* format, ...) +{ + DWORD lastError; + char* lastErrorAsString; + int lastErrorAsString_should_be_freed; + time_t t; + int systemMessage_should_be_freed; + char* systemMessage; + int userMessage_should_be_freed; + char* userMessage; + + va_list args; + va_start(args, format); + + /*this is what this case will do: + 1. snip the last error + 2. create a string with what that last error means + 3. printf the system message (__FILE__, __LINE__ etc) + the last error + whatever the user wanted + */ + /*1. snip the last error*/ + lastError = GetLastError(); + + /*2. create a string with what that last error means*/ + lastErrorAsString = lastErrorToString(lastError); + if (lastErrorAsString == NULL) + { + (void)printf("failure in lastErrorToString"); + lastErrorAsString = ""; + lastErrorAsString_should_be_freed = 0; + } + else + { + lastErrorAsString_should_be_freed = 1; + } + + t = time(NULL); + systemMessage = printf_alloc("Error: Time:%.24s File:%s Func:%s Line:%d %s", ctime(&t), file, func, line, lastErrorAsString); + + if (systemMessage == NULL) + { + systemMessage = ""; + (void)printf("Error: [FAILED] Time:%.24s File : %s Func : %s Line : %d %s", ctime(&t), file, func, line, lastErrorAsString); + systemMessage_should_be_freed = 0; + } + else + { + systemMessage_should_be_freed = 1; + } + + userMessage = vprintf_alloc(format, args); + if (userMessage == NULL) + { + (void)printf("[FAILED] "); + (void)vprintf(format, args); + (void)printf("\n"); + userMessage_should_be_freed = 0; + } + else + { + /*3. printf the system message(__FILE__, __LINE__ etc) + the last error + whatever the user wanted*/ + (void)printf("%s %s\n", systemMessage, userMessage); + userMessage_should_be_freed = 1; + } + + if (userMessage_should_be_freed == 1) + { + free(userMessage); + } + + if (systemMessage_should_be_freed == 1) + { + free(systemMessage); + } + + if (lastErrorAsString_should_be_freed == 1) + { + free(lastErrorAsString); + } + va_end(args); +} +#endif + +#if defined(__GNUC__) +__attribute__ ((format (printf, 6, 7))) +#endif +void consolelogger_log(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...) +{ + time_t t; + va_list args; + va_start(args, format); + + t = time(NULL); + + switch (log_category) + { + case AZ_LOG_INFO: + (void)printf("Info: "); + break; + case AZ_LOG_ERROR: + (void)printf("Error: Time:%.24s File:%s Func:%s Line:%d ", ctime(&t), file, func, line); + break; + default: + break; + } + + (void)vprintf(format, args); + va_end(args); + + (void)log_category; + if (options & LOG_LINE) + { + (void)printf("\r\n"); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/constbuffer.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// PUT NO INCLUDES BEFORE HERE +// +#include <stdlib.h> +#include <stddef.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/refcount.h" + +typedef struct CONSTBUFFER_HANDLE_DATA_TAG +{ + CONSTBUFFER alias; +}CONSTBUFFER_HANDLE_DATA; + +DEFINE_REFCOUNT_TYPE(CONSTBUFFER_HANDLE_DATA); + +static CONSTBUFFER_HANDLE CONSTBUFFER_Create_Internal(const unsigned char* source, size_t size) +{ + CONSTBUFFER_HANDLE_DATA* result; + /*Codes_SRS_CONSTBUFFER_02_005: [The non-NULL handle returned by CONSTBUFFER_Create shall have its ref count set to "1".]*/ + /*Codes_SRS_CONSTBUFFER_02_010: [The non-NULL handle returned by CONSTBUFFER_CreateFromBuffer shall have its ref count set to "1".]*/ + result = REFCOUNT_TYPE_CREATE(CONSTBUFFER_HANDLE_DATA); + if (result == NULL) + { + /*Codes_SRS_CONSTBUFFER_02_003: [If creating the copy fails then CONSTBUFFER_Create shall return NULL.]*/ + /*Codes_SRS_CONSTBUFFER_02_008: [If copying the content fails, then CONSTBUFFER_CreateFromBuffer shall fail and return NULL.] */ + LogError("unable to malloc"); + /*return as is*/ + } + else + { + /*Codes_SRS_CONSTBUFFER_02_002: [Otherwise, CONSTBUFFER_Create shall create a copy of the memory area pointed to by source having size bytes.]*/ + result->alias.size = size; + if (size == 0) + { + result->alias.buffer = NULL; + } + else + { + unsigned char* temp = (unsigned char*)malloc(size); + if (temp == NULL) + { + /*Codes_SRS_CONSTBUFFER_02_003: [If creating the copy fails then CONSTBUFFER_Create shall return NULL.]*/ + /*Codes_SRS_CONSTBUFFER_02_008: [If copying the content fails, then CONSTBUFFER_CreateFromBuffer shall fail and return NULL.] */ + LogError("unable to malloc"); + free(result); + result = NULL; + } + else + { + + /*Codes_SRS_CONSTBUFFER_02_004: [Otherwise CONSTBUFFER_Create shall return a non-NULL handle.]*/ + /*Codes_SRS_CONSTBUFFER_02_007: [Otherwise, CONSTBUFFER_CreateFromBuffer shall copy the content of buffer.]*/ + /*Codes_SRS_CONSTBUFFER_02_009: [Otherwise, CONSTBUFFER_CreateFromBuffer shall return a non-NULL handle.]*/ + (void)memcpy(temp, source, size); + result->alias.buffer = temp; + } + } + } + return (CONSTBUFFER_HANDLE)result; +} + +CONSTBUFFER_HANDLE CONSTBUFFER_Create(const unsigned char* source, size_t size) +{ + CONSTBUFFER_HANDLE_DATA* result; + /*Codes_SRS_CONSTBUFFER_02_001: [If source is NULL and size is different than 0 then CONSTBUFFER_Create shall fail and return NULL.]*/ + if ( + (source == NULL) && + (size != 0) + ) + { + LogError("invalid arguments passes to CONSTBUFFER_Create"); + result = NULL; + } + else + { + result = (CONSTBUFFER_HANDLE_DATA*)CONSTBUFFER_Create_Internal(source, size); + } + return (CONSTBUFFER_HANDLE)result; +} + +/*this creates a new constbuffer from an existing BUFFER_HANDLE*/ +CONSTBUFFER_HANDLE CONSTBUFFER_CreateFromBuffer(BUFFER_HANDLE buffer) +{ + CONSTBUFFER_HANDLE_DATA* result; + /*Codes_SRS_CONSTBUFFER_02_006: [If buffer is NULL then CONSTBUFFER_CreateFromBuffer shall fail and return NULL.]*/ + if (buffer == NULL) + { + LogError("invalid arg passed to CONSTBUFFER_CreateFromBuffer"); + result = NULL; + } + else + { + size_t length = BUFFER_length(buffer); + unsigned char* rawBuffer = BUFFER_u_char(buffer); + result = (CONSTBUFFER_HANDLE_DATA*)CONSTBUFFER_Create_Internal(rawBuffer, length); + } + return (CONSTBUFFER_HANDLE)result; +} + +CONSTBUFFER_HANDLE CONSTBUFFER_Clone(CONSTBUFFER_HANDLE constbufferHandle) +{ + if (constbufferHandle == NULL) + { + /*Codes_SRS_CONSTBUFFER_02_013: [If constbufferHandle is NULL then CONSTBUFFER_Clone shall fail and return NULL.]*/ + LogError("invalid arg"); + } + else + { + /*Codes_SRS_CONSTBUFFER_02_014: [Otherwise, CONSTBUFFER_Clone shall increment the reference count and return constbufferHandle.]*/ + INC_REF(CONSTBUFFER_HANDLE_DATA, constbufferHandle); + } + return constbufferHandle; +} + +const CONSTBUFFER* CONSTBUFFER_GetContent(CONSTBUFFER_HANDLE constbufferHandle) +{ + const CONSTBUFFER* result; + if (constbufferHandle == NULL) + { + /*Codes_SRS_CONSTBUFFER_02_011: [If constbufferHandle is NULL then CONSTBUFFER_GetContent shall return NULL.]*/ + result = NULL; + LogError("invalid arg"); + } + else + { + /*Codes_SRS_CONSTBUFFER_02_012: [Otherwise, CONSTBUFFER_GetContent shall return a const CONSTBUFFER* that matches byte by byte the original bytes used to created the const buffer and has the same length.]*/ + result = &(((CONSTBUFFER_HANDLE_DATA*)constbufferHandle)->alias); + } + return result; +} + +void CONSTBUFFER_Destroy(CONSTBUFFER_HANDLE constbufferHandle) +{ + /*Codes_SRS_CONSTBUFFER_02_015: [If constbufferHandle is NULL then CONSTBUFFER_Destroy shall do nothing.]*/ + if (constbufferHandle != NULL) + { + /*Codes_SRS_CONSTBUFFER_02_016: [Otherwise, CONSTBUFFER_Destroy shall decrement the refcount on the constbufferHandle handle.]*/ + if (DEC_REF(CONSTBUFFER_HANDLE_DATA, constbufferHandle) == DEC_RETURN_ZERO) + { + /*Codes_SRS_CONSTBUFFER_02_017: [If the refcount reaches zero, then CONSTBUFFER_Destroy shall deallocate all resources used by the CONSTBUFFER_HANDLE.]*/ + CONSTBUFFER_HANDLE_DATA* constbufferHandleData = (CONSTBUFFER_HANDLE_DATA*)constbufferHandle; + free((void*)constbufferHandleData->alias.buffer); + free(constbufferHandleData); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/constmap.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/refcount.h" + +DEFINE_ENUM_STRINGS(CONSTMAP_RESULT, CONSTMAP_RESULT_VALUES); + +typedef struct CONSTMAP_HANDLE_DATA_TAG +{ + MAP_HANDLE map; +} CONSTMAP_HANDLE_DATA; + +DEFINE_REFCOUNT_TYPE(CONSTMAP_HANDLE_DATA); + +#define LOG_CONSTMAP_ERROR(result) LogError("result = %s", ENUM_TO_STRING(CONSTMAP_RESULT, (result))); + +CONSTMAP_HANDLE ConstMap_Create(MAP_HANDLE sourceMap) +{ + CONSTMAP_HANDLE_DATA* result = REFCOUNT_TYPE_CREATE(CONSTMAP_HANDLE_DATA); + + if (result == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_048: [ConstMap_Create shall accept any non-NULL MAP_HANDLE as input.]*/ + /*Codes_SRS_CONSTMAP_17_001: [ConstMap_Create shall create an immutable map, populated by the key, value pairs in the source map.]*/ + result->map = Map_Clone(sourceMap); + if (result->map == NULL) + { + free(result); + /*Codes_SRS_CONSTMAP_17_002: [If during creation there are any errors, then ConstMap_Create shall return NULL.]*/ + result = NULL; + LOG_CONSTMAP_ERROR(CONSTMAP_ERROR); + } + + } + /*Codes_SRS_CONSTMAP_17_003: [Otherwise, it shall return a non-NULL handle that can be used in subsequent calls.]*/ + return (CONSTMAP_HANDLE)result; +} + +void ConstMap_Destroy(CONSTMAP_HANDLE handle) +{ + /*Codes_SRS_CONSTMAP_17_005: [If parameter handle is NULL then ConstMap_Destroy shall take no action.]*/ + if (handle == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_049: [ConstMap_Destroy shall decrement the internal reference count of the immutable map.]*/ + if (DEC_REF(CONSTMAP_HANDLE_DATA, handle) == DEC_RETURN_ZERO) + { + /*Codes_SRS_CONSTMAP_17_004: [If the reference count is zero, ConstMap_Destroy shall release all resources associated with the immutable map.]*/ + Map_Destroy(((CONSTMAP_HANDLE_DATA *)handle)->map); + free(handle); + } + + } +} + +CONSTMAP_HANDLE ConstMap_Clone(CONSTMAP_HANDLE handle) +{ + /*Codes_SRS_CONSTMAP_17_038: [ConstMap_Clone returns NULL if parameter handle is NULL.] */ + if (handle == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_039: [ConstMap_Clone shall increase the internal reference count of the immutable map indicated by parameter handle]*/ + /*Codes_SRS_CONSTMAP_17_050: [ConstMap_Clone shall return the non-NULL handle. ]*/ + INC_REF(CONSTMAP_HANDLE_DATA, handle); + } + return (handle); +} + +static CONSTMAP_RESULT ConstMap_ErrorConvert(MAP_RESULT mapResult) +{ + CONSTMAP_RESULT result; + switch (mapResult) + { + case MAP_OK: + result = CONSTMAP_OK; + break; + case MAP_INVALIDARG: + result = CONSTMAP_INVALIDARG; + break; + case MAP_KEYNOTFOUND: + result = CONSTMAP_KEYNOTFOUND; + break; + default: + result = CONSTMAP_ERROR; + break; + } + return result; +} + +MAP_HANDLE ConstMap_CloneWriteable(CONSTMAP_HANDLE handle) +{ + MAP_HANDLE result = NULL; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_051: [ConstMap_CloneWriteable returns NULL if parameter handle is NULL. ]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_052: [ConstMap_CloneWriteable shall create a new, writeable map, populated by the key, value pairs in the parameter defined by handle.]*/ + /*Codes_SRS_CONSTMAP_17_053: [If during cloning, any operation fails, then ConstMap_CloneWriteableap_Clone shall return NULL.]*/ + /*Codes_SRS_CONSTMAP_17_054: [Otherwise, ConstMap_CloneWriteable shall return a non-NULL handle that can be used in subsequent calls.]*/ + result = Map_Clone(((CONSTMAP_HANDLE_DATA *)handle)->map); + } + return result; +} + +bool ConstMap_ContainsKey(CONSTMAP_HANDLE handle, const char* key ) +{ + bool keyExists = false; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_024: [If parameter handle or key are NULL then ConstMap_ContainsKey shall return false.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (key == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_025: [Otherwise if a key exists then ConstMap_ContainsKey shall return true.]*/ + MAP_RESULT mapResult = Map_ContainsKey(((CONSTMAP_HANDLE_DATA *)handle)->map, key, &keyExists); + if (mapResult != MAP_OK) + { + /*Codes_SRS_CONSTMAP_17_026: [If a key doesn't exist, then ConstMap_ContainsKey shall return false.]*/ + keyExists = false; + LOG_CONSTMAP_ERROR(ConstMap_ErrorConvert(mapResult)); + } + } + } + return keyExists; +} + +bool ConstMap_ContainsValue(CONSTMAP_HANDLE handle, const char* value) +{ + bool valueExists = false; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_027: [If parameter handle or value is NULL then ConstMap_ContainsValue shall return false.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (value == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_028: [Otherwise, if a pair has its value equal to the parameter value, the ConstMap_ContainsValue shall return true.]*/ + MAP_RESULT mapResult = Map_ContainsValue(((CONSTMAP_HANDLE_DATA *)handle)->map, value, &valueExists); + if (mapResult != MAP_OK) + { + /*Codes_SRS_CONSTMAP_17_029: [Otherwise, if such a does not exist, then ConstMap_ContainsValue shall return false.]*/ + LOG_CONSTMAP_ERROR(ConstMap_ErrorConvert(mapResult)); + } + } + } + return valueExists; +} + +const char* ConstMap_GetValue(CONSTMAP_HANDLE handle, const char* key) +{ + const char* value = NULL; + + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_040: [If parameter handle or key is NULL then ConstMap_GetValue returns NULL.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (key == NULL) + { + /*Codes_SRS_CONSTMAP_17_040: [If parameter handle or key is NULL then ConstMap_GetValue returns NULL.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_041: [If the key is not found, then ConstMap_GetValue returns NULL.]*/ + /*Codes_SRS_CONSTMAP_17_042: [Otherwise, ConstMap_GetValue returns the key's value.]*/ + value = Map_GetValueFromKey(((CONSTMAP_HANDLE_DATA *)handle)->map, key); + } + } + return value; +} + +CONSTMAP_RESULT ConstMap_GetInternals(CONSTMAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count) +{ + CONSTMAP_RESULT result; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_046: [If parameter handle, keys, values or count is NULL then ConstMap_GetInternals shall return CONSTMAP_INVALIDARG.]*/ + result = CONSTMAP_INVALIDARG; + LOG_CONSTMAP_ERROR(result); + } + else + { + /*Codes_SRS_CONSTMAP_17_043: [ConstMap_GetInternals shall produce in *keys a pointer to an array of const char* having all the keys stored so far by the map.] + *Codes_SRS_CONSTMAP_17_044: [ConstMap_GetInternals shall produce in *values a pointer to an array of const char* having all the values stored so far by the map.] + *Codes_SRS_CONSTMAP_17_045: [ ConstMap_GetInternals shall produce in *count the number of stored keys and values.] + */ + MAP_RESULT mapResult = Map_GetInternals(((CONSTMAP_HANDLE_DATA *)handle)->map, keys, values, count); + result = ConstMap_ErrorConvert(mapResult); + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/crt_abstractions.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,822 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <limits.h> +#include <float.h> +#include <math.h> +#include <errno.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +// VS 2008 does not have INFINITY and all the nice goodies... +#if defined (TIZENRT) || defined (WINCE) +#define DEFINE_INFINITY 1 +#else + +#if defined _MSC_VER +#if _MSC_VER <= 1500 +#define DEFINE_INFINITY 1 +#endif +#endif +#endif + +#if defined DEFINE_INFINITY + +#pragma warning(disable:4756 4056) // warning C4756: overflow in constant arithmetic + +// These defines are missing in math.h for WEC2013 SDK +#ifndef _HUGE_ENUF +#define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow +#endif + +#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF)) +#define HUGE_VALF ((float)INFINITY) +#define HUGE_VALL ((long double)INFINITY) +#define NAN ((float)(INFINITY * 0.0F)) +#endif + +#ifdef _MSC_VER +#else + +/*Codes_SRS_CRT_ABSTRACTIONS_99_008: [strcat_s shall append the src to dst and terminates the resulting string with a null character.]*/ +int strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + int result; + /*Codes_SRS_CRT_ABSTRACTIONS_99_004: [If dst is NULL or unterminated, the error code returned shall be EINVAL & dst shall not be modified.]*/ + if (dst == NULL) + { + result = EINVAL; + } + /*Codes_SRS_CRT_ABSTRACTIONS_99_005: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/ + else if (src == NULL) + { + dst[0] = '\0'; + result = EINVAL; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_006: [If the dstSizeInBytes is 0 or smaller than the required size for dst & src, the error code returned shall be ERANGE & dst[0] set to 0.]*/ + if (dstSizeInBytes == 0) + { + result = ERANGE; + dst[0] = '\0'; + } + else + { + size_t dstStrLen = 0; +#ifdef __STDC_LIB_EXT1__ + dstStrLen = strnlen_s(dst, dstSizeInBytes); +#else + size_t i; + for(i=0; (i < dstSizeInBytes) && (dst[i]!= '\0'); i++) + { + } + dstStrLen = i; +#endif + /*Codes_SRS_CRT_ABSTRACTIONS_99_004: [If dst is NULL or unterminated, the error code returned shall be EINVAL & dst shall not be modified.]*/ + if (dstSizeInBytes == dstStrLen) /* this means the dst string is not terminated*/ + { + result = EINVAL; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_009: [The initial character of src shall overwrite the terminating null character of dst.]*/ + (void)strncpy(&dst[dstStrLen], src, dstSizeInBytes - dstStrLen); + /*Codes_SRS_CRT_ABSTRACTIONS_99_006: [If the dstSizeInBytes is 0 or smaller than the required size for dst & src, the error code returned shall be ERANGE & dst[0] set to 0.]*/ + if (dst[dstSizeInBytes-1] != '\0') + { + dst[0] = '\0'; + result = ERANGE; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_003: [strcat_s shall return Zero upon success.]*/ + result = 0; + } + } + } + } + + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_99_025: [strncpy_s shall copy the first N characters of src to dst, where N is the lesser of MaxCount and the length of src.]*/ +int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t maxCount) +{ + int result; + int truncationFlag = 0; + /*Codes_SRS_CRT_ABSTRACTIONS_99_020: [If dst is NULL, the error code returned shall be EINVAL and dst shall not be modified.]*/ + if (dst == NULL) + { + result = EINVAL; + } + /*Codes_SRS_CRT_ABSTRACTIONS_99_021: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/ + else if (src == NULL) + { + dst[0] = '\0'; + result = EINVAL; + } + /*Codes_SRS_CRT_ABSTRACTIONS_99_022: [If the dstSizeInBytes is 0, the error code returned shall be EINVAL and dst shall not be modified.]*/ + else if (dstSizeInBytes == 0) + { + result = EINVAL; + } + else + { + size_t srcLength = strlen(src); + if (maxCount != _TRUNCATE) + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_041: [If those N characters will fit within dst (whose size is given as dstSizeInBytes) and still leave room for a null terminator, then those characters shall be copied and a terminating null is appended; otherwise, strDest[0] is set to the null character and ERANGE error code returned.]*/ + if (srcLength > maxCount) + { + srcLength = maxCount; + } + + /*Codes_SRS_CRT_ABSTRACTIONS_99_023: [If dst is not NULL & dstSizeInBytes is smaller than the required size for the src string, the error code returned shall be ERANGE and dst[0] shall be set to 0.]*/ + if (srcLength + 1 > dstSizeInBytes) + { + dst[0] = '\0'; + result = ERANGE; + } + else + { + (void)strncpy(dst, src, srcLength); + dst[srcLength] = '\0'; + /*Codes_SRS_CRT_ABSTRACTIONS_99_018: [strncpy_s shall return Zero upon success]*/ + result = 0; + } + } + /*Codes_SRS_CRT_ABSTRACTIONS_99_026: [If MaxCount is _TRUNCATE (defined as -1), then as much of src as will fit into dst shall be copied while still leaving room for the terminating null to be appended.]*/ + else + { + if (srcLength + 1 > dstSizeInBytes ) + { + srcLength = dstSizeInBytes - 1; + truncationFlag = 1; + } + (void)strncpy(dst, src, srcLength); + dst[srcLength] = '\0'; + result = 0; + } + } + + /*Codes_SRS_CRT_ABSTRACTIONS_99_019: [If truncation occurred as a result of the copy, the error code returned shall be STRUNCATE.]*/ + if (truncationFlag == 1) + { + result = STRUNCATE; + } + + return result; +} + +/* Codes_SRS_CRT_ABSTRACTIONS_99_016: [strcpy_s shall copy the contents in the address of src, including the terminating null character, to the location that's specified by dst.]*/ +int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + int result; + + /* Codes_SRS_CRT_ABSTRACTIONS_99_012: [If dst is NULL, the error code returned shall be EINVAL & dst shall not be modified.]*/ + if (dst == NULL) + { + result = EINVAL; + } + /* Codes_SRS_CRT_ABSTRACTIONS_99_013: [If src is NULL, the error code returned shall be EINVAL and dst[0] shall be set to 0.]*/ + else if (src == NULL) + { + dst[0] = '\0'; + result = EINVAL; + } + /* Codes_SRS_CRT_ABSTRACTIONS_99_014: [If the dstSizeInBytes is 0 or smaller than the required size for the src string, the error code returned shall be ERANGE & dst[0] set to 0.]*/ + else if (dstSizeInBytes == 0) + { + dst[0] = '\0'; + result = ERANGE; + } + else + { + size_t neededBuffer = strlen(src); + /* Codes_SRS_CRT_ABSTRACTIONS_99_014: [If the dstSizeInBytes is 0 or smaller than the required size for the src string, the error code returned shall be ERANGE & dst[0] set to 0.]*/ + if (neededBuffer + 1 > dstSizeInBytes) + { + dst[0] = '\0'; + result = ERANGE; + } + else + { + (void)memcpy(dst, src, neededBuffer + 1); + /*Codes_SRS_CRT_ABSTRACTIONS_99_011: [strcpy_s shall return Zero upon success]*/ + result = 0; + } + } + + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_99_029: [The sprintf_s function shall format and store series of characters and values in dst. Each argument (if any) is converted and output according to the corresponding Format Specification in the format variable.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_99_031: [A null character is appended after the last character written.]*/ +int sprintf_s(char* dst, size_t dstSizeInBytes, const char* format, ...) +{ + int result; + /*Codes_SRS_CRT_ABSTRACTIONS_99_028: [If dst or format is a null pointer, sprintf_s shall return -1 and set errno to EINVAL]*/ + if ((dst == NULL) || + (format == NULL)) + { + errno = EINVAL; + result = -1; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_033: [sprintf_s shall check the format string for valid formatting characters. If the check fails, the function returns -1.]*/ + +#if defined _MSC_VER +#error crt_abstractions is not provided for Microsoft Compilers +#else + /*not Microsoft compiler... */ +#if defined (__STDC_VERSION__) || (__cplusplus) +#if ( \ + ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) || \ + (defined __cplusplus) \ + ) + /*C99 compiler*/ + va_list args; + va_start(args, format); + /*Codes_SRS_CRT_ABSTRACTIONS_99_027: [sprintf_s shall return the number of characters stored in dst upon success. This number shall not include the terminating null character.]*/ + result = vsnprintf(dst, dstSizeInBytes, format, args); + va_end(args); + + /*C99: Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n*/ + if (result < 0) + { + result = -1; + } + else if ((size_t)result >= dstSizeInBytes) + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_034: [If the dst buffer is too small for the text being printed, then dst is set to an empty string and the function shall return -1.]*/ + dst[0] = '\0'; + result = -1; + } + else + { + /*do nothing, all is fine*/ + } +#else +#error STDC_VERSION defined, but of unknown value; unable to sprinf_s, or provide own implementation +#endif +#else +#error for STDC_VERSION undefined (assumed C89), provide own implementation of sprintf_s +#endif +#endif + } + return result; +} +#endif /* _MSC_VER */ + +/*Codes_SRS_CRT_ABSTRACTIONS_21_006: [The strtoull_s must use the letters from a(or A) through z(or Z) to represent the numbers between 10 to 35.]*/ +/* returns the integer value that correspond to the character 'c'. If the character is invalid, it returns -1. */ +#define DIGIT_VAL(c) (((c>='0') && (c<='9')) ? (c-'0') : ((c>='a') && (c<='z')) ? (c-'a'+10) : ((c>='A') && (c<='Z')) ? (c-'A'+10) : -1) +#define IN_BASE_RANGE(d, b) ((d >= 0) && (d < b)) + +/*Codes_SRS_CRT_ABSTRACTIONS_21_010: [The white-space must be one of the characters ' ', '\f', '\n', '\r', '\t', '\v'.]*/ +#define IS_SPACE(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') + +/*Codes_SRS_CRT_ABSTRACTIONS_21_001: [The strtoull_s must convert the initial portion of the string pointed to by nptr to unsigned long long int representation.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_002: [The strtoull_s must resembling an integer represented in some radix determined by the value of base.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_003: [The strtoull_s must return the integer that represents the value in the initial part of the string. If any.]*/ +unsigned long long strtoull_s(const char* nptr, char** endptr, int base) +{ + unsigned long long result = 0ULL; + bool validStr = true; + char* runner = (char*)nptr; + bool isNegative = false; + int digitVal; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_005: [The strtoull_s must convert number using base 2 to 36.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_012: [If the subject sequence is empty or does not have the expected form, the strtoull_s must not perform any conversion; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a NULL pointer.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_013: [If no conversion could be performed, the strtoull_s returns the value 0L.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_035: [If the nptr is NULL, the strtoull_s must **not** perform any conversion and must returns 0L; endptr must receive NULL, provided that endptr is not a NULL pointer.]*/ + if (((base >= 2) || (base == 0)) && (base <= 36) && (runner != NULL)) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_011: [The valid sequence starts after the first non-white-space character, followed by an optional positive or negative sign, a number or a letter(depending of the base).]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_010: [The white-space must be one of the characters ' ', '\f', '\n', '\r', '\t', '\v'.]*/ + while (IS_SPACE(*runner)) + { + runner++; + } + if ((*runner) == '+') + { + runner++; + } + else if ((*runner) == '-') + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_038: [If the subject sequence starts with a negative sign, the strtoull_s will convert it to the posive representation of the negative value.]*/ + isNegative = true; + runner++; + } + + if ((*runner) == '0') + { + if ((*(runner+1) == 'x') || (*(runner+1) == 'X')) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_008: [If the base is 0 and '0x' or '0X' precedes the number, strtoull_s must convert to a hexadecimal (base 16).]*/ + /* hexadecimal... */ + if ((base == 0) || (base == 16)) + { + base = 16; + runner += 2; + } + } + else if((base == 0) || (base == 8)) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_009: [If the base is 0 and '0' precedes the number, strtoull_s must convert to an octal (base 8).]*/ + /* octal... */ + base = 8; + runner++; + } + } + + if(base == 0) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_007: [If the base is 0 and no special chars precedes the number, strtoull_s must convert to a decimal (base 10).]*/ + /* decimal... */ + base = 10; + } + + digitVal = DIGIT_VAL(*runner); + if (validStr && IN_BASE_RANGE(digitVal, base)) + { + errno = 0; + do + { + if (((ULLONG_MAX - digitVal) / base) < result) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_014: [If the correct value is outside the range, the strtoull_s returns the value ULLONG_MAX, and errno will receive the value ERANGE.]*/ + /* overflow... */ + result = ULLONG_MAX; + errno = ERANGE; + } + else + { + result = result * base + digitVal; + } + runner++; + digitVal = DIGIT_VAL(*runner); + } while (IN_BASE_RANGE(digitVal, base)); + } + else + { + runner = (char*)nptr; + } + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_004: [The strtoull_s must return in endptr a final string of one or more unrecognized characters, including the terminating null character of the input string.]*/ + if (endptr != NULL) + { + (*endptr) = (char*)runner; + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_038: [If the subject sequence starts with a negative sign, the strtoull_s will convert it to the posive representation of the negative value.]*/ + if (isNegative) + { + result = ULLONG_MAX - result + 1; + } + + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_21_023: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtof_s must return the INFINITY value for float.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_024: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtof_s must return 0.0f and points endptr to the first character after the 'NAN' sequence.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_033: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtold_s must return the INFINITY value for long double.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_034: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtold_s must return 0.0 and points endptr to the first character after the 'NAN' sequence.]*/ +static int substricmp(const char* nptr, const char* subsrt) +{ + int result = 0; + while (((*subsrt) != '\0') && (result == 0)) + { + result = TOUPPER(*nptr) - TOUPPER(*subsrt); + nptr++; + subsrt++; + } + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_21_023: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtof_s must return the INFINITY value for float.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_033: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtold_s must return the INFINITY value for long double.]*/ +static bool isInfinity(const char** endptr) +{ + bool result = false; + if (substricmp((*endptr), "INF") == 0) + { + (*endptr) += 3; + result = true; + if (substricmp((*endptr), "INITY") == 0) + { + (*endptr) += 5; + } + } + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_21_024: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtof_s must return 0.0f and points endptr to the first character after the 'NAN' sequence.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_034: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtold_s must return 0.0 and points endptr to the first character after the 'NAN' sequence.]*/ +static bool isNaN(const char** endptr) +{ + const char* runner = (*endptr); + bool result = false; + if (substricmp(runner, "NAN") == 0) + { + runner += 3; + result = true; + if ((*runner) == '(') + { + do + { + runner++; + } while (((*runner) != '\0') && ((*runner) != ')')); + if ((*runner) == ')') + runner++; + else + result = false; + } + } + if (result) + (*endptr) = runner; + return result; +} + +#define FLOAT_STRING_TYPE_VALUES \ + FST_INFINITY, \ + FST_NAN, \ + FST_NUMBER, \ + FST_OVERFLOW, \ + FST_ERROR + +DEFINE_ENUM(FLOAT_STRING_TYPE, FLOAT_STRING_TYPE_VALUES); + +static FLOAT_STRING_TYPE splitFloatString(const char* nptr, char** endptr, int *signal, double *fraction, int *exponential) +{ + FLOAT_STRING_TYPE result = FST_ERROR; + + unsigned long long ullInteger = 0; + unsigned long long ullFraction = 0; + int integerSize = 0; + int fractionSize = 0; + char* startptr; + + (*endptr) = (char*)nptr; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_018: [The white-space for strtof_s must be one of the characters ' ', '\f', '\n', '\r', '\t', '\v'.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_028: [The white-space for strtold_s must be one of the characters ' ', '\f', '\n', '\r', '\t', '\v'.]*/ + while (IS_SPACE(**endptr)) + { + (*endptr)++; + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_019: [The valid sequence for strtof_s starts after the first non-white - space character, followed by an optional positive or negative sign, a number, 'INF', or 'NAN' (ignoring case).]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_029: [The valid sequence for strtold_s starts after the first non-white - space character, followed by an optional positive or negative sign, a number, 'INF', or 'NAN' (ignoring case).]*/ + (*signal) = +1; + if ((**endptr) == '+') + { + (*endptr)++; + } + else if ((**endptr) == '-') + { + (*signal) = -1; + (*endptr)++; + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_023: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtof_s must return the INFINITY value for float.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_033: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtold_s must return the INFINITY value for long double.]*/ + if (isInfinity((const char**)endptr)) + { + result = FST_INFINITY; + } + /*Codes_SRS_CRT_ABSTRACTIONS_21_034: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtold_s must return 0.0 and points endptr to the first character after the 'NAN' sequence.]*/ + /*Codes_SRS_CRT_ABSTRACTIONS_21_024: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtof_s must return 0.0f and points endptr to the first character after the 'NAN' sequence.]*/ + else if (isNaN((const char**)endptr)) + { + result = FST_NAN; + } + else if (IN_BASE_RANGE(DIGIT_VAL(**endptr), 10)) + { + result = FST_NUMBER; + startptr = *endptr; + /* integers will go to the fraction and exponential. */ + ullInteger = strtoull_s(startptr, endptr, 10); + integerSize = (int)((*endptr) - startptr); + if ((ullInteger == ULLONG_MAX) && (errno != 0)) + { + result = FST_OVERFLOW; + } + + /* get the real fraction part, if exist. */ + if ((**endptr) == '.') + { + startptr = (*endptr) + 1; + ullFraction = strtoull_s(startptr, endptr, 10); + fractionSize = (int)((*endptr) - startptr); + if ((ullFraction == ULLONG_MAX) && (errno != 0)) + { + result = FST_OVERFLOW; + } + } + + if (((**endptr) == 'e') || ((**endptr) == 'E')) + { + startptr = (*endptr) + 1; + (*exponential) = (int)strtol(startptr, endptr, 10); + if (((*exponential) < (DBL_MAX_10_EXP * (-1))) || ((*exponential) > DBL_MAX_10_EXP)) + { + result = FST_OVERFLOW; + } + } + else + { + (*exponential) = 0; + } + + if (result == FST_NUMBER) + { + /* Add ullInteger to ullFraction. */ + ullFraction += (ullInteger * (unsigned long long)(pow(10, (double)fractionSize))); + (*fraction) = ((double)ullFraction / (pow(10.0f, (double)(fractionSize + integerSize - 1)))); + + /* Unify rest of integerSize and fractionSize in the exponential. */ + (*exponential) += integerSize - 1; + } + } + + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_21_015: [The strtof_s must convert the initial portion of the string pointed to by nptr to float representation.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_016: [The strtof_s must return the float that represents the value in the initial part of the string. If any.]*/ +float strtof_s(const char* nptr, char** endptr) +{ + int signal = 1; + double fraction; + int exponential; + char* runner = (char*)nptr; + double val; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_021: [If no conversion could be performed, the strtof_s returns the value 0.0.]*/ + float result = 0.0; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_036: [**If the nptr is NULL, the strtof_s must not perform any conversion and must returns 0.0f; endptr must receive NULL, provided that endptr is not a NULL pointer.]*/ + if (nptr != NULL) + { + switch (splitFloatString(nptr, &runner, &signal, &fraction, &exponential)) + { + case FST_INFINITY: + /*Codes_SRS_CRT_ABSTRACTIONS_21_023: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtof_s must return the INFINITY value for float.]*/ + result = INFINITY * (signal); + errno = 0; + break; + case FST_NAN: + /*Codes_SRS_CRT_ABSTRACTIONS_21_024: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtof_s must return 0.0f and points endptr to the first character after the 'NAN' sequence.]*/ + result = NAN; + break; + case FST_NUMBER: + val = fraction * pow(10.0, (double)exponential) * (double)signal; + if ((val >= (FLT_MAX * (-1))) && (val <= FLT_MAX)) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_016: [The strtof_s must return the float that represents the value in the initial part of the string. If any.]*/ + result = (float)val; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_022: [If the correct value is outside the range, the strtof_s returns the value plus or minus HUGE_VALF, and errno will receive the value ERANGE.]*/ + result = HUGE_VALF * (signal); + errno = ERANGE; + } + break; + case FST_OVERFLOW: + /*Codes_SRS_CRT_ABSTRACTIONS_21_022: [If the correct value is outside the range, the strtof_s returns the value plus or minus HUGE_VALF, and errno will receive the value ERANGE.]*/ + result = HUGE_VALF * (signal); + errno = ERANGE; + break; + default: + /*Codes_SRS_CRT_ABSTRACTIONS_21_020: [If the subject sequence is empty or does not have the expected form, the strtof_s must not perform any conversion and must returns 0.0f; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a NULL pointer.]*/ + runner = (char*)nptr; + break; + } + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_017: [The strtof_s must return in endptr a final string of one or more unrecognized characters, including the terminating null character of the input string.]*/ + if (endptr != NULL) + { + (*endptr) = runner; + } + + return result; +} + +/*Codes_SRS_CRT_ABSTRACTIONS_21_025: [The strtold_s must convert the initial portion of the string pointed to by nptr to long double representation.]*/ +/*Codes_SRS_CRT_ABSTRACTIONS_21_026: [The strtold_s must return the long double that represents the value in the initial part of the string. If any.]*/ +long double strtold_s(const char* nptr, char** endptr) +{ + int signal = 1; + double fraction; + int exponential; + char* runner = (char*)nptr; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_031: [If no conversion could be performed, the strtold_s returns the value 0.0.]*/ + long double result = 0.0; + + /*Codes_SRS_CRT_ABSTRACTIONS_21_037: [If the nptr is NULL, the strtold_s must not perform any conversion and must returns 0.0; endptr must receive NULL, provided that endptr is not a NULL pointer.]*/ + if (nptr != NULL) + { + switch (splitFloatString(nptr, &runner, &signal, &fraction, &exponential)) + { + case FST_INFINITY: + /*Codes_SRS_CRT_ABSTRACTIONS_21_033: [If the string is 'INF' of 'INFINITY' (ignoring case), the strtold_s must return the INFINITY value for long double.]*/ + result = INFINITY * (signal); + errno = 0; + break; + case FST_NAN: + /*Codes_SRS_CRT_ABSTRACTIONS_21_034: [If the string is 'NAN' or 'NAN(...)' (ignoring case), the strtold_s must return 0.0 and points endptr to the first character after the 'NAN' sequence.]*/ + result = NAN; + break; + case FST_NUMBER: + if ((exponential != DBL_MAX_10_EXP || (fraction <= 1.7976931348623158)) && + (exponential != (DBL_MAX_10_EXP * (-1)) || (fraction <= 2.2250738585072014))) + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_026: [The strtold_s must return the long double that represents the value in the initial part of the string. If any.]*/ + result = fraction * pow(10.0, (double)exponential) * (double)signal; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_21_032: [If the correct value is outside the range, the strtold_s returns the value plus or minus HUGE_VALL, and errno will receive the value ERANGE.]*/ + result = HUGE_VALF * (signal); + errno = ERANGE; + } + break; + case FST_OVERFLOW: + /*Codes_SRS_CRT_ABSTRACTIONS_21_032: [If the correct value is outside the range, the strtold_s returns the value plus or minus HUGE_VALL, and errno will receive the value ERANGE.]*/ + result = HUGE_VALF * (signal); + errno = ERANGE; + break; + default: + /*Codes_SRS_CRT_ABSTRACTIONS_21_030: [If the subject sequence is empty or does not have the expected form, the strtold_s must not perform any conversion and must returns 0.0; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a NULL pointer.]*/ + runner = (char*)nptr; + break; + } + } + + /*Codes_SRS_CRT_ABSTRACTIONS_21_027: [The strtold_s must return in endptr a final string of one or more unrecognized characters, including the terminating null character of the input string.]*/ + if (endptr != NULL) + { + (*endptr) = runner; + } + + return result; +} + + +/*Codes_SRS_CRT_ABSTRACTIONS_99_038: [mallocAndstrcpy_s shall allocate memory for destination buffer to fit the string in the source parameter.]*/ +int mallocAndStrcpy_s(char** destination, const char* source) +{ + int result; + int copied_result; + /*Codes_SRS_CRT_ABSTRACTIONS_99_036: [destination parameter or source parameter is NULL, the error code returned shall be EINVAL and destination shall not be modified.]*/ + if ((destination == NULL) || (source == NULL)) + { + /*If strDestination or strSource is a NULL pointer[...]these functions return EINVAL */ + result = EINVAL; + } + else + { + size_t l = strlen(source); + char* temp = (char*)malloc(l + 1); + + /*Codes_SRS_CRT_ABSTRACTIONS_99_037: [Upon failure to allocate memory for the destination, the function will return ENOMEM.]*/ + if (temp == NULL) + { + result = ENOMEM; + } + else + { + *destination = temp; + /*Codes_SRS_CRT_ABSTRACTIONS_99_039: [mallocAndstrcpy_s shall copy the contents in the address source, including the terminating null character into location specified by the destination pointer after the memory allocation.]*/ + copied_result = strcpy_s(*destination, l + 1, source); + if (copied_result < 0) /*strcpy_s error*/ + { + free(*destination); + *destination = NULL; + result = copied_result; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_99_035: [mallocAndstrcpy_s shall return Zero upon success]*/ + result = 0; + } + } + } + return result; +} + +/*takes "value" and transforms it into a decimal string*/ +/*10 => "10"*/ +/*return 0 when everything went ok*/ +/*Codes_SRS_CRT_ABSTRACTIONS_02_001: [unsignedIntToString shall convert the parameter value to its decimal representation as a string in the buffer indicated by parameter destination having the size indicated by parameter destinationSize.] */ +int unsignedIntToString(char* destination, size_t destinationSize, unsigned int value) +{ + int result; + size_t pos; + /*the below loop gets the number in reverse order*/ + /*Codes_SRS_CRT_ABSTRACTIONS_02_003: [If destination is NULL then unsignedIntToString shall fail.] */ + /*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */ + if ( + (destination == NULL) || + (destinationSize < 2) /*because the smallest number is '0\0' which requires 2 characters*/ + ) + { + result = __FAILURE__; + } + else + { + pos = 0; + do + { + destination[pos++] = '0' + (value % 10); + value /= 10; + } while ((value > 0) && (pos < (destinationSize-1))); + + if (value == 0) + { + size_t w; + destination[pos] = '\0'; + /*all converted and they fit*/ + for (w = 0; w <= (pos-1) >> 1; w++) + { + char temp; + temp = destination[w]; + destination[w] = destination[pos - 1 - w]; + destination[pos -1 - w] = temp; + } + /*Codes_SRS_CRT_ABSTRACTIONS_02_004: [If the conversion has been successfull then unsignedIntToString shall return 0.] */ + result = 0; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */ + result = __FAILURE__; + } + } + return result; +} + +/*takes "value" and transforms it into a decimal string*/ +/*10 => "10"*/ +/*return 0 when everything went ok*/ +/*Codes_SRS_CRT_ABSTRACTIONS_02_001: [unsignedIntToString shall convert the parameter value to its decimal representation as a string in the buffer indicated by parameter destination having the size indicated by parameter destinationSize.] */ +int size_tToString(char* destination, size_t destinationSize, size_t value) +{ + int result; + size_t pos; + /*the below loop gets the number in reverse order*/ + /*Codes_SRS_CRT_ABSTRACTIONS_02_003: [If destination is NULL then unsignedIntToString shall fail.] */ + /*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */ + if ( + (destination == NULL) || + (destinationSize < 2) /*because the smallest number is '0\0' which requires 2 characters*/ + ) + { + result = __FAILURE__; + } + else + { + pos = 0; + do + { + destination[pos++] = '0' + (value % 10); + value /= 10; + } while ((value > 0) && (pos < (destinationSize - 1))); + + if (value == 0) + { + size_t w; + destination[pos] = '\0'; + /*all converted and they fit*/ + for (w = 0; w <= (pos - 1) >> 1; w++) + { + char temp; + temp = destination[w]; + destination[w] = destination[pos - 1 - w]; + destination[pos - 1 - w] = temp; + } + /*Codes_SRS_CRT_ABSTRACTIONS_02_004: [If the conversion has been successfull then unsignedIntToString shall return 0.] */ + result = 0; + } + else + { + /*Codes_SRS_CRT_ABSTRACTIONS_02_002: [If the conversion fails for any reason (for example, insufficient buffer space), a non-zero return value shall be supplied and unsignedIntToString shall fail.] */ + result = __FAILURE__; + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/doublylinkedlist.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/doublylinkedlist.h" + +void +DList_InitializeListHead( + PDLIST_ENTRY ListHead +) +{ + /* Codes_SRS_DLIST_06_005: [DList_InitializeListHead will initialize the Flink & Blink to the address of the DLIST_ENTRY.] */ + ListHead->Flink = ListHead->Blink = ListHead; + return; +} + +int +DList_IsListEmpty( + const PDLIST_ENTRY ListHead +) +{ + /* Codes_SRS_DLIST_06_003: [DList_IsListEmpty shall return a non-zero value if there are no DLIST_ENTRY's on this list other than the list head.] */ + /* Codes_SRS_DLIST_06_004: [DList_IsListEmpty shall return 0 if there is one or more items in the list.] */ + return (ListHead->Flink == ListHead); +} + +int +DList_RemoveEntryList( + PDLIST_ENTRY Entry +) +{ + /* Codes_SRS_DLIST_06_008: [DList_RemoveEntryList shall remove a listEntry from whatever list it is properly part of.] */ + /* Codes_SRS_DLIST_06_009: [The remaining list is properly formed.] */ + /* Codes_SRS_DLIST_06_010: [DList_RemoveEntryList shall return non-zero if the remaining list is empty.] */ + /* Codes_SRS_DLIST_06_011: [DList_RemoveEntryList shall return zero if the remaining list is NOT empty.] */ + PDLIST_ENTRY Blink; + PDLIST_ENTRY Flink; + + Flink = Entry->Flink; + Blink = Entry->Blink; + Blink->Flink = Flink; + Flink->Blink = Blink; + return (Flink == Blink); +} + +PDLIST_ENTRY +DList_RemoveHeadList( + PDLIST_ENTRY ListHead +) +{ + /* Codes_SRS_DLIST_06_012: [DList_RemoveHeadList removes the oldest entry from the list defined by the listHead parameter and returns a pointer to that entry.] */ + /* Codes_SRS_DLIST_06_013: [DList_RemoveHeadList shall return listHead if that's the only item in the list.] */ + + PDLIST_ENTRY Flink; + PDLIST_ENTRY Entry; + + Entry = ListHead->Flink; + Flink = Entry->Flink; + ListHead->Flink = Flink; + Flink->Blink = ListHead; + return Entry; +} + + + +void +DList_InsertTailList( + PDLIST_ENTRY ListHead, + PDLIST_ENTRY Entry +) +{ + PDLIST_ENTRY Blink; + + /* Codes_SRS_DLIST_06_006: [DListInsertTailList shall place the DLIST_ENTRY at the end of the list defined by the listHead parameter.] */ + Blink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; + return; +} + + +void +DList_AppendTailList( + PDLIST_ENTRY ListHead, + PDLIST_ENTRY ListToAppend +) +{ + /* Codes_SRS_DLIST_06_007: [DList_AppendTailList shall place the list defined by listToAppend at the end of the list defined by the listHead parameter.] */ + PDLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; + return; +} + + +/*Codes_SRS_DLIST_02_002: [DList_InsertHeadList inserts a singular entry in the list having as head listHead after "head".]*/ +void DList_InsertHeadList(PDLIST_ENTRY listHead, PDLIST_ENTRY entry) +{ + entry->Blink = listHead; + entry->Flink = listHead->Flink; + listHead->Flink->Blink = entry; + listHead->Flink = entry; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/gb_rand.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef __cplusplus +#include <cstdlib> +#else +#include <stdlib.h> +#endif + +#include "azure_c_shared_utility/gb_rand.h" + +/*this is rand*/ +int gb_rand(void) +{ + return rand(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/gb_stdio.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*depending if the symbol GB_STDIO_INTERCEPT is defined, this file does the following + + a) if GB_STDIO_INTERCEPT is NOT defined, then the file shall be empty (almost:) + b) if GB_STDIO_INTERCEPT is defined, then the file shall call to the 'real' stdio.h functions from their gb_* synonyms*/ +#ifdef _MSC_VER +static const int avoid_a_warning_C4206 = 0; /* warning C4206: nonstandard extension used: translation unit is empty*/ +#endif +#ifdef GB_STDIO_INTERCEPT + +#ifdef __cplusplus +#include <cstdio> +#include <cstdarg> +#else +#include <stdio.h> +#include <stdarg.h> +#endif + +#include "azure_c_shared_utility/gb_stdio.h" + +/*this is fopen*/ +FILE *gb_fopen(const char * filename, const char * mode) +{ + return fopen(filename, mode); +} + +int gb_fclose(FILE *stream) +{ + return fclose(stream); +} + +int gb_fseek(FILE *stream, long int offset, int whence) +{ + return fseek(stream, offset, whence); +} + +long int gb_ftell(FILE *stream) +{ + return ftell(stream); +} + +int fprintf(FILE * stream, const char * format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stream, format, args); + va_end(args); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/gb_time.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*depending if the symbol GB_TIME_INTERCEPT is defined, this file does the following + + a) if GB_TIME_INTERCEPT is NOT defined, then the file shall be empty (almost:) + b) if GB_TIME_INTERCEPT is defined, then the file shall call to the 'real' time.h functions from their gb_* synonyms*/ +#ifdef _MSC_VER +static const int avoid_a_warning_C4206 = 0; /* warning C4206: nonstandard extension used: translation unit is empty*/ +#endif +#ifdef GB_TIME_INTERCEPT + +#ifdef __cplusplus +#include <ctime> +#else +#include <time.h> +#endif + +#include "azure_c_shared_utility/gb_time.h" + +/*this is time*/ +time_t gb_time(time_t *timer); +{ + return time(timer); +} + +/*this is localtime*/ +struct tm *gb_localtime(const time_t *timer) +{ + return gb_localtime(timer); +} + +size_t gb_strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr) +{ + return strftime(s, maxsize, format, timeptr); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/gballoc.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,446 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +#ifndef GB_USE_CUSTOM_HEAP + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)~(size_t)0) +#endif + +typedef struct ALLOCATION_TAG +{ + size_t size; + void* ptr; + void* next; +} ALLOCATION; + +typedef enum GBALLOC_STATE_TAG +{ + GBALLOC_STATE_INIT, + GBALLOC_STATE_NOT_INIT +} GBALLOC_STATE; + +static ALLOCATION* head = NULL; +static size_t totalSize = 0; +static size_t maxSize = 0; +static size_t g_allocations = 0; +static GBALLOC_STATE gballocState = GBALLOC_STATE_NOT_INIT; + +static LOCK_HANDLE gballocThreadSafeLock = NULL; + +int gballoc_init(void) +{ + int result; + + if (gballocState != GBALLOC_STATE_NOT_INIT) + { + /* Codes_SRS_GBALLOC_01_025: [Init after Init shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + /* Codes_SRS_GBALLOC_01_026: [gballoc_Init shall create a lock handle that will be used to make the other gballoc APIs thread-safe.] */ + else if ((gballocThreadSafeLock = Lock_Init()) == NULL) + { + /* Codes_SRS_GBALLOC_01_027: [If the Lock creation fails, gballoc_init shall return a non-zero value.]*/ + result = __FAILURE__; + } + else + { + gballocState = GBALLOC_STATE_INIT; + + /* Codes_ SRS_GBALLOC_01_002: [Upon initialization the total memory used and maximum total memory used tracked by the module shall be set to 0.] */ + totalSize = 0; + maxSize = 0; + g_allocations = 0; + + /* Codes_SRS_GBALLOC_01_024: [gballoc_init shall initialize the gballoc module and return 0 upon success.] */ + result = 0; + } + + return result; +} + +void gballoc_deinit(void) +{ + if (gballocState == GBALLOC_STATE_INIT) + { + /* Codes_SRS_GBALLOC_01_028: [gballoc_deinit shall free all resources allocated by gballoc_init.] */ + (void)Lock_Deinit(gballocThreadSafeLock); + } + + gballocState = GBALLOC_STATE_NOT_INIT; +} + +void* gballoc_malloc(size_t size) +{ + void* result; + + if (gballocState != GBALLOC_STATE_INIT) + { + /* Codes_SRS_GBALLOC_01_039: [If gballoc was not initialized gballoc_malloc shall simply call malloc without any memory tracking being performed.] */ + result = malloc(size); + } + /* Codes_SRS_GBALLOC_01_030: [gballoc_malloc shall ensure thread safety by using the lock created by gballoc_Init.] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_048: [If acquiring the lock fails, gballoc_malloc shall return NULL.] */ + LogError("Failed to get the Lock."); + result = NULL; + } + else + { + ALLOCATION* allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION)); + if (allocation == NULL) + { + result = NULL; + } + else + { + /* Codes_SRS_GBALLOC_01_003: [gb_malloc shall call the C99 malloc function and return its result.] */ + result = malloc(size); + if (result == NULL) + { + /* Codes_SRS_GBALLOC_01_012: [When the underlying malloc call fails, gballoc_malloc shall return NULL and size should not be counted towards total memory used.] */ + free(allocation); + } + else + { + /* Codes_SRS_GBALLOC_01_004: [If the underlying malloc call is successful, gb_malloc shall increment the total memory used with the amount indicated by size.] */ + allocation->ptr = result; + allocation->size = size; + allocation->next = head; + head = allocation; + + g_allocations++; + totalSize += size; + /* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */ + if (maxSize < totalSize) + { + maxSize = totalSize; + } + } + } + + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +void* gballoc_calloc(size_t nmemb, size_t size) +{ + void* result; + + if (gballocState != GBALLOC_STATE_INIT) + { + /* Codes_SRS_GBALLOC_01_040: [If gballoc was not initialized gballoc_calloc shall simply call calloc without any memory tracking being performed.] */ + result = calloc(nmemb, size); + } + /* Codes_SRS_GBALLOC_01_031: [gballoc_calloc shall ensure thread safety by using the lock created by gballoc_Init] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_046: [If acquiring the lock fails, gballoc_calloc shall return NULL.] */ + LogError("Failed to get the Lock."); + result = NULL; + } + else + { + ALLOCATION* allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION)); + if (allocation == NULL) + { + result = NULL; + } + else + { + /* Codes_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */ + result = calloc(nmemb, size); + if (result == NULL) + { + /* Codes_SRS_GBALLOC_01_022: [When the underlying calloc call fails, gballoc_calloc shall return NULL and size should not be counted towards total memory used.] */ + free(allocation); + } + else + { + /* Codes_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */ + allocation->ptr = result; + allocation->size = nmemb * size; + allocation->next = head; + head = allocation; + g_allocations++; + + totalSize += allocation->size; + /* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */ + if (maxSize < totalSize) + { + maxSize = totalSize; + } + } + } + + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +void* gballoc_realloc(void* ptr, size_t size) +{ + ALLOCATION* curr; + void* result; + ALLOCATION* allocation = NULL; + + if (gballocState != GBALLOC_STATE_INIT) + { + /* Codes_SRS_GBALLOC_01_041: [If gballoc was not initialized gballoc_realloc shall shall simply call realloc without any memory tracking being performed.] */ + result = realloc(ptr, size); + } + /* Codes_SRS_GBALLOC_01_032: [gballoc_realloc shall ensure thread safety by using the lock created by gballoc_Init.] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_047: [If acquiring the lock fails, gballoc_realloc shall return NULL.] */ + LogError("Failed to get the Lock."); + result = NULL; + } + else + { + if (ptr == NULL) + { + /* Codes_SRS_GBALLOC_01_017: [When ptr is NULL, gballoc_realloc shall call the underlying realloc with ptr being NULL and the realloc result shall be tracked by gballoc.] */ + allocation = (ALLOCATION*)malloc(sizeof(ALLOCATION)); + } + else + { + curr = head; + while (curr != NULL) + { + if (curr->ptr == ptr) + { + allocation = curr; + break; + } + else + { + curr = (ALLOCATION*)curr->next; + } + } + } + + if (allocation == NULL) + { + /* Codes_SRS_GBALLOC_01_015: [When allocating memory used for tracking by gballoc_realloc fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */ + /* Codes_SRS_GBALLOC_01_016: [When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_realloc shall return NULL and the underlying realloc shall not be called.] */ + result = NULL; + } + else + { + result = realloc(ptr, size); + if (result == NULL) + { + /* Codes_SRS_GBALLOC_01_014: [When the underlying realloc call fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */ + if (ptr == NULL) + { + free(allocation); + } + } + else + { + if (ptr != NULL) + { + /* Codes_SRS_GBALLOC_01_006: [If the underlying realloc call is successful, gballoc_realloc shall look up the size associated with the pointer ptr and decrease the total memory used with that size.] */ + allocation->ptr = result; + totalSize -= allocation->size; + allocation->size = size; + } + else + { + /* add block */ + allocation->ptr = result; + allocation->size = size; + allocation->next = head; + head = allocation; + } + + /* Codes_SRS_GBALLOC_01_007: [If realloc is successful, gballoc_realloc shall also increment the total memory used value tracked by this module.] */ + totalSize += size; + g_allocations++; + + /* Codes_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */ + if (maxSize < totalSize) + { + maxSize = totalSize; + } + } + } + + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +void gballoc_free(void* ptr) +{ + ALLOCATION* curr = head; + ALLOCATION* prev = NULL; + + if (gballocState != GBALLOC_STATE_INIT) + { + /* Codes_SRS_GBALLOC_01_042: [If gballoc was not initialized gballoc_free shall shall simply call free.] */ + free(ptr); + } + /* Codes_SRS_GBALLOC_01_033: [gballoc_free shall ensure thread safety by using the lock created by gballoc_Init.] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_049: [If acquiring the lock fails, gballoc_free shall do nothing.] */ + LogError("Failed to get the Lock."); + } + else + { + /* Codes_SRS_GBALLOC_01_009: [gballoc_free shall also look up the size associated with the ptr pointer and decrease the total memory used with the associated size amount.] */ + while (curr != NULL) + { + if (curr->ptr == ptr) + { + /* Codes_SRS_GBALLOC_01_008: [gballoc_free shall call the C99 free function.] */ + free(ptr); + totalSize -= curr->size; + if (prev != NULL) + { + prev->next = curr->next; + } + else + { + head = (ALLOCATION*)curr->next; + } + + free(curr); + break; + } + + prev = curr; + curr = (ALLOCATION*)curr->next; + } + + if ((curr == NULL) && (ptr != NULL)) + { + /* Codes_SRS_GBALLOC_01_019: [When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_free shall not free any memory.] */ + + /* could not find the allocation */ + LogError("Could not free allocation for address %p (not found)", ptr); + } + (void)Unlock(gballocThreadSafeLock); + } +} + +size_t gballoc_getMaximumMemoryUsed(void) +{ + size_t result; + + /* Codes_SRS_GBALLOC_01_038: [If gballoc was not initialized gballoc_getMaximumMemoryUsed shall return MAX_INT_SIZE.] */ + if (gballocState != GBALLOC_STATE_INIT) + { + LogError("gballoc is not initialized."); + result = SIZE_MAX; + } + /* Codes_SRS_GBALLOC_01_034: [gballoc_getMaximumMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_050: [If the lock cannot be acquired, gballoc_getMaximumMemoryUsed shall return SIZE_MAX.] */ + LogError("Failed to get the Lock."); + result = SIZE_MAX; + } + else + { + /* Codes_SRS_GBALLOC_01_010: [gballoc_getMaximumMemoryUsed shall return the maximum amount of total memory used recorded since the module initialization.] */ + result = maxSize; + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +size_t gballoc_getCurrentMemoryUsed(void) +{ + size_t result; + + /* Codes_SRS_GBALLOC_01_044: [If gballoc was not initialized gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */ + if (gballocState != GBALLOC_STATE_INIT) + { + LogError("gballoc is not initialized."); + result = SIZE_MAX; + } + /* Codes_SRS_GBALLOC_01_036: [gballoc_getCurrentMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.]*/ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_01_051: [If the lock cannot be acquired, gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */ + LogError("Failed to get the Lock."); + result = SIZE_MAX; + } + else + { + /*Codes_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */ + result = totalSize; + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +size_t gballoc_getAllocationCount(void) +{ + size_t result; + + /* Codes_SRS_GBALLOC_07_001: [ If gballoc was not initialized gballoc_getAllocationCount shall return 0. ] */ + if (gballocState != GBALLOC_STATE_INIT) + { + LogError("gballoc is not initialized."); + result = 0; + } + /* Codes_SRS_GBALLOC_07_002: [ gballoc_getAllocationCount shall ensure thread safety by using the lock created by gballoc_Init ] */ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_07_003: [ If the lock cannot be acquired, gballoc_getAllocationCount shall return 0. ] */ + LogError("Failed to get the Lock."); + result = 0; + } + else + { + /* Codes_SRS_GBALLOC_07_004: [ gballoc_getAllocationCount shall return the currently number of allocations. ] */ + result = g_allocations; + (void)Unlock(gballocThreadSafeLock); + } + + return result; +} + +void gballoc_resetMetrics() +{ + /* Codes_SRS_GBALLOC_07_005: [ If gballoc was not initialized gballoc_reset Metrics shall do nothing.] */ + if (gballocState != GBALLOC_STATE_INIT) + { + LogError("gballoc is not initialized."); + } + /* Codes_SRS_GBALLOC_07_006: [ gballoc_resetMetrics shall ensure thread safety by using the lock created by gballoc_Init ]*/ + else if (LOCK_OK != Lock(gballocThreadSafeLock)) + { + /* Codes_SRS_GBALLOC_07_007: [ If the lock cannot be acquired, gballoc_reset Metrics shall do nothing.] */ + LogError("Failed to get the Lock."); + } + else + { + /* Codes_SRS_GBALLOC_07_008: [ gballoc_resetMetrics shall reset the total allocation size, max allocation size and number of allocation to zero. ] */ + totalSize = 0; + maxSize = 0; + g_allocations = 0; + (void)Unlock(gballocThreadSafeLock); + } +} + +#endif // GB_USE_CUSTOM_HEAP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/hmac.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/**************************** hmac.c ****************************/ +/******************** See RFC 4634 for details ******************/ +/* +* Description: +* This file implements the HMAC algorithm (Keyed-Hashing for +* Message Authentication, RFC2104), expressed in terms of the +* various SHA algorithms. +*/ + +#include "azure_c_shared_utility/sha.h" + +/* +* hmac +* +* Description: +* This function will compute an HMAC message digest. +* +* Parameters: +* whichSha: [in] +* One of SHA1, SHA224, SHA256, SHA384, SHA512 +* key: [in] +* The secret shared key. +* key_len: [in] +* The length of the secret shared key. +* message_array: [in] +* An array of characters representing the message. +* length: [in] +* The length of the message in message_array +* digest: [out] +* Where the digest is returned. +* NOTE: The length of the digest is determined by +* the value of whichSha. +* +* Returns: +* sha Error Code. +* +*/ +int hmac(SHAversion whichSha, const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + uint8_t digest[USHAMaxHashSize]) +{ + HMACContext ctx; + return hmacReset(&ctx, whichSha, key, key_len) || + hmacInput(&ctx, text, text_len) || + hmacResult(&ctx, digest); +} + +/* +* hmacReset +* +* Description: +* This function will initialize the hmacContext in preparation +* for computing a new HMAC message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* whichSha: [in] +* One of SHA1, SHA224, SHA256, SHA384, SHA512 +* key: [in] +* The secret shared key. +* key_len: [in] +* The length of the secret shared key. +* +* Returns: +* sha Error Code. +* +*/ +int hmacReset(HMACContext *ctx, enum SHAversion whichSha, + const unsigned char *key, int key_len) +{ + int i, blocksize, hashsize; + + /* inner padding - key XORd with ipad */ + unsigned char k_ipad[USHA_Max_Message_Block_Size]; + + /* temporary buffer when keylen > blocksize */ + unsigned char tempkey[USHAMaxHashSize]; + + if (!ctx) return shaNull; + + blocksize = ctx->blockSize = USHABlockSize(whichSha); + hashsize = ctx->hashSize = USHAHashSize(whichSha); + + ctx->whichSha = whichSha; + + /* + * If key is longer than the hash blocksize, + * reset it to key = HASH(key). + */ + if (key_len > blocksize) { + USHAContext tctx; + int err = USHAReset(&tctx, whichSha) || + USHAInput(&tctx, key, key_len) || + USHAResult(&tctx, tempkey); + if (err != shaSuccess) return err; + + key = tempkey; + key_len = hashsize; + } + + /* + * The HMAC transform looks like: + * + * SHA(K XOR opad, SHA(K XOR ipad, text)) + * + * where K is an n byte key. + * ipad is the byte 0x36 repeated blocksize times + * opad is the byte 0x5c repeated blocksize times + * and text is the data being protected. + */ + + /* store key into the pads, XOR'd with ipad and opad values */ + for (i = 0; i < key_len; i++) { + k_ipad[i] = key[i] ^ 0x36; + ctx->k_opad[i] = key[i] ^ 0x5c; + } + /* remaining pad bytes are '\0' XOR'd with ipad and opad values */ + for (; i < blocksize; i++) { + k_ipad[i] = 0x36; + ctx->k_opad[i] = 0x5c; + } + + /* perform inner hash */ + /* init context for 1st pass */ + return USHAReset(&ctx->shaContext, whichSha) || + /* and start with inner pad */ + USHAInput(&ctx->shaContext, k_ipad, blocksize); +} + +/* +* hmacInput +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The HMAC context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int hmacInput(HMACContext *ctx, const unsigned char *text, + int text_len) +{ + if (!ctx) return shaNull; + /* then text of datagram */ + return USHAInput(&ctx->shaContext, text, text_len); +} + +/* +* HMACFinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The HMAC context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int hmacFinalBits(HMACContext *ctx, + const uint8_t bits, + unsigned int bitcount) +{ + if (!ctx) return shaNull; + /* then final bits of datagram */ + return USHAFinalBits(&ctx->shaContext, bits, bitcount); +} + +/* +* HMACResult +* +* Description: +* This function will return the N-byte message digest into the +* Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the Nth element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the HMAC hash. +* digest: [out] +* Where the digest is returned. +* NOTE 2: The length of the hash is determined by the value of +* whichSha that was passed to hmacReset(). +* +* Returns: +* sha Error Code. +* +*/ +int hmacResult(HMACContext *ctx, uint8_t *digest) +{ + if (!ctx) return shaNull; + + /* finish up 1st pass */ + /* (Use digest here as a temporary buffer.) */ + return USHAResult(&ctx->shaContext, digest) || + + /* perform outer SHA */ + /* init context for 2nd pass */ + USHAReset(&ctx->shaContext, (SHAversion)ctx->whichSha) || + + /* start with outer pad */ + USHAInput(&ctx->shaContext, ctx->k_opad, ctx->blockSize) || + + /* then results of 1st hash */ + USHAInput(&ctx->shaContext, digest, ctx->hashSize) || + + /* finish up 2nd pass */ + USHAResult(&ctx->shaContext, digest); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/hmacsha256.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include "azure_c_shared_utility/hmacsha256.h" +#include "azure_c_shared_utility/hmac.h" +#include "azure_c_shared_utility/buffer_.h" + +HMACSHA256_RESULT HMACSHA256_ComputeHash(const unsigned char* key, size_t keyLen, const unsigned char* payload, size_t payloadLen, BUFFER_HANDLE hash) +{ + HMACSHA256_RESULT result; + + if (key == NULL || + keyLen == 0 || + payload == NULL || + payloadLen == 0 || + hash == NULL) + { + result = HMACSHA256_INVALID_ARG; + } + else + { + if ((BUFFER_enlarge(hash, 32) != 0) || + (hmac(SHA256, payload, (int)payloadLen, key, (int)keyLen, BUFFER_u_char(hash) ) != 0)) + { + result = HMACSHA256_ERROR; + } + else + { + result = HMACSHA256_OK; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/http_proxy_io.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,967 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <limits.h> +#include <stddef.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/socketio.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/http_proxy_io.h" +#include "azure_c_shared_utility/base64.h" + +typedef enum HTTP_PROXY_IO_STATE_TAG +{ + HTTP_PROXY_IO_STATE_CLOSED, + HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO, + HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE, + HTTP_PROXY_IO_STATE_OPEN, + HTTP_PROXY_IO_STATE_CLOSING, + HTTP_PROXY_IO_STATE_ERROR +} HTTP_PROXY_IO_STATE; + +typedef struct HTTP_PROXY_IO_INSTANCE_TAG +{ + HTTP_PROXY_IO_STATE http_proxy_io_state; + ON_BYTES_RECEIVED on_bytes_received; + void* on_bytes_received_context; + ON_IO_ERROR on_io_error; + void* on_io_error_context; + ON_IO_OPEN_COMPLETE on_io_open_complete; + void* on_io_open_complete_context; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + void* on_io_close_complete_context; + char* hostname; + int port; + char* proxy_hostname; + int proxy_port; + char* username; + char* password; + XIO_HANDLE underlying_io; + unsigned char* receive_buffer; + size_t receive_buffer_size; +} HTTP_PROXY_IO_INSTANCE; + +static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters) +{ + HTTP_PROXY_IO_INSTANCE* result; + + if (io_create_parameters == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_002: [ If `io_create_parameters` is NULL, `http_proxy_io_create` shall fail and return NULL. ]*/ + result = NULL; + LogError("NULL io_create_parameters."); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_003: [ `io_create_parameters` shall be used as an `HTTP_PROXY_IO_CONFIG*`. ]*/ + HTTP_PROXY_IO_CONFIG* http_proxy_io_config = (HTTP_PROXY_IO_CONFIG*)io_create_parameters; + if ((http_proxy_io_config->hostname == NULL) || + (http_proxy_io_config->proxy_hostname == NULL)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_004: [ If the `hostname` or `proxy_hostname` member is NULL, then `http_proxy_io_create` shall fail and return NULL. ]*/ + result = NULL; + LogError("Bad arguments: hostname = %p, proxy_hostname = %p", + http_proxy_io_config->hostname, http_proxy_io_config->proxy_hostname); + } + /* Codes_SRS_HTTP_PROXY_IO_01_095: [ If one of the fields `username` and `password` is non-NULL, then the other has to be also non-NULL, otherwise `http_proxy_io_create` shall fail and return NULL. ]*/ + else if (((http_proxy_io_config->username == NULL) && (http_proxy_io_config->password != NULL)) || + ((http_proxy_io_config->username != NULL) && (http_proxy_io_config->password == NULL))) + { + result = NULL; + LogError("Bad arguments: username = %p, password = %p", + http_proxy_io_config->username, http_proxy_io_config->password); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_001: [ `http_proxy_io_create` shall create a new instance of the HTTP proxy IO. ]*/ + result = (HTTP_PROXY_IO_INSTANCE*)malloc(sizeof(HTTP_PROXY_IO_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_051: [ If allocating memory for the new instance fails, `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Failed allocating HTTP proxy IO instance."); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_005: [ `http_proxy_io_create` shall copy the `hostname`, `port`, `username` and `password` values for later use when the actual CONNECT is performed. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ + if (mallocAndStrcpy_s(&result->hostname, http_proxy_io_config->hostname) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Failed to copy the hostname."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result); + result = NULL; + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ + if (mallocAndStrcpy_s(&result->proxy_hostname, http_proxy_io_config->proxy_hostname) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Failed to copy the proxy_hostname."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->username = NULL; + result->password = NULL; + + /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_094: [ `username` and `password` shall be optional. ]*/ + if ((http_proxy_io_config->username != NULL) && (mallocAndStrcpy_s(&result->username, http_proxy_io_config->username) != 0)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Failed to copy the username."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result->proxy_hostname); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_094: [ `username` and `password` shall be optional. ]*/ + if ((http_proxy_io_config->password != NULL) && (mallocAndStrcpy_s(&result->password, http_proxy_io_config->password) != 0)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Failed to copy the passowrd."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result->username); + free(result->proxy_hostname); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_010: [ - `io_interface_description` shall be set to the result of `socketio_get_interface_description`. ]*/ + const IO_INTERFACE_DESCRIPTION* underlying_io_interface = socketio_get_interface_description(); + if (underlying_io_interface == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_050: [ If `socketio_get_interface_description` fails, `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Unable to get the socket IO interface description."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result->password); + free(result->username); + free(result->proxy_hostname); + free(result->hostname); + free(result); + result = NULL; + } + else + { + SOCKETIO_CONFIG socket_io_config; + + /* Codes_SRS_HTTP_PROXY_IO_01_011: [ - `xio_create_parameters` shall be set to a `SOCKETIO_CONFIG*` where `hostname` is set to the `proxy_hostname` member of `io_create_parameters` and `port` is set to the `proxy_port` member of `io_create_parameters`. ]*/ + socket_io_config.hostname = http_proxy_io_config->proxy_hostname; + socket_io_config.port = http_proxy_io_config->proxy_port; + socket_io_config.accepted_socket = NULL; + + /* Codes_SRS_HTTP_PROXY_IO_01_009: [ `http_proxy_io_create` shall create a new socket IO by calling `xio_create` with the arguments: ]*/ + result->underlying_io = xio_create(underlying_io_interface, &socket_io_config); + if (result->underlying_io == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_012: [ If `xio_create` fails, `http_proxy_io_create` shall fail and return NULL. ]*/ + LogError("Unable to create the underlying IO."); + /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ + free(result->password); + free(result->username); + free(result->proxy_hostname); + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->port = http_proxy_io_config->port; + result->proxy_port = http_proxy_io_config->proxy_port; + result->receive_buffer = NULL; + result->receive_buffer_size = 0; + result->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + } + } + } + } + } + } + } + } + } + + return result; +} + +static void http_proxy_io_destroy(CONCRETE_IO_HANDLE http_proxy_io) +{ + if (http_proxy_io == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_014: [ If `http_proxy_io` is NULL, `http_proxy_io_destroy` shall do nothing. ]*/ + LogError("NULL http_proxy_io."); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + /* Codes_SRS_HTTP_PROXY_IO_01_013: [ `http_proxy_io_destroy` shall free the HTTP proxy IO instance indicated by `http_proxy_io`. ]*/ + if (http_proxy_io_instance->receive_buffer != NULL) + { + free(http_proxy_io_instance->receive_buffer); + } + + /* Codes_SRS_HTTP_PROXY_IO_01_016: [ `http_proxy_io_destroy` shall destroy the underlying IO created in `http_proxy_io_create` by calling `xio_destroy`. ]*/ + xio_destroy(http_proxy_io_instance->underlying_io); + free(http_proxy_io_instance->hostname); + free(http_proxy_io_instance->proxy_hostname); + free(http_proxy_io_instance->username); + free(http_proxy_io_instance->password); + free(http_proxy_io_instance); + } +} + +static void indicate_open_complete_error_and_close(HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance) +{ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); + http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_ERROR); +} + +// This callback usage needs to be either verified and commented or integrated into +// the state machine. +static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + (void)context; + (void)send_result; +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + if (context == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_081: [ `on_underlying_io_open_complete` called with NULL context shall do nothing. ]*/ + LogError("NULL context in on_underlying_io_open_complete"); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; + switch (http_proxy_io_instance->http_proxy_io_state) + { + default: + LogError("on_underlying_io_open_complete called in an unexpected state."); + break; + + case HTTP_PROXY_IO_STATE_CLOSING: + case HTTP_PROXY_IO_STATE_OPEN: + /* Codes_SRS_HTTP_PROXY_IO_01_077: [ When `on_underlying_io_open_complete` is called in after OPEN has completed, the `on_io_error` callback shall be triggered passing the `on_io_error_context` argument as `context`. ]*/ + http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context); + break; + + case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: + /* Codes_SRS_HTTP_PROXY_IO_01_076: [ When `on_underlying_io_open_complete` is called while waiting for the CONNECT reply, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Open complete called again by underlying IO."); + indicate_open_complete_error_and_close(http_proxy_io_instance); + break; + + case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: + switch (open_result) + { + default: + case IO_OPEN_ERROR: + /* Codes_SRS_HTTP_PROXY_IO_01_078: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_ERROR`, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Underlying IO open failed"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + break; + + case IO_OPEN_CANCELLED: + /* Codes_SRS_HTTP_PROXY_IO_01_079: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_CANCELLED`, the `on_open_complete` callback shall be triggered with `IO_OPEN_CANCELLED`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Underlying IO open failed"); + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); + http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED); + break; + + case IO_OPEN_OK: + { + STRING_HANDLE encoded_auth_string; + + /* Codes_SRS_HTTP_PROXY_IO_01_057: [ When `on_underlying_io_open_complete` is called, the `http_proxy_io` shall send the CONNECT request constructed per RFC 2817: ]*/ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE; + + if (http_proxy_io_instance->username != NULL) + { + char* plain_auth_string_bytes; + + /* Codes_SRS_HTTP_PROXY_IO_01_060: [ - The value of `Proxy-Authorization` shall be the constructed according to RFC 2617. ]*/ + int plain_auth_string_length = (int)(strlen(http_proxy_io_instance->username)+1); + if (http_proxy_io_instance->password != NULL) + { + plain_auth_string_length += (int)strlen(http_proxy_io_instance->password); + } + + if (plain_auth_string_length < 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + encoded_auth_string = NULL; + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + plain_auth_string_bytes = (char*)malloc(plain_auth_string_length + 1); + if (plain_auth_string_bytes == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + encoded_auth_string = NULL; + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_091: [ To receive authorization, the client sends the userid and password, separated by a single colon (":") character, within a base64 [7] encoded string in the credentials. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_092: [ A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_093: [ Userids might be case sensitive. ]*/ + if (sprintf(plain_auth_string_bytes, "%s:%s", http_proxy_io_instance->username, (http_proxy_io_instance->password == NULL) ? "" : http_proxy_io_instance->password) < 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + encoded_auth_string = NULL; + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_061: [ Encoding to Base64 shall be done by calling `Base64_Encode_Bytes`. ]*/ + encoded_auth_string = Base64_Encode_Bytes((const unsigned char*)plain_auth_string_bytes, plain_auth_string_length); + if (encoded_auth_string == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot Base64 encode auth string"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + } + + free(plain_auth_string_bytes); + } + } + } + else + { + encoded_auth_string = NULL; + } + + if ((http_proxy_io_instance->username != NULL) && + (encoded_auth_string == NULL)) + { + LogError("Cannot create authorization header"); + } + else + { + int connect_request_length; + const char* auth_string_payload; + /* Codes_SRS_HTTP_PROXY_IO_01_075: [ The Request-URI portion of the Request-Line is always an 'authority' as defined by URI Generic Syntax [2], which is to say the host name and port number destination of the requested connection separated by a colon: ]*/ + const char request_format[] = "CONNECT %s:%d HTTP/1.1\r\nHost:%s:%d%s%s\r\n\r\n"; + const char proxy_basic[] = "\r\nProxy-authorization: Basic "; + if (http_proxy_io_instance->username != NULL) + { + auth_string_payload = STRING_c_str(encoded_auth_string); + } + else + { + auth_string_payload = ""; + } + + /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If `username` and `password` have been specified in the arguments passed to `http_proxy_io_create`, then the header `Proxy-Authorization` shall be added to the request. ]*/ + + connect_request_length = (int)(strlen(request_format)+(strlen(http_proxy_io_instance->hostname)*2)+strlen(auth_string_payload)+10); + if (http_proxy_io_instance->username != NULL) + { + connect_request_length += (int)strlen(proxy_basic); + } + + if (connect_request_length < 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot encode the CONNECT request"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + char* connect_request = (char*)malloc(connect_request_length + 1); + if (connect_request == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot allocate memory for CONNECT request"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If `username` and `password` have been specified in the arguments passed to `http_proxy_io_create`, then the header `Proxy-Authorization` shall be added to the request. ]*/ + connect_request_length = sprintf(connect_request, request_format, + http_proxy_io_instance->hostname, + http_proxy_io_instance->port, + http_proxy_io_instance->hostname, + http_proxy_io_instance->port, + (http_proxy_io_instance->username != NULL) ? proxy_basic : "", + auth_string_payload); + + if (connect_request_length < 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot encode the CONNECT request"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_063: [ The request shall be sent by calling `xio_send` and passing NULL as `on_send_complete` callback. ]*/ + if (xio_send(http_proxy_io_instance->underlying_io, connect_request, connect_request_length, unchecked_on_send_complete, NULL) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_064: [ If `xio_send` fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Could not send CONNECT request"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + } + + free(connect_request); + } + } + } + + if (encoded_auth_string != NULL) + { + STRING_delete(encoded_auth_string); + } + + break; + } + } + + break; + } + } +} + +static void on_underlying_io_error(void* context) +{ + if (context == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_088: [ `on_underlying_io_error` called with NULL context shall do nothing. ]*/ + LogError("NULL context in on_underlying_io_error"); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; + + switch (http_proxy_io_instance->http_proxy_io_state) + { + default: + LogError("on_underlying_io_error in invalid state"); + break; + + case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: + case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: + /* Codes_SRS_HTTP_PROXY_IO_01_087: [ If the `on_underlying_io_error` callback is called while OPENING, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + indicate_open_complete_error_and_close(http_proxy_io_instance); + break; + + case HTTP_PROXY_IO_STATE_OPEN: + /* Codes_SRS_HTTP_PROXY_IO_01_089: [ If the `on_underlying_io_error` callback is called while the IO is OPEN, the `on_io_error` callback shall be called with the `on_io_error_context` argument as `context`. ]*/ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_ERROR; + http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context); + break; + } + } +} + +static void on_underlying_io_close_complete(void* context) +{ + if (context == NULL) + { + /* Cdoes_SRS_HTTP_PROXY_IO_01_084: [ `on_underlying_io_close_complete` called with NULL context shall do nothing. ]*/ + LogError("NULL context in on_underlying_io_open_complete"); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; + + switch (http_proxy_io_instance->http_proxy_io_state) + { + default: + LogError("on_underlying_io_close_complete called in an invalid state"); + break; + + case HTTP_PROXY_IO_STATE_CLOSING: + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + + /* Codes_SRS_HTTP_PROXY_IO_01_086: [ If the `on_io_close_complete` callback passed to `http_proxy_io_close` was NULL, no callback shall be triggered. ]*/ + if (http_proxy_io_instance->on_io_close_complete != NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_083: [ `on_underlying_io_close_complete` while CLOSING shall call the `on_io_close_complete` callback, passing to it the `on_io_close_complete_context` as `context` argument. ]*/ + http_proxy_io_instance->on_io_close_complete(http_proxy_io_instance->on_io_close_complete_context); + } + + break; + } + } +} + +/*the following function does the same as sscanf(pos2, "%d", &sec)*/ +/*this function only exists because some of platforms do not have sscanf. */ +static int ParseStringToDecimal(const char *src, int* dst) +{ + int result; + char* next; + + (*dst) = (int)strtol(src, &next, 0); + if ((src == next) || ((((*dst) == INT_MAX) || ((*dst) == INT_MIN)) && (errno != 0))) + { + result = __LINE__; + } + else + { + result = 0; + } + + return result; +} + +/*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */ +/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */ +static int ParseHttpResponse(const char* src, int* dst) +{ + int result; + static const char HTTPPrefix[] = "HTTP/"; + bool fail; + const char* runPrefix; + + if ((src == NULL) || (dst == NULL)) + { + result = __LINE__; + } + else + { + fail = false; + runPrefix = HTTPPrefix; + + while ((*runPrefix) != '\0') + { + if ((*runPrefix) != (*src)) + { + fail = true; + break; + } + src++; + runPrefix++; + } + + if (!fail) + { + while ((*src) != '.') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (!fail) + { + while ((*src) != ' ') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (fail) + { + result = __LINE__; + } + else + { + if (ParseStringToDecimal(src, dst) != 0) + { + result = __LINE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + if (context == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_082: [ `on_underlying_io_bytes_received` called with NULL context shall do nothing. ]*/ + LogError("NULL context in on_underlying_io_bytes_received"); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; + + switch (http_proxy_io_instance->http_proxy_io_state) + { + default: + case HTTP_PROXY_IO_STATE_CLOSING: + LogError("Bytes received in invalid state"); + break; + + case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_HTTP_PROXY_IO_01_080: [ If `on_underlying_io_bytes_received` is called while the underlying IO is being opened, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Bytes received while opening underlying IO"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + break; + + case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: + { + /* Codes_SRS_HTTP_PROXY_IO_01_065: [ When bytes are received and the response to the CONNECT request was not yet received, the bytes shall be accumulated until a double new-line is detected. ]*/ + unsigned char* new_receive_buffer = (unsigned char*)realloc(http_proxy_io_instance->receive_buffer, http_proxy_io_instance->receive_buffer_size + size + 1); + if (new_receive_buffer == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_067: [ If allocating memory for the buffered bytes fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot allocate memory for received data"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + http_proxy_io_instance->receive_buffer = new_receive_buffer; + memcpy(http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size, buffer, size); + http_proxy_io_instance->receive_buffer_size += size; + } + + if (http_proxy_io_instance->receive_buffer_size >= 4) + { + const char* request_end_ptr; + + http_proxy_io_instance->receive_buffer[http_proxy_io_instance->receive_buffer_size] = 0; + + /* Codes_SRS_HTTP_PROXY_IO_01_066: [ When a double new-line is detected the response shall be parsed in order to extract the status code. ]*/ + if ((http_proxy_io_instance->receive_buffer_size >= 4) && + ((request_end_ptr = strstr((const char*)http_proxy_io_instance->receive_buffer, "\r\n\r\n")) != NULL)) + { + int status_code; + + /* This part should really be done with the HTTPAPI, but that has to be done as a separate step + as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */ + + if (ParseHttpResponse((const char*)http_proxy_io_instance->receive_buffer, &status_code) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_068: [ If parsing the CONNECT response fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Cannot decode HTTP response"); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + /* Codes_SRS_HTTP_PROXY_IO_01_069: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_090: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/ + else if ((status_code < 200) || (status_code > 299)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_071: [ If the status code is not successful, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ + LogError("Bad status (%d) received in CONNECT response", status_code); + indicate_open_complete_error_and_close(http_proxy_io_instance); + } + else + { + size_t length_remaining = http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size - ((const unsigned char *)request_end_ptr + 4); + + /* Codes_SRS_HTTP_PROXY_IO_01_073: [ Once a success status code was parsed, the IO shall be OPEN. ]*/ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPEN; + /* Codes_SRS_HTTP_PROXY_IO_01_070: [ When a success status code is parsed, the `on_open_complete` callback shall be triggered with `IO_OPEN_OK`, passing also the `on_open_complete_context` argument as `context`. ]*/ + http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_OK); + + if (length_remaining > 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_072: [ Any bytes that are extra (not consumed by the CONNECT response), shall be indicated as received by calling the `on_bytes_received` callback and passing the `on_bytes_received_context` as context argument. ]*/ + http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, (const unsigned char*)request_end_ptr + 4, length_remaining); + } + } + } + } + break; + } + case HTTP_PROXY_IO_STATE_OPEN: + /* Codes_SRS_HTTP_PROXY_IO_01_074: [ If `on_underlying_io_bytes_received` is called while OPEN, all bytes shall be indicated as received by calling the `on_bytes_received` callback and passing the `on_bytes_received_context` as context argument. ]*/ + http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, buffer, size); + break; + } + } +} + +static int http_proxy_io_open(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result; + + /* Codes_SRS_HTTP_PROXY_IO_01_051: [ The arguments `on_io_open_complete_context`, `on_bytes_received_context` and `on_io_error_context` shall be allowed to be NULL. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_018: [ If any of the arguments `http_proxy_io`, `on_io_open_complete`, `on_bytes_received` or `on_io_error` are NULL then `http_proxy_io_open` shall return a non-zero value. ]*/ + if ((http_proxy_io == NULL) || + (on_io_open_complete == NULL) || + (on_bytes_received == NULL) || + (on_io_error == NULL)) + { + LogError("Bad arguments: http_proxy_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error_context = %p.", + http_proxy_io, + on_io_open_complete, + on_bytes_received, + on_io_error); + result = __LINE__; + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED) + { + LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_CLOSED."); + result = __LINE__; + } + else + { + http_proxy_io_instance->on_bytes_received = on_bytes_received; + http_proxy_io_instance->on_bytes_received_context = on_bytes_received_context; + + http_proxy_io_instance->on_io_error = on_io_error; + http_proxy_io_instance->on_io_error_context = on_io_error_context; + + http_proxy_io_instance->on_io_open_complete = on_io_open_complete; + http_proxy_io_instance->on_io_open_complete_context = on_io_open_complete_context; + + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO; + + /* Codes_SRS_HTTP_PROXY_IO_01_019: [ `http_proxy_io_open` shall open the underlying IO by calling `xio_open` on the underlying IO handle created in `http_proxy_io_create`, while passing to it the callbacks `on_underlying_io_open_complete`, `on_underlying_io_bytes_received` and `on_underlying_io_error`. ]*/ + if (xio_open(http_proxy_io_instance->underlying_io, on_underlying_io_open_complete, http_proxy_io_instance, on_underlying_io_bytes_received, http_proxy_io_instance, on_underlying_io_error, http_proxy_io_instance) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_020: [ If `xio_open` fails, then `http_proxy_io_open` shall return a non-zero value. ]*/ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + LogError("Cannot open the underlying IO."); + result = __LINE__; + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_017: [ `http_proxy_io_open` shall open the HTTP proxy IO and on success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static int http_proxy_io_close(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) +{ + int result = 0; + + /* Codes_SRS_HTTP_PROXY_IO_01_052: [ `on_io_close_complete_context` shall be allowed to be NULL. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_028: [ `on_io_close_complete` shall be allowed to be NULL. ]*/ + if (http_proxy_io == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_023: [ If the argument `http_proxy_io` is NULL, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ + result = __LINE__; + LogError("NULL http_proxy_io."); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + /* Codes_SRS_HTTP_PROXY_IO_01_027: [ If `http_proxy_io_close` is called when not open, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ + if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSED) || + /* Codes_SRS_HTTP_PROXY_IO_01_054: [ `http_proxy_io_close` while OPENING shall fail and return a non-zero value. ]*/ + (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSING)) + { + result = __LINE__; + LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_OPEN."); + } + else if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO) || + (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_053: [ `http_proxy_io_close` while OPENING shall trigger the `on_io_open_complete` callback with `IO_OPEN_CANCELLED`. ]*/ + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; + (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); + http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED); + + /* Codes_SRS_HTTP_PROXY_IO_01_022: [ `http_proxy_io_close` shall close the HTTP proxy IO and on success it shall return 0. ]*/ + result = 0; + } + else + { + HTTP_PROXY_IO_STATE previous_state = http_proxy_io_instance->http_proxy_io_state; + + http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSING; + + /* Codes_SRS_HTTP_PROXY_IO_01_026: [ The `on_io_close_complete` and `on_io_close_complete_context` arguments shall be saved for later use. ]*/ + http_proxy_io_instance->on_io_close_complete = on_io_close_complete; + http_proxy_io_instance->on_io_close_complete_context = on_io_close_complete_context; + + /* Codes_SRS_HTTP_PROXY_IO_01_024: [ `http_proxy_io_close` shall close the underlying IO by calling `xio_close` on the IO handle create in `http_proxy_io_create`, while passing to it the `on_underlying_io_close_complete` callback. ]*/ + if (xio_close(http_proxy_io_instance->underlying_io, on_underlying_io_close_complete, http_proxy_io_instance) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_025: [ If `xio_close` fails, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ + result = __LINE__; + http_proxy_io_instance->http_proxy_io_state = previous_state; + LogError("Cannot close underlying IO."); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_022: [ `http_proxy_io_close` shall close the HTTP proxy IO and on success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static int http_proxy_io_send(CONCRETE_IO_HANDLE http_proxy_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* on_send_complete_context) +{ + int result; + + /* Codes_SRS_HTTP_PROXY_IO_01_032: [ `on_send_complete` shall be allowed to be NULL. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_030: [ If any of the arguments `http_proxy_io` or `buffer` is NULL, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ + if ((http_proxy_io == NULL) || + (buffer == NULL) || + /* Codes_SRS_HTTP_PROXY_IO_01_031: [ If `size` is 0, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ + (size == 0)) + { + result = __LINE__; + LogError("Bad arguments: http_proxy_io = %p, buffer = %p.", + http_proxy_io, buffer); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + /* Codes_SRS_HTTP_PROXY_IO_01_034: [ If `http_proxy_io_send` is called when the IO is not open, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_035: [ If the IO is in an error state (an error was reported through the `on_io_error` callback), `http_proxy_io_send` shall fail and return a non-zero value. ]*/ + if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_OPEN) + { + result = __LINE__; + LogError("Invalid HTTP proxy IO state. Expected state is HTTP_PROXY_IO_STATE_OPEN."); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_033: [ `http_proxy_io_send` shall send the bytes by calling `xio_send` on the underlying IO created in `http_proxy_io_create` and passing `buffer` and `size` as arguments. ]*/ + if (xio_send(http_proxy_io_instance->underlying_io, buffer, size, on_send_complete, on_send_complete_context) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_055: [ If `xio_send` fails, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ + result = __LINE__; + LogError("Underlying xio_send failed."); + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_029: [ `http_proxy_io_send` shall send the `size` bytes pointed to by `buffer` and on success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static void http_proxy_io_dowork(CONCRETE_IO_HANDLE http_proxy_io) +{ + if (http_proxy_io == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_038: [ If the `http_proxy_io` argument is NULL, `http_proxy_io_dowork` shall do nothing. ]*/ + LogError("NULL http_proxy_io."); + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED) + { + /* Codes_SRS_HTTP_PROXY_IO_01_037: [ `http_proxy_io_dowork` shall call `xio_dowork` on the underlying IO created in `http_proxy_io_create`. ]*/ + xio_dowork(http_proxy_io_instance->underlying_io); + } + } +} + +static int http_proxy_io_set_option(CONCRETE_IO_HANDLE http_proxy_io, const char* option_name, const void* value) +{ + int result; + + if ((http_proxy_io == NULL) || (option_name == NULL)) + { + /* Codes_SRS_HTTP_PROXY_IO_01_040: [ If any of the arguments `http_proxy_io` or `option_name` is NULL, `http_proxy_io_set_option` shall return a non-zero value. ]*/ + LogError("Bad arguments: http_proxy_io = %p, option_name = %p", + http_proxy_io, option_name); + result = __LINE__; + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + /* Codes_SRS_HTTP_PROXY_IO_01_045: [ None. ]*/ + + /* Codes_SRS_HTTP_PROXY_IO_01_043: [ If the `option_name` argument indicates an option that is not handled by `http_proxy_io_set_option`, then `xio_setoption` shall be called on the underlying IO created in `http_proxy_io_create`, passing the option name and value to it. ]*/ + /* Codes_SRS_HTTP_PROXY_IO_01_056: [ The `value` argument shall be allowed to be NULL. ]*/ + if (xio_setoption(http_proxy_io_instance->underlying_io, option_name, value) != 0) + { + /* Codes_SRS_HTTP_PROXY_IO_01_044: [ if `xio_setoption` fails, `http_proxy_io_set_option` shall return a non-zero value. ]*/ + LogError("Unrecognized option"); + result = __LINE__; + } + else + { + /* Codes_SRS_HTTP_PROXY_IO_01_042: [ If the option was handled by `http_proxy_io_set_option` or the underlying IO, then `http_proxy_io_set_option` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +static OPTIONHANDLER_HANDLE http_proxy_io_retrieve_options(CONCRETE_IO_HANDLE http_proxy_io) +{ + OPTIONHANDLER_HANDLE result; + + if (http_proxy_io == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_047: [ If the parameter `http_proxy_io` is NULL then `http_proxy_io_retrieve_options` shall fail and return NULL. ]*/ + LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle=%p", http_proxy_io); + result = NULL; + } + else + { + HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; + + /* Codes_SRS_HTTP_PROXY_IO_01_046: [ `http_proxy_io_retrieve_options` shall return an `OPTIONHANDLER_HANDLE` obtained by calling `xio_retrieveoptions` on the underlying IO created in `http_proxy_io_create`. ]*/ + result = xio_retrieveoptions(http_proxy_io_instance->underlying_io); + if (result == NULL) + { + /* Codes_SRS_HTTP_PROXY_IO_01_048: [ If `xio_retrieveoptions` fails, `http_proxy_io_retrieve_options` shall return NULL. ]*/ + LogError("unable to create option handler"); + } + } + return result; +} + +static const IO_INTERFACE_DESCRIPTION http_proxy_io_interface_description = +{ + http_proxy_io_retrieve_options, + http_proxy_io_create, + http_proxy_io_destroy, + http_proxy_io_open, + http_proxy_io_close, + http_proxy_io_send, + http_proxy_io_dowork, + http_proxy_io_set_option +}; + +const IO_INTERFACE_DESCRIPTION* http_proxy_io_get_interface_description(void) +{ + /* Codes_SRS_HTTP_PROXY_IO_01_049: [ `http_proxy_io_get_interface_description` shall return a pointer to an `IO_INTERFACE_DESCRIPTION` structure that contains pointers to the functions: `http_proxy_io_retrieve_options`, `http_proxy_io_retrieve_create`, `http_proxy_io_destroy`, `http_proxy_io_open`, `http_proxy_io_close`, `http_proxy_io_send` and `http_proxy_io_dowork`. ]*/ + return &http_proxy_io_interface_description; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/httpapiex.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,657 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/vector.h" + +typedef struct HTTPAPIEX_SAVED_OPTION_TAG +{ + const char* optionName; + const void* value; +}HTTPAPIEX_SAVED_OPTION; + +typedef struct HTTPAPIEX_HANDLE_DATA_TAG +{ + STRING_HANDLE hostName; + int k; + HTTP_HANDLE httpHandle; + VECTOR_HANDLE savedOptions; +}HTTPAPIEX_HANDLE_DATA; + +DEFINE_ENUM_STRINGS(HTTPAPIEX_RESULT, HTTPAPIEX_RESULT_VALUES); + +#define LOG_HTTAPIEX_ERROR() LogError("error code = %s", ENUM_TO_STRING(HTTPAPIEX_RESULT, result)) + +HTTPAPIEX_HANDLE HTTPAPIEX_Create(const char* hostName) +{ + HTTPAPIEX_HANDLE result; + /*Codes_SRS_HTTPAPIEX_02_001: [If parameter hostName is NULL then HTTPAPIEX_Create shall return NULL.]*/ + if (hostName == NULL) + { + LogError("invalid (NULL) parameter"); + result = NULL; + } + else + { + /*Codes_SRS_HTTPAPIEX_02_005: [If creating the handle fails for any reason, then HTTAPIEX_Create shall return NULL.] */ + HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)malloc(sizeof(HTTPAPIEX_HANDLE_DATA)); + if (handleData == NULL) + { + LogError("malloc failed."); + result = NULL; + } + else + { + /*Codes_SRS_HTTPAPIEX_02_002: [Parameter hostName shall be saved.]*/ + handleData->hostName = STRING_construct(hostName); + if (handleData->hostName == NULL) + { + free(handleData); + LogError("unable to STRING_construct"); + result = NULL; + } + else + { + /*Codes_SRS_HTTPAPIEX_02_004: [Otherwise, HTTPAPIEX_Create shall return a HTTAPIEX_HANDLE suitable for further calls to the module.] */ + handleData->savedOptions = VECTOR_create(sizeof(HTTPAPIEX_SAVED_OPTION)); + if (handleData->savedOptions == NULL) + { + STRING_delete(handleData->hostName); + free(handleData); + result = NULL; + } + else + { + handleData->k = -1; + handleData->httpHandle = NULL; + result = handleData; + } + } + } + } + return result; +} + +/*this function builds the default request http headers if none are specified*/ +/*returns 0 if no error*/ +/*any other code is error*/ +static int buildRequestHttpHeadersHandle(HTTPAPIEX_HANDLE_DATA *handleData, BUFFER_HANDLE requestContent, HTTP_HEADERS_HANDLE originalRequestHttpHeadersHandle, bool* isOriginalRequestHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedRequestHttpHeadersHandle) +{ + int result; + + + if (originalRequestHttpHeadersHandle != NULL) + { + *toBeUsedRequestHttpHeadersHandle = originalRequestHttpHeadersHandle; + *isOriginalRequestHttpHeadersHandle = true; + + } + else + { + /*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers + Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call + Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] + */ + *isOriginalRequestHttpHeadersHandle = false; + *toBeUsedRequestHttpHeadersHandle = HTTPHeaders_Alloc(); + } + + if (*toBeUsedRequestHttpHeadersHandle == NULL) + { + result = __FAILURE__; + LogError("unable to HTTPHeaders_Alloc"); + } + else + { + char temp[22] = { 0 }; + (void)size_tToString(temp, 22, BUFFER_length(requestContent)); /*cannot fail, MAX_uint64 has 19 digits*/ + /*Codes_SRS_HTTPAPIEX_02_011: [If parameter requestHttpHeadersHandle is not NULL then HTTPAPIEX_ExecuteRequest shall create or update the following headers of the request: + Host:{hostname} + Content-Length:the size of the requestContent parameter, and shall use the so constructed HTTPHEADERS object to all calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] + */ + /*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers + Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call + Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] + */ + if (!( + (HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Host", STRING_c_str(handleData->hostName)) == HTTP_HEADERS_OK) && + (HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Content-Length", temp) == HTTP_HEADERS_OK) + )) + { + if (! *isOriginalRequestHttpHeadersHandle) + { + HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle); + } + *toBeUsedRequestHttpHeadersHandle = NULL; + result = __FAILURE__; + } + else + { + result = 0; + } + } + return result; +} + +static int buildResponseHttpHeadersHandle(HTTP_HEADERS_HANDLE originalResponsetHttpHeadersHandle, bool* isOriginalResponseHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedResponsetHttpHeadersHandle) +{ + int result; + if (originalResponsetHttpHeadersHandle == NULL) + { + if ((*toBeUsedResponsetHttpHeadersHandle = HTTPHeaders_Alloc()) == NULL) + { + result = __FAILURE__; + } + else + { + *isOriginalResponseHttpHeadersHandle = false; + result = 0; + } + } + else + { + *isOriginalResponseHttpHeadersHandle = true; + *toBeUsedResponsetHttpHeadersHandle = originalResponsetHttpHeadersHandle; + result = 0; + } + return result; +} + + +static int buildBufferIfNotExist(BUFFER_HANDLE originalRequestContent, bool* isOriginalRequestContent, BUFFER_HANDLE* toBeUsedRequestContent) +{ + int result; + if (originalRequestContent == NULL) + { + *toBeUsedRequestContent = BUFFER_new(); + if (*toBeUsedRequestContent == NULL) + { + result = __FAILURE__; + } + else + { + *isOriginalRequestContent = false; + result = 0; + } + } + else + { + *isOriginalRequestContent = true; + *toBeUsedRequestContent = originalRequestContent; + result = 0; + } + return result; +} + +static unsigned int dummyStatusCode; + +static int buildAllRequests(HTTPAPIEX_HANDLE_DATA* handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, + HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, + HTTP_HEADERS_HANDLE responseHttpHeadersHandle, BUFFER_HANDLE responseContent, + + const char** toBeUsedRelativePath, + HTTP_HEADERS_HANDLE *toBeUsedRequestHttpHeadersHandle, bool *isOriginalRequestHttpHeadersHandle, + BUFFER_HANDLE *toBeUsedRequestContent, bool *isOriginalRequestContent, + unsigned int** toBeUsedStatusCode, + HTTP_HEADERS_HANDLE *toBeUsedResponseHttpHeadersHandle, bool *isOriginalResponseHttpHeadersHandle, + BUFFER_HANDLE *toBeUsedResponseContent, bool *isOriginalResponseContent) +{ + int result; + (void)requestType; + /*Codes_SRS_HTTPAPIEX_02_013: [If requestContent is NULL then HTTPAPIEX_ExecuteRequest shall behave as if a buffer of zero size would have been used, that is, it shall call HTTPAPI_ExecuteRequest with parameter content = NULL and contentLength = 0.]*/ + /*Codes_SRS_HTTPAPIEX_02_014: [If requestContent is not NULL then its content and its size shall be used for parameters content and contentLength of HTTPAPI_ExecuteRequest.] */ + if (buildBufferIfNotExist(requestContent, isOriginalRequestContent, toBeUsedRequestContent) != 0) + { + LogError("unable to build the request content"); + result = __FAILURE__; + } + else + { + if (buildRequestHttpHeadersHandle(handle, *toBeUsedRequestContent, requestHttpHeadersHandle, isOriginalRequestHttpHeadersHandle, toBeUsedRequestHttpHeadersHandle) != 0) + { + /*Codes_SRS_HTTPAPIEX_02_010: [If any of the operations in SRS_HTTAPIEX_02_009 fails, then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */ + if (*isOriginalRequestContent == false) + { + BUFFER_delete(*toBeUsedRequestContent); + } + LogError("unable to build the request http headers handle"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_HTTPAPIEX_02_008: [If parameter relativePath is NULL then HTTPAPIEX_INVALID_ARG shall not assume a relative path - that is, it will assume an empty path ("").] */ + if (relativePath == NULL) + { + *toBeUsedRelativePath = ""; + } + else + { + *toBeUsedRelativePath = relativePath; + } + + /*Codes_SRS_HTTPAPIEX_02_015: [If statusCode is NULL then HTTPAPIEX_ExecuteRequest shall not write in statusCode the HTTP status code, and it will use a temporary internal int for parameter statusCode to the calls of HTTPAPI_ExecuteRequest.] */ + if (statusCode == NULL) + { + /*Codes_SRS_HTTPAPIEX_02_016: [If statusCode is not NULL then If statusCode is NULL then HTTPAPIEX_ExecuteRequest shall use it for parameter statusCode to the calls of HTTPAPI_ExecuteRequest.] */ + *toBeUsedStatusCode = &dummyStatusCode; + } + else + { + *toBeUsedStatusCode = statusCode; + } + + /*Codes_SRS_HTTPAPIEX_02_017: [If responseHeaders handle is NULL then HTTPAPIEX_ExecuteRequest shall create a temporary internal instance of HTTPHEADERS object and use that for responseHeaders parameter of HTTPAPI_ExecuteRequest call.] */ + /*Codes_SRS_HTTPAPIEX_02_019: [If responseHeaders is not NULL, then then HTTPAPIEX_ExecuteRequest shall use that object as parameter responseHeaders of HTTPAPI_ExecuteRequest call.] */ + if (buildResponseHttpHeadersHandle(responseHttpHeadersHandle, isOriginalResponseHttpHeadersHandle, toBeUsedResponseHttpHeadersHandle) != 0) + { + /*Codes_SRS_HTTPAPIEX_02_018: [If creating the temporary http headers in SRS_HTTPAPIEX_02_017 fails then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */ + if (*isOriginalRequestContent == false) + { + BUFFER_delete(*toBeUsedRequestContent); + } + if (*isOriginalRequestHttpHeadersHandle == false) + { + HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle); + } + LogError("unable to build response content"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_HTTPAPIEX_02_020: [If responseContent is NULL then HTTPAPIEX_ExecuteRequest shall create a temporary internal BUFFER object and use that as parameter responseContent of HTTPAPI_ExecuteRequest call.] */ + /*Codes_SRS_HTTPAPIEX_02_022: [If responseContent is not NULL then HTTPAPIEX_ExecuteRequest use that as parameter responseContent of HTTPAPI_ExecuteRequest call.] */ + if (buildBufferIfNotExist(responseContent, isOriginalResponseContent, toBeUsedResponseContent) != 0) + { + /*Codes_SRS_HTTPAPIEX_02_021: [If creating the BUFFER_HANDLE in SRS_HTTPAPIEX_02_020 fails, then HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_ERROR.] */ + if (*isOriginalRequestContent == false) + { + BUFFER_delete(*toBeUsedRequestContent); + } + if (*isOriginalRequestHttpHeadersHandle == false) + { + HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle); + } + if (*isOriginalResponseHttpHeadersHandle == false) + { + HTTPHeaders_Free(*toBeUsedResponseHttpHeadersHandle); + } + LogError("unable to build response content"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + return result; +} + +HTTPAPIEX_RESULT HTTPAPIEX_ExecuteRequest(HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, + HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, + HTTP_HEADERS_HANDLE responseHttpHeadersHandle, BUFFER_HANDLE responseContent) +{ + HTTPAPIEX_RESULT result; + /*Codes_SRS_HTTPAPIEX_02_006: [If parameter handle is NULL then HTTPAPIEX_ExecuteRequest shall fail and return HTTPAPIEX_INVALID_ARG.]*/ + if (handle == NULL) + { + result = HTTPAPIEX_INVALID_ARG; + LOG_HTTAPIEX_ERROR(); + } + else + { + /*Codes_SRS_HTTPAPIEX_02_007: [If parameter requestType does not indicate a valid request, HTTPAPIEX_ExecuteRequest shall fail and return HTTPAPIEX_INVALID_ARG.] */ + if (requestType >= COUNT_ARG(HTTPAPI_REQUEST_TYPE_VALUES)) + { + result = HTTPAPIEX_INVALID_ARG; + LOG_HTTAPIEX_ERROR(); + } + else + { + HTTPAPIEX_HANDLE_DATA *handleData = (HTTPAPIEX_HANDLE_DATA *)handle; + + /*call to buildAll*/ + const char* toBeUsedRelativePath; + HTTP_HEADERS_HANDLE toBeUsedRequestHttpHeadersHandle; bool isOriginalRequestHttpHeadersHandle; + BUFFER_HANDLE toBeUsedRequestContent; bool isOriginalRequestContent; + unsigned int* toBeUsedStatusCode; + HTTP_HEADERS_HANDLE toBeUsedResponseHttpHeadersHandle; bool isOriginalResponseHttpHeadersHandle; + BUFFER_HANDLE toBeUsedResponseContent; bool isOriginalResponseContent; + + if (buildAllRequests(handleData, requestType, relativePath, requestHttpHeadersHandle, requestContent, statusCode, responseHttpHeadersHandle, responseContent, + &toBeUsedRelativePath, + &toBeUsedRequestHttpHeadersHandle, &isOriginalRequestHttpHeadersHandle, + &toBeUsedRequestContent, &isOriginalRequestContent, + &toBeUsedStatusCode, + &toBeUsedResponseHttpHeadersHandle, &isOriginalResponseHttpHeadersHandle, + &toBeUsedResponseContent, &isOriginalResponseContent) != 0) + { + result = HTTPAPIEX_ERROR; + LOG_HTTAPIEX_ERROR(); + } + else + { + + /*Codes_SRS_HTTPAPIEX_02_023: [HTTPAPIEX_ExecuteRequest shall try to execute the HTTP call by ensuring the following API call sequence is respected:]*/ + /*Codes_SRS_HTTPAPIEX_02_024: [If any point in the sequence fails, HTTPAPIEX_ExecuteRequest shall attempt to recover by going back to the previous step and retrying that step.]*/ + /*Codes_SRS_HTTPAPIEX_02_025: [If the first step fails, then the sequence fails.]*/ + /*Codes_SRS_HTTPAPIEX_02_026: [A step shall be retried at most once.]*/ + /*Codes_SRS_HTTPAPIEX_02_027: [If a step has been retried then all subsequent steps shall be retried too.]*/ + bool st[3] = { false, false, false }; /*the three levels of possible failure in resilient send: HTTAPI_Init, HTTPAPI_CreateConnection, HTTPAPI_ExecuteRequest*/ + if (handleData->k == -1) + { + handleData->k = 0; + } + + do + { + bool goOn; + + if (handleData->k > 2) + { + /* error */ + break; + } + + if (st[handleData->k] == true) /*already been tried*/ + { + goOn = false; + } + else + { + switch (handleData->k) + { + case 0: + { + if (HTTPAPI_Init() != HTTPAPI_OK) + { + goOn = false; + } + else + { + goOn = true; + } + break; + } + case 1: + { + if ((handleData->httpHandle = HTTPAPI_CreateConnection(STRING_c_str(handleData->hostName))) == NULL) + { + goOn = false; + } + else + { + size_t i; + size_t vectorSize = VECTOR_size(handleData->savedOptions); + for (i = 0; i < vectorSize; i++) + { + /*Codes_SRS_HTTPAPIEX_02_035: [HTTPAPIEX_ExecuteRequest shall pass all the saved options (see HTTPAPIEX_SetOption) to the newly create HTTPAPI_HANDLE in step 2 by calling HTTPAPI_SetOption.]*/ + /*Codes_SRS_HTTPAPIEX_02_036: [If setting the option fails, then the failure shall be ignored.] */ + HTTPAPIEX_SAVED_OPTION* option = (HTTPAPIEX_SAVED_OPTION*)VECTOR_element(handleData->savedOptions, i); + if (HTTPAPI_SetOption(handleData->httpHandle, option->optionName, option->value) != HTTPAPI_OK) + { + LogError("HTTPAPI_SetOption failed when called for option %s", option->optionName); + } + } + goOn = true; + } + break; + } + case 2: + { + size_t length = BUFFER_length(toBeUsedRequestContent); + unsigned char* buffer = BUFFER_u_char(toBeUsedRequestContent); + if (HTTPAPI_ExecuteRequest(handleData->httpHandle, requestType, toBeUsedRelativePath, toBeUsedRequestHttpHeadersHandle, buffer, length, toBeUsedStatusCode, toBeUsedResponseHttpHeadersHandle, toBeUsedResponseContent) != HTTPAPI_OK) + { + goOn = false; + } + else + { + goOn = true; + } + break; + } + default: + { + /*serious error*/ + goOn = false; + break; + } + } + } + + if (goOn) + { + if (handleData->k == 2) + { + /*Codes_SRS_HTTPAPIEX_02_028: [HTTPAPIEX_ExecuteRequest shall return HTTPAPIEX_OK when a call to HTTPAPI_ExecuteRequest has been completed successfully.]*/ + result = HTTPAPIEX_OK; + goto out; + } + else + { + st[handleData->k] = true; + handleData->k++; + st[handleData->k] = false; + } + } + else + { + st[handleData->k] = false; + handleData->k--; + switch (handleData->k) + { + case 0: + { + HTTPAPI_Deinit(); + break; + } + case 1: + { + HTTPAPI_CloseConnection(handleData->httpHandle); + handleData->httpHandle = NULL; + break; + } + case 2: + { + break; + } + default: + { + break; + } + } + } + } while (handleData->k >= 0); + /*Codes_SRS_HTTPAPIEX_02_029: [Otherwise, HTTAPIEX_ExecuteRequest shall return HTTPAPIEX_RECOVERYFAILED.] */ + result = HTTPAPIEX_RECOVERYFAILED; + LogError("unable to recover sending to a working state"); + out:; + /*in all cases, unbuild the temporaries*/ + if (isOriginalRequestContent == false) + { + BUFFER_delete(toBeUsedRequestContent); + } + if (isOriginalRequestHttpHeadersHandle == false) + { + HTTPHeaders_Free(toBeUsedRequestHttpHeadersHandle); + } + if (isOriginalResponseContent == false) + { + BUFFER_delete(toBeUsedResponseContent); + } + if (isOriginalResponseHttpHeadersHandle == false) + { + HTTPHeaders_Free(toBeUsedResponseHttpHeadersHandle); + } + } + } + } + return result; +} + + +void HTTPAPIEX_Destroy(HTTPAPIEX_HANDLE handle) +{ + if (handle != NULL) + { + /*Codes_SRS_HTTPAPIEX_02_042: [HTTPAPIEX_Destroy shall free all the resources used by HTTAPIEX_HANDLE.]*/ + size_t i; + size_t vectorSize; + HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)handle; + + if (handleData->k == 2) + { + HTTPAPI_CloseConnection(handleData->httpHandle); + HTTPAPI_Deinit(); + } + STRING_delete(handleData->hostName); + + vectorSize = VECTOR_size(handleData->savedOptions); + for (i = 0; i < vectorSize; i++) + { + HTTPAPIEX_SAVED_OPTION* savedOption = (HTTPAPIEX_SAVED_OPTION*)VECTOR_element(handleData->savedOptions, i); + free((void*)savedOption->optionName); + free((void*)savedOption->value); + } + VECTOR_destroy(handleData->savedOptions); + + free(handle); + } + else + { + /*Codes_SRS_HTTPAPIEX_02_043: [If parameter handle is NULL then HTTPAPIEX_Destroy shall take no action.] */ + } +} + +static bool sameName(const void* element, const void* value) +{ + return (strcmp(((HTTPAPIEX_SAVED_OPTION*)element)->optionName, (const char*)value) == 0) ? true : false; +} + +/*return 0 on success, any other value is error*/ +/*obs: value is already cloned at the time of calling this function */ +static int createOrUpdateOption(HTTPAPIEX_HANDLE_DATA* handleData, const char* optionName, const void* value) +{ + /*this function is called after the option value has been saved (cloned)*/ + int result; + + /*decide bwtween update or create*/ + HTTPAPIEX_SAVED_OPTION* whereIsIt = (HTTPAPIEX_SAVED_OPTION*)VECTOR_find_if(handleData->savedOptions, sameName, optionName); + if (whereIsIt != NULL) + { + free((void*)(whereIsIt->value)); + whereIsIt->value = value; + result = 0; + } + else + { + HTTPAPIEX_SAVED_OPTION newOption; + if (mallocAndStrcpy_s((char**)&(newOption.optionName), optionName) != 0) + { + free((void*)value); + result = __FAILURE__; + } + else + { + newOption.value = value; + if (VECTOR_push_back(handleData->savedOptions, &newOption, 1) != 0) + { + LogError("unable to VECTOR_push_back"); + free((void*)newOption.optionName); + free((void*)value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +HTTPAPIEX_RESULT HTTPAPIEX_SetOption(HTTPAPIEX_HANDLE handle, const char* optionName, const void* value) +{ + HTTPAPIEX_RESULT result; + /*Codes_SRS_HTTPAPIEX_02_032: [If parameter handle is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */ + /*Codes_SRS_HTTPAPIEX_02_033: [If parameter optionName is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */ + /*Codes_SRS_HTTPAPIEX_02_034: [If parameter value is NULL then HTTPAPIEX_SetOption shall return HTTPAPIEX_INVALID_ARG.] */ + if ( + (handle == NULL) || + (optionName == NULL) || + (value == NULL) + ) + { + result = HTTPAPIEX_INVALID_ARG; + LOG_HTTAPIEX_ERROR(); + } + else + { + const void* savedOption; + HTTPAPI_RESULT saveOptionResult; + + /*Codes_SRS_HTTPAPIEX_02_037: [HTTPAPIEX_SetOption shall attempt to save the value of the option by calling HTTPAPI_CloneOption passing optionName and value, irrespective of the existence of a HTTPAPI_HANDLE] */ + saveOptionResult = HTTPAPI_CloneOption(optionName, value, &savedOption); + + if(saveOptionResult == HTTPAPI_INVALID_ARG) + { + /*Codes_SRS_HTTPAPIEX_02_038: [If HTTPAPI_CloneOption returns HTTPAPI_INVALID_ARG then HTTPAPIEX shall return HTTPAPIEX_INVALID_ARG.] */ + result = HTTPAPIEX_INVALID_ARG; + LOG_HTTAPIEX_ERROR(); + } + else if (saveOptionResult != HTTPAPI_OK) + { + /*Codes_SRS_HTTPAPIEX_02_040: [For all other return values of HTTPAPI_SetOption, HTTPIAPIEX_SetOption shall return HTTPAPIEX_ERROR.] */ + result = HTTPAPIEX_ERROR; + LOG_HTTAPIEX_ERROR(); + } + else + { + HTTPAPIEX_HANDLE_DATA* handleData = (HTTPAPIEX_HANDLE_DATA*)handle; + /*Codes_SRS_HTTPAPIEX_02_039: [If HTTPAPI_CloneOption returns HTTPAPI_OK then HTTPAPIEX_SetOption shall create or update the pair optionName/value.]*/ + if (createOrUpdateOption(handleData, optionName, savedOption) != 0) + { + /*Codes_SRS_HTTPAPIEX_02_041: [If creating or updating the pair optionName/value fails then shall return HTTPAPIEX_ERROR.] */ + result = HTTPAPIEX_ERROR; + LOG_HTTAPIEX_ERROR(); + + } + else + { + /*Codes_SRS_HTTPAPIEX_02_031: [If HTTPAPI_HANDLE exists then HTTPAPIEX_SetOption shall call HTTPAPI_SetOption passing the same optionName and value and shall return a value conforming to the below table:] */ + if (handleData->httpHandle != NULL) + { + HTTPAPI_RESULT HTTPAPI_result = HTTPAPI_SetOption(handleData->httpHandle, optionName, value); + if (HTTPAPI_result == HTTPAPI_OK) + { + result = HTTPAPIEX_OK; + } + else if (HTTPAPI_result == HTTPAPI_INVALID_ARG) + { + result = HTTPAPIEX_INVALID_ARG; + LOG_HTTAPIEX_ERROR(); + } + else + { + result = HTTPAPIEX_ERROR; + LOG_HTTAPIEX_ERROR(); + } + } + else + { + result = HTTPAPIEX_OK; + } + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/httpapiexsas.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include <stddef.h> +#include <time.h> +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/sastoken.h" +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +typedef struct HTTPAPIEX_SAS_STATE_TAG +{ + char* key; + char* uriResource; + char* keyName; +} HTTPAPIEX_SAS_STATE; + +static HTTPAPIEX_SAS_STATE* construct_httpex_sas(const char* key, const char* uriResource, const char* keyName) +{ + HTTPAPIEX_SAS_STATE* result; + + result = (HTTPAPIEX_SAS_STATE*)malloc(sizeof(HTTPAPIEX_SAS_STATE)); + if (result == NULL) + { + LogError("Failure allocating HTTPAPIEX_SAS_Create."); + } + else + { + (void)memset(result, 0, sizeof(HTTPAPIEX_SAS_STATE)); + if (mallocAndStrcpy_s(&result->key, key) != 0) + { + /*Codes_SRS_HTTPAPIEXSAS_06_004: [If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create shall return NULL.]*/ + LogError("Failure allocating sas key."); + HTTPAPIEX_SAS_Destroy(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->uriResource, uriResource) != 0) + { + /*Codes_SRS_HTTPAPIEXSAS_06_004: [If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create shall return NULL.]*/ + LogError("Failure allocating sas uriResource."); + HTTPAPIEX_SAS_Destroy(result); + result = NULL; + } + else if (keyName != NULL && mallocAndStrcpy_s(&result->keyName, keyName) != 0) + { + /*Codes_SRS_HTTPAPIEXSAS_06_004: [If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create shall return NULL.]*/ + LogError("Failure allocating sas keyName."); + HTTPAPIEX_SAS_Destroy(result); + result = NULL; + } + } + return result; +} + +HTTPAPIEX_SAS_HANDLE HTTPAPIEX_SAS_Create_From_String(const char* key, const char* uriResource, const char* keyName) +{ + HTTPAPIEX_SAS_HANDLE result = NULL; + if (key == NULL || uriResource == NULL) + { + /* Codes_SRS_HTTPAPIEXSAS_07_001: [ If the parameter key or uriResource is NULL then HTTPAPIEX_SAS_Create_From_String shall return NULL. ] */ + LogError("Invalid parameter key: %p, uriResource: %p", key, uriResource); + result = NULL; + } + else + { + /* Codes_SRS_HTTPAPIEXSAS_07_002: [ If there are any other errors in the instantiation of this handle then HTTPAPIEX_SAS_Create_From_String shall return NULL. ] */ + result = construct_httpex_sas(key, uriResource, keyName); + } + /* Codes_SRS_HTTPAPIEXSAS_07_003: [ HTTPAPIEX_SAS_Create_From_String shall create a new instance of HTTPAPIEX_SAS and return a non-NULL handle to it ] */ + return result; +} + +HTTPAPIEX_SAS_HANDLE HTTPAPIEX_SAS_Create(STRING_HANDLE key, STRING_HANDLE uriResource, STRING_HANDLE keyName) +{ + HTTPAPIEX_SAS_HANDLE result = NULL; + if (key == NULL) + { + /*Codes_SRS_HTTPAPIEXSAS_06_001: [If the parameter key is NULL then HTTPAPIEX_SAS_Create shall return NULL.]*/ + LogError("No key passed to HTTPAPIEX_SAS_Create."); + } + else if (uriResource == NULL) + { + /*Codes_SRS_HTTPAPIEXSAS_06_002: [If the parameter uriResource is NULL then HTTPAPIEX_SAS_Create shall return NULL.]*/ + LogError("No uri resource passed to HTTPAPIEX_SAS_Create."); + } + else + { + /*Codes_SRS_HTTPAPIEXSAS_06_003: [The parameter keyName for HTTPAPIEX_SAS_Create is optional and can be NULL.]*/ + /*Codes_SRS_HTTPAPIEXSAS_01_001: [ HTTPAPIEX_SAS_Create shall create a new instance of HTTPAPIEX_SAS and return a non-NULL handle to it. ]*/ + result = construct_httpex_sas(STRING_c_str(key), STRING_c_str(uriResource), STRING_c_str(keyName) ); + } + return result; +} + +void HTTPAPIEX_SAS_Destroy(HTTPAPIEX_SAS_HANDLE handle) +{ + /*Codes_SRS_HTTPAPIEXSAS_06_005: [If the parameter handle is NULL then HTTAPIEX_SAS_Destroy shall do nothing and return.]*/ + if (handle) + { + HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)handle; + /*Codes_SRS_HTTPAPIEXSAS_06_006: [HTTAPIEX_SAS_Destroy shall deallocate any structures denoted by the parameter handle.]*/ + if (state->key) + { + free(state->key); + } + if (state->uriResource) + { + free(state->uriResource); + } + if (state->keyName) + { + free(state->keyName); + } + free(state); + } +} + +HTTPAPIEX_RESULT HTTPAPIEX_SAS_ExecuteRequest(HTTPAPIEX_SAS_HANDLE sasHandle, HTTPAPIEX_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE requestHttpHeadersHandle, BUFFER_HANDLE requestContent, unsigned int* statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent) +{ + /*Codes_SRS_HTTPAPIEXSAS_06_007: [If the parameter sasHandle is NULL then HTTPAPIEX_SAS_ExecuteRequest shall simply invoke HTTPAPIEX_ExecuteRequest with the remaining parameters (following sasHandle) as its arguments and shall return immediately with the result of that call as the result of HTTPAPIEX_SAS_ExecuteRequest.]*/ + if (sasHandle != NULL) + { + /*Codes_SRS_HTTPAPIEXSAS_06_008: [if the parameter requestHttpHeadersHandle is NULL then fallthrough.]*/ + if (requestHttpHeadersHandle != NULL) + { + /*Codes_SRS_HTTPAPIEXSAS_06_009: [HTTPHeaders_FindHeaderValue shall be invoked with the requestHttpHeadersHandle as its first argument and the string "Authorization" as its second argument.]*/ + /*Codes_SRS_HTTPAPIEXSAS_06_010: [If the return result of the invocation of HTTPHeaders_FindHeaderValue is NULL then fallthrough.]*/ + if (HTTPHeaders_FindHeaderValue(requestHttpHeadersHandle, "Authorization") != NULL) + { + HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)sasHandle; + /*Codes_SRS_HTTPAPIEXSAS_06_018: [A value of type time_t that shall be known as currentTime is obtained from calling get_time.]*/ + time_t currentTime = get_time(NULL); + /*Codes_SRS_HTTPAPIEXSAS_06_019: [If the value of currentTime is (time_t)-1 is then fallthrough.]*/ + if (currentTime == (time_t)-1) + { + LogError("Time does not appear to be working."); + } + else + { + /*Codes_SRS_HTTPAPIEXSAS_06_011: [SASToken_Create shall be invoked.]*/ + /*Codes_SRS_HTTPAPIEXSAS_06_012: [If the return result of SASToken_Create is NULL then fallthrough.]*/ + size_t expiry = (size_t)(difftime(currentTime, 0) + 3600); + STRING_HANDLE newSASToken = SASToken_CreateString(state->key, state->uriResource, state->keyName, expiry); + if (newSASToken != NULL) + { + /*Codes_SRS_HTTPAPIEXSAS_06_013: [HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (newSASToken) as its third argument.]*/ + if (HTTPHeaders_ReplaceHeaderNameValuePair(requestHttpHeadersHandle, "Authorization", STRING_c_str(newSASToken)) != HTTP_HEADERS_OK) + { + /*Codes_SRS_HTTPAPIEXSAS_06_014: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/ + LogError("Unable to replace the old SAS Token."); + } + /*Codes_SRS_HTTPAPIEXSAS_06_015: [STRING_delete(newSASToken) will be invoked.]*/ + STRING_delete(newSASToken); + } + else + { + LogError("Unable to create a new SAS token."); + } + } + } + } + } + /*Codes_SRS_HTTPAPIEXSAS_06_016: [HTTPAPIEX_ExecuteRequest with the remaining parameters (following sasHandle) as its arguments will be invoked and the result of that call is the result of HTTPAPIEX_SAS_ExecuteRequest.]*/ + return HTTPAPIEX_ExecuteRequest(handle,requestType,relativePath,requestHttpHeadersHandle,requestContent,statusCode,responseHeadersHandle,responseContent); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/httpheaders.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,335 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/httpheaders.h" +#include <string.h> +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" + +DEFINE_ENUM_STRINGS(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES); + +typedef struct HTTP_HEADERS_HANDLE_DATA_TAG +{ + MAP_HANDLE headers; +} HTTP_HEADERS_HANDLE_DATA; + +HTTP_HEADERS_HANDLE HTTPHeaders_Alloc(void) +{ + /*Codes_SRS_HTTP_HEADERS_99_002:[ This API shall produce a HTTP_HANDLE that can later be used in subsequent calls to the module.]*/ + HTTP_HEADERS_HANDLE_DATA* result; + result = (HTTP_HEADERS_HANDLE_DATA*)malloc(sizeof(HTTP_HEADERS_HANDLE_DATA)); + + if (result == NULL) + { + LogError("malloc failed"); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_004:[ After a successful init, HTTPHeaders_GetHeaderCount shall report 0 existing headers.]*/ + result->headers = Map_Create(NULL); + if (result->headers == NULL) + { + LogError("Map_Create failed"); + free(result); + result = NULL; + } + else + { + /*all is fine*/ + } + } + + /*Codes_SRS_HTTP_HEADERS_99_003:[ The function shall return NULL when the function cannot execute properly]*/ + return (HTTP_HEADERS_HANDLE)result; +} + +/*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/ +void HTTPHeaders_Free(HTTP_HEADERS_HANDLE handle) +{ + /*Codes_SRS_HTTP_HEADERS_02_001: [If httpHeadersHandle is NULL then HTTPHeaders_Free shall perform no action.] */ + if (handle == NULL) + { + /*do nothing*/ + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/ + HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle; + + Map_Destroy(handleData->headers); + free(handleData); + } +} + +/*Codes_SRS_HTTP_HEADERS_99_012:[ Calling this API shall record a header from name and value parameters.]*/ +static HTTP_HEADERS_RESULT headers_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE handle, const char* name, const char* value, bool replace) +{ + HTTP_HEADERS_RESULT result; + /*Codes_SRS_HTTP_HEADERS_99_014:[ The function shall return when the handle is not valid or when name parameter is NULL or when value parameter is NULL.]*/ + if ( + (handle == NULL) || + (name == NULL) || + (value == NULL) + ) + { + result = HTTP_HEADERS_INVALID_ARG; + LogError("invalid arg (NULL) , result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_036:[ If name contains the characters outside character codes 33 to 126 then the return value shall be HTTP_HEADERS_INVALID_ARG]*/ + /*Codes_SRS_HTTP_HEADERS_99_031:[ If name contains the character ":" then the return value shall be HTTP_HEADERS_INVALID_ARG.]*/ + size_t i; + size_t nameLen = strlen(name); + for (i = 0; i < nameLen; i++) + { + if ((name[i] < 33) || (126 < name[i]) || (name[i] == ':')) + { + break; + } + } + + if (i < nameLen) + { + result = HTTP_HEADERS_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle; + const char* existingValue = Map_GetValueFromKey(handleData->headers, name); + /*eat up the whitespaces from value, as per RFC 2616, chapter 4.2 "The field value MAY be preceded by any amount of LWS, though a single SP is preferred."*/ + /*Codes_SRS_HTTP_HEADERS_02_002: [The LWS from the beginning of the value shall not be stored.] */ + while ((value[0] == ' ') || (value[0] == '\t') || (value[0] == '\r') || (value[0] == '\n')) + { + value++; + } + + if (!replace && (existingValue != NULL)) + { + size_t existingValueLen = strlen(existingValue); + size_t valueLen = strlen(value); + char* newValue = (char*)malloc(sizeof(char) * (existingValueLen + /*COMMA_AND_SPACE_LENGTH*/ 2 + valueLen + /*EOL*/ 1)); + if (newValue == NULL) + { + /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/ + result = HTTP_HEADERS_ALLOC_FAILED; + LogError("failed to malloc , result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + char* runNewValue; + /*Codes_SRS_HTTP_HEADERS_99_017:[ If the name already exists in the collection of headers, the function shall concatenate the new value after the existing value, separated by a comma and a space as in: old-value+", "+new-value.]*/ + (void)memcpy(newValue, existingValue, existingValueLen); + runNewValue = newValue + existingValueLen; + (*runNewValue++) = ','; + (*runNewValue++) = ' '; + (void)memcpy(runNewValue, value, valueLen + /*EOL*/ 1); + + /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/ + if (Map_AddOrUpdate(handleData->headers, name, newValue) != MAP_OK) + { + /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/ + result = HTTP_HEADERS_ERROR; + LogError("failed to Map_AddOrUpdate, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_013:[ The function shall return HTTP_HEADERS_OK when execution is successful.]*/ + result = HTTP_HEADERS_OK; + } + free(newValue); + } + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/ + if (Map_AddOrUpdate(handleData->headers, name, value) != MAP_OK) + { + /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/ + result = HTTP_HEADERS_ALLOC_FAILED; + LogError("failed to Map_AddOrUpdate, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + result = HTTP_HEADERS_OK; + } + } + } + } + + return result; +} + +HTTP_HEADERS_RESULT HTTPHeaders_AddHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value) +{ + return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, false); +} + +/* Codes_SRS_HTTP_HEADERS_06_001: [This API will perform exactly as HTTPHeaders_AddHeaderNameValuePair except that if the header name already exists the already existing value will be replaced as opposed to concatenated to.] */ +HTTP_HEADERS_RESULT HTTPHeaders_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value) +{ + return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, true); +} + + +const char* HTTPHeaders_FindHeaderValue(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name) +{ + const char* result; + /*Codes_SRS_HTTP_HEADERS_99_022:[ The return value shall be NULL if name parameter is NULL or if httpHeadersHandle is NULL]*/ + if ( + (httpHeadersHandle == NULL) || + (name == NULL) + ) + { + result = NULL; + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_018:[ Calling this API shall retrieve the value for a previously stored name.]*/ + /*Codes_SRS_HTTP_HEADERS_99_020:[ The return value shall be different than NULL when the name matches the name of a previously stored name:value pair.] */ + /*Codes_SRS_HTTP_HEADERS_99_021:[ In this case the return value shall point to a string that shall strcmp equal to the original stored string.]*/ + HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)httpHeadersHandle; + result = Map_GetValueFromKey(handleData->headers, name); + } + return result; + +} + +HTTP_HEADERS_RESULT HTTPHeaders_GetHeaderCount(HTTP_HEADERS_HANDLE handle, size_t* headerCount) +{ + HTTP_HEADERS_RESULT result; + /*Codes_SRS_HTTP_HEADERS_99_024:[ The function shall return HTTP_HEADERS_INVALID_ARG when an invalid handle is passed.]*/ + /*Codes_SRS_HTTP_HEADERS_99_025:[ The function shall return HTTP_HEADERS_INVALID_ARG when headersCount is NULL.]*/ + if ((handle == NULL) || + (headerCount == NULL)) + { + result = HTTP_HEADERS_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + HTTP_HEADERS_HANDLE_DATA *handleData = (HTTP_HEADERS_HANDLE_DATA *)handle; + const char*const* keys; + const char*const* values; + /*Codes_SRS_HTTP_HEADERS_99_023:[ Calling this API shall provide the number of stored headers.]*/ + if (Map_GetInternals(handleData->headers, &keys, &values, headerCount) != MAP_OK) + { + /*Codes_SRS_HTTP_HEADERS_99_037:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs.]*/ + result = HTTP_HEADERS_ERROR; + LogError("Map_GetInternals failed, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_026:[ The function shall write in *headersCount the number of currently stored headers and shall return HTTP_HEADERS_OK]*/ + result = HTTP_HEADERS_OK; + } + } + + return result; +} + +/*produces a string in *destination that is equal to name: value*/ +HTTP_HEADERS_RESULT HTTPHeaders_GetHeader(HTTP_HEADERS_HANDLE handle, size_t index, char** destination) +{ + HTTP_HEADERS_RESULT result = HTTP_HEADERS_OK; + + /*Codes_SRS_HTTP_HEADERS_99_028:[ The function shall return NULL if the handle is invalid.]*/ + /*Codes_SRS_HTTP_HEADERS_99_032:[ The function shall return HTTP_HEADERS_INVALID_ARG if the destination is NULL]*/ + if ( + (handle == NULL) || + (destination == NULL) + ) + { + result = HTTP_HEADERS_INVALID_ARG; + LogError("invalid arg (NULL), result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + /*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/ + else + { + HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle; + const char*const* keys; + const char*const* values; + size_t headerCount; + if (Map_GetInternals(handleData->headers, &keys, &values, &headerCount) != MAP_OK) + { + /*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/ + result = HTTP_HEADERS_ERROR; + LogError("Map_GetInternals failed, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/ + if (index >= headerCount) + { + result = HTTP_HEADERS_INVALID_ARG; + LogError("index out of bounds, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + size_t keyLen = strlen(keys[index]); + size_t valueLen = strlen(values[index]); + *destination = (char*)malloc(sizeof(char) * (keyLen + /*COLON_AND_SPACE_LENGTH*/ 2 + valueLen + /*EOL*/ 1)); + if (*destination == NULL) + { + /*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/ + result = HTTP_HEADERS_ERROR; + LogError("unable to malloc, result= %s", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result)); + } + else + { + /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/ + /*Codes_SRS_HTTP_HEADERS_99_027:[ Calling this API shall produce the string value+": "+pair) for the index header in the *destination parameter.]*/ + char* runDestination = (*destination); + (void)memcpy(runDestination, keys[index], keyLen); + runDestination += keyLen; + (*runDestination++) = ':'; + (*runDestination++) = ' '; + (void)memcpy(runDestination, values[index], valueLen + /*EOL*/ 1); + /*Codes_SRS_HTTP_HEADERS_99_035:[ The function shall return HTTP_HEADERS_OK when the function executed without error.]*/ + result = HTTP_HEADERS_OK; + } + } + } + } + + return result; +} + +HTTP_HEADERS_HANDLE HTTPHeaders_Clone(HTTP_HEADERS_HANDLE handle) +{ + HTTP_HEADERS_HANDLE_DATA* result; + /*Codes_SRS_HTTP_HEADERS_02_003: [If handle is NULL then HTTPHeaders_Clone shall return NULL.] */ + if (handle == NULL) + { + result = NULL; + } + else + { + /*Codes_SRS_HTTP_HEADERS_02_004: [Otherwise HTTPHeaders_Clone shall clone the content of handle to a new handle.] */ + result = (HTTP_HEADERS_HANDLE_DATA*)malloc(sizeof(HTTP_HEADERS_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */ + } + else + { + HTTP_HEADERS_HANDLE_DATA* handleData = handle; + result->headers = Map_Clone(handleData->headers); + if (result->headers == NULL) + { + /*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */ + free(result); + result = NULL; + } + else + { + /*all is fine*/ + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/map.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,677 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" + +DEFINE_ENUM_STRINGS(MAP_RESULT, MAP_RESULT_VALUES); + +typedef struct MAP_HANDLE_DATA_TAG +{ + char** keys; + char** values; + size_t count; + MAP_FILTER_CALLBACK mapFilterCallback; +}MAP_HANDLE_DATA; + +#define LOG_MAP_ERROR LogError("result = %s", ENUM_TO_STRING(MAP_RESULT, result)); + +MAP_HANDLE Map_Create(MAP_FILTER_CALLBACK mapFilterFunc) +{ + /*Codes_SRS_MAP_02_001: [Map_Create shall create a new, empty map.]*/ + MAP_HANDLE_DATA* result = (MAP_HANDLE_DATA*)malloc(sizeof(MAP_HANDLE_DATA)); + /*Codes_SRS_MAP_02_002: [If during creation there are any error, then Map_Create shall return NULL.]*/ + if (result != NULL) + { + /*Codes_SRS_MAP_02_003: [Otherwise, it shall return a non-NULL handle that can be used in subsequent calls.] */ + result->keys = NULL; + result->values = NULL; + result->count = 0; + result->mapFilterCallback = mapFilterFunc; + } + return (MAP_HANDLE)result; +} + +void Map_Destroy(MAP_HANDLE handle) +{ + /*Codes_SRS_MAP_02_005: [If parameter handle is NULL then Map_Destroy shall take no action.] */ + if (handle != NULL) + { + /*Codes_SRS_MAP_02_004: [Map_Destroy shall release all resources associated with the map.] */ + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + size_t i; + + for (i = 0; i < handleData->count; i++) + { + free(handleData->keys[i]); + free(handleData->values[i]); + } + free(handleData->keys); + free(handleData->values); + free(handleData); + } +} + +/*makes a copy of a vector of const char*, having size "size". source cannot be NULL*/ +/*returns NULL if it fails*/ +static char** Map_CloneVector(const char*const * source, size_t count) +{ + char** result; + result = (char**)malloc(count *sizeof(char*)); + if (result == NULL) + { + /*do nothing, just return it (NULL)*/ + } + else + { + size_t i; + for (i = 0; i < count; i++) + { + if (mallocAndStrcpy_s(result + i, source[i]) != 0) + { + break; + } + } + + if (i == count) + { + /*it is all good, proceed to return result*/ + } + else + { + size_t j; + for (j = 0; j < i; j++) + { + free(result[j]); + } + free(result); + result = NULL; + } + } + return result; +} + +/*Codes_SRS_MAP_02_039: [Map_Clone shall make a copy of the map indicated by parameter handle and return a non-NULL handle to it.]*/ +MAP_HANDLE Map_Clone(MAP_HANDLE handle) +{ + MAP_HANDLE_DATA* result; + if (handle == NULL) + { + /*Codes_SRS_MAP_02_038: [Map_Clone returns NULL if parameter handle is NULL.]*/ + result = NULL; + LogError("invalid arg to Map_Clone (NULL)"); + } + else + { + MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle; + result = (MAP_HANDLE_DATA*)malloc(sizeof(MAP_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */ + /*do nothing, proceed to return it, this is an error case*/ + LogError("unable to malloc"); + } + else + { + if (handleData->count == 0) + { + result->count = 0; + result->keys = NULL; + result->values = NULL; + result->mapFilterCallback = NULL; + } + else + { + result->mapFilterCallback = handleData->mapFilterCallback; + result->count = handleData->count; + if( (result->keys = Map_CloneVector((const char* const*)handleData->keys, handleData->count))==NULL) + { + /*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */ + LogError("unable to clone keys"); + free(result); + result = NULL; + } + else if ((result->values = Map_CloneVector((const char* const*)handleData->values, handleData->count)) == NULL) + { + size_t i; + /*Codes_SRS_MAP_02_047: [If during cloning, any operation fails, then Map_Clone shall return NULL.] */ + LogError("unable to clone values"); + for (i = 0; i < result->count; i++) + { + free(result->keys[i]); + } + free(result->keys); + free(result); + result = NULL; + } + else + { + /*all fine, return it*/ + } + } + } + } + return (MAP_HANDLE)result; +} + +static int Map_IncreaseStorageKeysValues(MAP_HANDLE_DATA* handleData) +{ + int result; + char** newKeys = (char**)realloc(handleData->keys, (handleData->count + 1) * sizeof(char*)); + if (newKeys == NULL) + { + LogError("realloc error"); + result = __FAILURE__; + } + else + { + char** newValues; + handleData->keys = newKeys; + handleData->keys[handleData->count] = NULL; + newValues = (char**)realloc(handleData->values, (handleData->count + 1) * sizeof(char*)); + if (newValues == NULL) + { + LogError("realloc error"); + if (handleData->count == 0) /*avoiding an implementation defined behavior */ + { + free(handleData->keys); + handleData->keys = NULL; + } + else + { + char** undoneKeys = (char**)realloc(handleData->keys, (handleData->count) * sizeof(char*)); + if (undoneKeys == NULL) + { + LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size"); + } + else + { + handleData->keys = undoneKeys; + } + } + result = __FAILURE__; + } + else + { + handleData->values = newValues; + handleData->values[handleData->count] = NULL; + handleData->count++; + result = 0; + } + } + return result; +} + +static void Map_DecreaseStorageKeysValues(MAP_HANDLE_DATA* handleData) +{ + if (handleData->count == 1) + { + free(handleData->keys); + handleData->keys = NULL; + free(handleData->values); + handleData->values = NULL; + handleData->count = 0; + handleData->mapFilterCallback = NULL; + } + else + { + /*certainly > 1...*/ + char** undoneValues; + char** undoneKeys = (char**)realloc(handleData->keys, sizeof(char*)* (handleData->count - 1)); + if (undoneKeys == NULL) + { + LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size"); + } + else + { + handleData->keys = undoneKeys; + } + + undoneValues = (char**)realloc(handleData->values, sizeof(char*)* (handleData->count - 1)); + if (undoneValues == NULL) + { + LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size"); + } + else + { + handleData->values = undoneValues; + } + + handleData->count--; + } +} + +static char** findKey(MAP_HANDLE_DATA* handleData, const char* key) +{ + char** result; + if (handleData->keys == NULL) + { + result = NULL; + } + else + { + size_t i; + result = NULL; + for (i = 0; i < handleData->count; i++) + { + if (strcmp(handleData->keys[i], key) == 0) + { + result = handleData->keys + i; + break; + } + } + } + return result; +} + +static char** findValue(MAP_HANDLE_DATA* handleData, const char* value) +{ + char** result; + if (handleData->values == NULL) + { + result = NULL; + } + else + { + size_t i; + result = NULL; + for (i = 0; i < handleData->count; i++) + { + if (strcmp(handleData->values[i], value) == 0) + { + result = handleData->values + i; + break; + } + } + } + return result; +} + +static int insertNewKeyValue(MAP_HANDLE_DATA* handleData, const char* key, const char* value) +{ + int result; + if (Map_IncreaseStorageKeysValues(handleData) != 0) /*this increases handleData->count*/ + { + result = __FAILURE__; + } + else + { + if (mallocAndStrcpy_s(&(handleData->keys[handleData->count - 1]), key) != 0) + { + Map_DecreaseStorageKeysValues(handleData); + LogError("unable to mallocAndStrcpy_s"); + result = __FAILURE__; + } + else + { + if (mallocAndStrcpy_s(&(handleData->values[handleData->count - 1]), value) != 0) + { + free(handleData->keys[handleData->count - 1]); + Map_DecreaseStorageKeysValues(handleData); + LogError("unable to mallocAndStrcpy_s"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + return result; +} + +MAP_RESULT Map_Add(MAP_HANDLE handle, const char* key, const char* value) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_006: [If parameter handle is NULL then Map_Add shall return MAP_INVALID_ARG.] */ + /*Codes_SRS_MAP_02_007: [If parameter key is NULL then Map_Add shall return MAP_INVALID_ARG.]*/ + /*Codes_SRS_MAP_02_008: [If parameter value is NULL then Map_Add shall return MAP_INVALID_ARG.] */ + if ( + (handle == NULL) || + (key == NULL) || + (value == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + /*Codes_SRS_MAP_02_009: [If the key already exists, then Map_Add shall return MAP_KEYEXISTS.] */ + if (findKey(handleData, key) != NULL) + { + result = MAP_KEYEXISTS; + } + else + { + /* Codes_SRS_MAP_07_009: [If the mapFilterCallback function is not NULL, then the return value will be check and if it is not zero then Map_Add shall return MAP_FILTER_REJECT.] */ + if ( (handleData->mapFilterCallback != NULL) && (handleData->mapFilterCallback(key, value) != 0) ) + { + result = MAP_FILTER_REJECT; + } + else + { + /*Codes_SRS_MAP_02_010: [Otherwise, Map_Add shall add the pair <key,value> to the map.] */ + if (insertNewKeyValue(handleData, key, value) != 0) + { + /*Codes_SRS_MAP_02_011: [If adding the pair <key,value> fails then Map_Add shall return MAP_ERROR.] */ + result = MAP_ERROR; + LOG_MAP_ERROR; + } + else + { + /*Codes_SRS_MAP_02_012: [Otherwise, Map_Add shall return MAP_OK.] */ + result = MAP_OK; + } + } + } + } + return result; +} + +MAP_RESULT Map_AddOrUpdate(MAP_HANDLE handle, const char* key, const char* value) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_013: [If parameter handle is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.]*/ + /*Codes_SRS_MAP_02_014: [If parameter key is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.]*/ + /*Codes_SRS_MAP_02_015: [If parameter value is NULL then Map_AddOrUpdate shall return MAP_INVALID_ARG.] */ + if ( + (handle == NULL) || + (key == NULL) || + (value == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + + /* Codes_SRS_MAP_07_008: [If the mapFilterCallback function is not NULL, then the return value will be check and if it is not zero then Map_AddOrUpdate shall return MAP_FILTER_REJECT.] */ + if (handleData->mapFilterCallback != NULL && handleData->mapFilterCallback(key, value) != 0) + { + result = MAP_FILTER_REJECT; + } + else + { + char** whereIsIt = findKey(handleData, key); + if (whereIsIt == NULL) + { + /*Codes_SRS_MAP_02_017: [Otherwise, Map_AddOrUpdate shall add the pair <key,value> to the map.]*/ + if (insertNewKeyValue(handleData, key, value) != 0) + { + result = MAP_ERROR; + LOG_MAP_ERROR; + } + else + { + result = MAP_OK; + } + } + else + { + /*Codes_SRS_MAP_02_016: [If the key already exists, then Map_AddOrUpdate shall overwrite the value of the existing key with parameter value.]*/ + size_t index = whereIsIt - handleData->keys; + size_t valueLength = strlen(value); + /*try to realloc value of this key*/ + char* newValue = (char*)realloc(handleData->values[index],valueLength + 1); + if (newValue == NULL) + { + result = MAP_ERROR; + LOG_MAP_ERROR; + } + else + { + (void)memcpy(newValue, value, valueLength + 1); + handleData->values[index] = newValue; + /*Codes_SRS_MAP_02_019: [Otherwise, Map_AddOrUpdate shall return MAP_OK.] */ + result = MAP_OK; + } + } + } + } + return result; +} + +MAP_RESULT Map_Delete(MAP_HANDLE handle, const char* key) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_020: [If parameter handle is NULL then Map_Delete shall return MAP_INVALIDARG.]*/ + /*Codes_SRS_MAP_02_021: [If parameter key is NULL then Map_Delete shall return MAP_INVALIDARG.]*/ + if ( + (handle == NULL) || + (key == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + char** whereIsIt = findKey(handleData,key); + if (whereIsIt == NULL) + { + /*Codes_SRS_MAP_02_022: [If key does not exist then Map_Delete shall return MAP_KEYNOTFOUND.]*/ + result = MAP_KEYNOTFOUND; + } + else + { + /*Codes_SRS_MAP_02_023: [Otherwise, Map_Delete shall remove the key and its associated value from the map and return MAP_OK.]*/ + size_t index = whereIsIt - handleData->keys; + free(handleData->keys[index]); + free(handleData->values[index]); + memmove(handleData->keys + index, handleData->keys + index + 1, (handleData->count - index - 1)*sizeof(char*)); /*if order doesn't matter... then this can be optimized*/ + memmove(handleData->values + index, handleData->values + index + 1, (handleData->count - index - 1)*sizeof(char*)); + Map_DecreaseStorageKeysValues(handleData); + result = MAP_OK; + } + + } + return result; +} + +MAP_RESULT Map_ContainsKey(MAP_HANDLE handle, const char* key, bool* keyExists) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_024: [If parameter handle, key or keyExists are NULL then Map_ContainsKey shall return MAP_INVALIDARG.]*/ + if ( + (handle ==NULL) || + (key == NULL) || + (keyExists == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + /*Codes_SRS_MAP_02_025: [Otherwise if a key exists then Map_ContainsKey shall return MAP_OK and shall write in keyExists "true".]*/ + /*Codes_SRS_MAP_02_026: [If a key doesn't exist, then Map_ContainsKey shall return MAP_OK and write in keyExists "false".] */ + *keyExists = (findKey(handleData, key) != NULL) ? true: false; + result = MAP_OK; + } + return result; +} + +MAP_RESULT Map_ContainsValue(MAP_HANDLE handle, const char* value, bool* valueExists) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_027: [If parameter handle, value or valueExists is NULL then Map_ContainsValue shall return MAP_INVALIDARG.] */ + if ( + (handle == NULL) || + (value == NULL) || + (valueExists == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA*)handle; + /*Codes_SRS_MAP_02_028: [Otherwise, if a pair <key, value> has its value equal to the parameter value, the Map_ContainsValue shall return MAP_OK and shall write in valueExists "true".]*/ + /*Codes_SRS_MAP_02_029: [Otherwise, if such a <key, value> does not exist, then Map_ContainsValue shall return MAP_OK and shall write in valueExists "false".] */ + *valueExists = (findValue(handleData, value) != NULL) ? true : false; + result = MAP_OK; + } + return result; +} + +const char* Map_GetValueFromKey(MAP_HANDLE handle, const char* key) +{ + const char* result; + /*Codes_SRS_MAP_02_040: [If parameter handle or key is NULL then Map_GetValueFromKey returns NULL.]*/ + if ( + (handle == NULL) || + (key == NULL) + ) + { + result = NULL; + LogError("invalid parameter to Map_GetValueFromKey"); + } + else + { + MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle; + char** whereIsIt = findKey(handleData, key); + if(whereIsIt == NULL) + { + /*Codes_SRS_MAP_02_041: [If the key is not found, then Map_GetValueFromKey returns NULL.]*/ + result = NULL; + } + else + { + /*Codes_SRS_MAP_02_042: [Otherwise, Map_GetValueFromKey returns the key's value.] */ + size_t index = whereIsIt - handleData->keys; + result = handleData->values[index]; + } + } + return result; +} + +MAP_RESULT Map_GetInternals(MAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count) +{ + MAP_RESULT result; + /*Codes_SRS_MAP_02_046: [If parameter handle, keys, values or count is NULL then Map_GetInternals shall return MAP_INVALIDARG.] */ + if ( + (handle == NULL) || + (keys == NULL) || + (values == NULL) || + (count == NULL) + ) + { + result = MAP_INVALIDARG; + LOG_MAP_ERROR; + } + else + { + /*Codes_SRS_MAP_02_043: [Map_GetInternals shall produce in *keys an pointer to an array of const char* having all the keys stored so far by the map.]*/ + /*Codes_SRS_MAP_02_044: [Map_GetInternals shall produce in *values a pointer to an array of const char* having all the values stored so far by the map.]*/ + /*Codes_SRS_MAP_02_045: [ Map_GetInternals shall produce in *count the number of stored keys and values.]*/ + MAP_HANDLE_DATA * handleData = (MAP_HANDLE_DATA *)handle; + *keys =(const char* const*)(handleData->keys); + *values = (const char* const*)(handleData->values); + *count = handleData->count; + result = MAP_OK; + } + return result; +} + +STRING_HANDLE Map_ToJSON(MAP_HANDLE handle) +{ + STRING_HANDLE result; + /*Codes_SRS_MAP_02_052: [If parameter handle is NULL then Map_ToJSON shall return NULL.] */ + if (handle == NULL) + { + result = NULL; + LogError("invalid arg (NULL)"); + } + else + { + /*Codes_SRS_MAP_02_048: [Map_ToJSON shall produce a STRING_HANDLE representing the content of the MAP.] */ + result = STRING_construct("{"); + if (result == NULL) + { + LogError("STRING_construct failed"); + } + else + { + size_t i; + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA *)handle; + /*Codes_SRS_MAP_02_049: [If the MAP is empty, then Map_ToJSON shall produce the string "{}".*/ + bool breakFor = false; /*used to break out of for*/ + for (i = 0; (i < handleData->count) && (!breakFor); i++) + { + /*add one entry to the JSON*/ + /*Codes_SRS_MAP_02_050: [If the map has properties then Map_ToJSON shall produce the following string:{"name1":"value1", "name2":"value2" ...}]*/ + STRING_HANDLE key = STRING_new_JSON(handleData->keys[i]); + if (key == NULL) + { + LogError("STRING_new_JSON failed"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + STRING_HANDLE value = STRING_new_JSON(handleData->values[i]); + if (value == NULL) + { + LogError("STRING_new_JSON failed"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + if (!( + ((i>0) ? (STRING_concat(result, ",") == 0) : 1) && + (STRING_concat_with_STRING(result, key) == 0) && + (STRING_concat(result, ":") == 0) && + (STRING_concat_with_STRING(result, value) == 0) + )) + { + LogError("failed to build the JSON"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + /*all nice, go to the next element in the map*/ + } + STRING_delete(value); + } + STRING_delete(key); + } + } + + if (breakFor) + { + LogError("error happened during JSON string builder"); + } + else + { + if (STRING_concat(result, "}") != 0) + { + LogError("failed to build the JSON"); + STRING_delete(result); + result = NULL; + } + else + { + /*return as is, JSON has been build*/ + } + } + } + } + return result; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/optionhandler.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,268 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/vector.h" + +typedef struct OPTION_TAG +{ + const char* name; + void* storage; +}OPTION; + +typedef struct OPTIONHANDLER_HANDLE_DATA_TAG +{ + pfCloneOption cloneOption; + pfDestroyOption destroyOption; + pfSetOption setOption; + VECTOR_HANDLE storage; +}OPTIONHANDLER_HANDLE_DATA; + +static OPTIONHANDLER_HANDLE CreateInternal(pfCloneOption cloneOption, pfDestroyOption destroyOption, pfSetOption setOption) +{ + OPTIONHANDLER_HANDLE result; + + result = (OPTIONHANDLER_HANDLE_DATA*)malloc(sizeof(OPTIONHANDLER_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_OPTIONHANDLER_02_004: [ Otherwise, OptionHandler_Create shall fail and return NULL. ]*/ + LogError("unable to malloc"); + /*return as is*/ + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_002: [ OptionHandler_Create shall create an empty VECTOR that will hold pairs of const char* and void*. ]*/ + result->storage = VECTOR_create(sizeof(OPTION)); + if (result->storage == NULL) + { + /*Codes_SRS_OPTIONHANDLER_02_004: [ Otherwise, OptionHandler_Create shall fail and return NULL. ]*/ + LogError("unable to VECTOR_create"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_003: [ If all the operations succeed then OptionHandler_Create shall succeed and return a non-NULL handle. ]*/ + result->cloneOption = cloneOption; + result->destroyOption = destroyOption; + result->setOption = setOption; + /*return as is*/ + } + } + + return result; +} + +static OPTIONHANDLER_RESULT AddOptionInternal(OPTIONHANDLER_HANDLE handle, const char* name, const void* value) +{ + OPTIONHANDLER_RESULT result; + const char* cloneOfName; + if (mallocAndStrcpy_s((char**)&cloneOfName, name) != 0) + { + /*Codes_SRS_OPTIONHANDLER_02_009: [ Otherwise, OptionHandler_AddProperty shall succeed and return OPTIONHANDLER_ERROR. ]*/ + LogError("unable to clone name"); + result = OPTIONHANDLER_ERROR; + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_006: [ OptionHandler_AddProperty shall call pfCloneOption passing name and value. ]*/ + void* cloneOfValue = handle->cloneOption(name, value); + if (cloneOfValue == NULL) + { + /*Codes_SRS_OPTIONHANDLER_02_009: [ Otherwise, OptionHandler_AddProperty shall succeed and return OPTIONHANDLER_ERROR. ]*/ + LogError("unable to clone value"); + free((void*)cloneOfName); + result = OPTIONHANDLER_ERROR; + } + else + { + OPTION temp; + temp.name = cloneOfName; + temp.storage = cloneOfValue; + /*Codes_SRS_OPTIONHANDLER_02_007: [ OptionHandler_AddProperty shall use VECTOR APIs to save the name and the newly created clone of value. ]*/ + if (VECTOR_push_back(handle->storage, &temp, 1) != 0) + { + /*Codes_SRS_OPTIONHANDLER_02_009: [ Otherwise, OptionHandler_AddProperty shall succeed and return OPTIONHANDLER_ERROR. ]*/ + LogError("unable to VECTOR_push_back"); + handle->destroyOption(name, cloneOfValue); + free((void*)cloneOfName); + result = OPTIONHANDLER_ERROR; + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_008: [ If all the operations succed then OptionHandler_AddProperty shall succeed and return OPTIONHANDLER_OK. ]*/ + result = OPTIONHANDLER_OK; + } + } + } + + return result; +} + +static void DestroyInternal(OPTIONHANDLER_HANDLE handle) +{ + /*Codes_SRS_OPTIONHANDLER_02_016: [ Otherwise, OptionHandler_Destroy shall free all used resources. ]*/ + size_t nOptions = VECTOR_size(handle->storage), i; + for (i = 0; i < nOptions; i++) + { + OPTION* option = (OPTION*)VECTOR_element(handle->storage, i); + handle->destroyOption(option->name, option->storage); + free((void*)option->name); + } + + VECTOR_destroy(handle->storage); + free(handle); +} + +OPTIONHANDLER_HANDLE OptionHandler_Create(pfCloneOption cloneOption, pfDestroyOption destroyOption, pfSetOption setOption) +{ + /*Codes_SRS_OPTIONHANDLER_02_001: [ OptionHandler_Create shall fail and retun NULL if any parameters are NULL. ]*/ + OPTIONHANDLER_HANDLE_DATA* result; + if ( + (cloneOption == NULL) || + (destroyOption == NULL) || + (setOption == NULL) + ) + { + LogError("invalid parameter = pfCloneOption cloneOption=%p, pfDestroyOption destroyOption=%p, pfSetOption setOption=%p", cloneOption, destroyOption, setOption); + result = NULL; + } + else + { + result = CreateInternal(cloneOption, destroyOption, setOption); + } + + return result; + +} + +OPTIONHANDLER_HANDLE OptionHandler_Clone(OPTIONHANDLER_HANDLE handler) +{ + OPTIONHANDLER_HANDLE_DATA* result; + + if (handler == NULL) + { + /* Codes_SRS_OPTIONHANDLER_01_010: [ If `handler` is NULL, OptionHandler_Clone shall fail and return NULL. ]*/ + LogError("NULL argument: handler"); + result = NULL; + } + else + { + /* Codes_SRS_OPTIONHANDLER_01_001: [ `OptionHandler_Clone` shall clone an existing option handler instance. ]*/ + /* Codes_SRS_OPTIONHANDLER_01_002: [ On success it shall return a non-NULL handle. ]*/ + /* Codes_SRS_OPTIONHANDLER_01_003: [ `OptionHandler_Clone` shall allocate memory for the new option handler instance. ]*/ + result = CreateInternal(handler->cloneOption, handler->destroyOption, handler->setOption); + if (result == NULL) + { + /* Codes_SRS_OPTIONHANDLER_01_004: [ If allocating memory fails, `OptionHandler_Clone` shall return NULL. ]*/ + LogError("unable to create option handler"); + } + else + { + /* Codes_SRS_OPTIONHANDLER_01_005: [ `OptionHandler_Clone` shall iterate through all the options stored by the option handler to be cloned by using VECTOR's iteration mechanism. ]*/ + size_t option_count = VECTOR_size(handler->storage); + size_t i; + + for (i = 0; i < option_count; i++) + { + OPTION* option = (OPTION*)VECTOR_element(handler->storage, i); + + /* Codes_SRS_OPTIONHANDLER_01_006: [ For each option the option name shall be cloned by calling `mallocAndStrcpy_s`. ]*/ + /* Codes_SRS_OPTIONHANDLER_01_007: [ For each option the value shall be cloned by using the cloning function associated with the source option handler `handler`. ]*/ + if (AddOptionInternal(result, option->name, option->storage) != OPTIONHANDLER_OK) + { + /* Codes_SRS_OPTIONHANDLER_01_008: [ If cloning one of the option names fails, `OptionHandler_Clone` shall return NULL. ]*/ + /* Codes_SRS_OPTIONHANDLER_01_009: [ If cloning one of the option values fails, `OptionHandler_Clone` shall return NULL. ]*/ + LogError("Error cloning option %s", option->name); + break; + } + } + + if (i < option_count) + { + DestroyInternal(result); + result = NULL; + } + } + } + + return result; +} + +OPTIONHANDLER_RESULT OptionHandler_AddOption(OPTIONHANDLER_HANDLE handle, const char* name, const void* value) +{ + OPTIONHANDLER_RESULT result; + /*Codes_SRS_OPTIONHANDLER_02_001: [ OptionHandler_Create shall fail and retun NULL if any parameters are NULL. ]*/ + if ( + (handle == NULL) || + (name == NULL) || + (value == NULL) + ) + { + LogError("invalid arguments: OPTIONHANDLER_HANDLE handle=%p, const char* name=%p, void* value=%p", handle, name, value); + result= OPTIONHANDLER_INVALIDARG; + } + else + { + result = AddOptionInternal(handle, name, value); + } + + return result; +} + +OPTIONHANDLER_RESULT OptionHandler_FeedOptions(OPTIONHANDLER_HANDLE handle, void* destinationHandle) +{ + OPTIONHANDLER_RESULT result; + /*Codes_SRS_OPTIONHANDLER_02_010: [ OptionHandler_FeedOptions shall fail and return OPTIONHANDLER_INVALIDARG if any argument is NULL. ]*/ + if ( + (handle == NULL) || + (destinationHandle == NULL) + ) + { + LogError("invalid arguments OPTIONHANDLER_HANDLE handle=%p, void* destinationHandle=%p", handle, destinationHandle); + result = OPTIONHANDLER_INVALIDARG; + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_011: [ Otherwise, OptionHandler_FeedOptions shall use VECTOR's iteration mechanisms to retrieve pairs of name, value (const char* and void*). ]*/ + size_t nOptions = VECTOR_size(handle->storage), i; + for (i = 0;i < nOptions;i++) + { + OPTION* option = (OPTION*)VECTOR_element(handle->storage, i); + /*Codes_SRS_OPTIONHANDLER_02_012: [ OptionHandler_FeedOptions shall call for every pair of name,value setOption passing destinationHandle, name and value. ]*/ + if (handle->setOption(destinationHandle, option->name, option->storage) != 0) + { + LogError("failure while trying to _SetOption"); + break; + } + } + + if (i == nOptions) + { + /*Codes_SRS_OPTIONHANDLER_02_014: [ Otherwise, OptionHandler_FeedOptions shall fail and return OPTIONHANDLER_ERROR. ]*/ + result = OPTIONHANDLER_OK; + } + else + { + /*Codes_SRS_OPTIONHANDLER_02_013: [ If all the operations succeed then OptionHandler_FeedOptions shall succeed and return OPTIONHANDLER_OK. ]*/ + result = OPTIONHANDLER_ERROR; + } + } + return result; +} + +void OptionHandler_Destroy(OPTIONHANDLER_HANDLE handle) +{ + /*Codes_SRS_OPTIONHANDLER_02_015: [ OptionHandler_Destroy shall do nothing if parameter handle is NULL. ]*/ + if (handle == NULL) + { + LogError("invalid argument OPTIONHANDLER_HANDLE handle=%p", handle); + } + else + { + DestroyInternal(handle); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/sastoken.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,346 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/sastoken.h" +#include "azure_c_shared_utility/urlencode.h" +#include "azure_c_shared_utility/hmacsha256.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +static double getExpiryValue(const char* expiryASCII) +{ + double value = 0; + size_t i = 0; + for (i = 0; expiryASCII[i] != '\0'; i++) + { + if (expiryASCII[i] >= '0' && expiryASCII[i] <= '9') + { + value = value * 10 + (expiryASCII[i] - '0'); + } + else + { + value = 0; + break; + } + } + return value; +} + +bool SASToken_Validate(STRING_HANDLE sasToken) +{ + bool result; + /*Codes_SRS_SASTOKEN_25_025: [**SASToken_Validate shall get the SASToken value by invoking STRING_c_str on the handle.**]***/ + const char* sasTokenArray = STRING_c_str(sasToken); + + /* Codes_SRS_SASTOKEN_25_024: [**If handle is NULL then SASToken_Validate shall return false.**] */ + /* Codes_SRS_SASTOKEN_25_026: [**If STRING_c_str on handle return NULL then SASToken_Validate shall return false.**] */ + if (sasToken == NULL || sasTokenArray == NULL) + { + result = false; + } + else + { + int seStart = -1, seStop = -1; + int srStart = -1, srStop = -1; + int sigStart = -1, sigStop = -1; + int tokenLength = (int)STRING_length(sasToken); + int i; + for (i = 0; i < tokenLength; i++) + { + if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'e' && sasTokenArray[i + 2] == '=') // Look for se= + { + seStart = i + 3; + if (srStart > 0 && srStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') // look for either & or space + srStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + srStop = i - 2; + else + seStart = -1; // as the format is not either "&se=" or " se=" + } + else if (sigStart > 0 && sigStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') + sigStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + sigStop = i - 2; + else + seStart = -1; + } + } + else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'r' && sasTokenArray[i + 2] == '=') // Look for sr= + { + srStart = i + 3; + if (seStart > 0 && seStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') + seStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + seStop = i - 2; + else + srStart = -1; + } + else if (sigStart > 0 && sigStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') + sigStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + sigStop = i - 2; + else + srStart = -1; + } + } + else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'i' && sasTokenArray[i + 2] == 'g' && sasTokenArray[i + 3] == '=') // Look for sig= + { + sigStart = i + 4; + if (srStart > 0 && srStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') + srStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + srStop = i - 2; + else + sigStart = -1; + } + else if (seStart > 0 && seStop < 0) + { + if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') + seStop = i - 1; + else if (sasTokenArray[i - 1] == '&') + seStop = i - 2; + else + sigStart = -1; + } + } + } + + /*Codes_SRS_SASTOKEN_25_027: [**If SASTOKEN does not obey the SASToken format then SASToken_Validate shall return false.**]***/ + /*Codes_SRS_SASTOKEN_25_028: [**SASToken_validate shall check for the presence of sr, se and sig from the token and return false if not found**]***/ + if (seStart < 0 || srStart < 0 || sigStart < 0) + { + result = false; + } + else + { + if (seStop < 0) + { + seStop = tokenLength; + } + else if (srStop < 0) + { + srStop = tokenLength; + } + else if (sigStop < 0) + { + sigStop = tokenLength; + } + + if ((seStop <= seStart) || + (srStop <= srStart) || + (sigStop <= sigStart)) + { + result = false; + } + else + { + char* expiryASCII = (char*)malloc(seStop - seStart + 1); + /*Codes_SRS_SASTOKEN_25_031: [**If malloc fails during validation then SASToken_Validate shall return false.**]***/ + if (expiryASCII == NULL) + { + result = false; + } + else + { + double expiry; + // Add the Null terminator here + memset(expiryASCII, 0, seStop - seStart + 1); + for (i = seStart; i < seStop; i++) + { + // The se contains the expiration values, if a & token is encountered then + // the se field is complete. + if (sasTokenArray[i] == '&') + { + break; + } + expiryASCII[i - seStart] = sasTokenArray[i]; + } + expiry = getExpiryValue(expiryASCII); + /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/ + if (expiry <= 0) + { + result = false; + } + else + { + double secSinceEpoch = get_difftime(get_time(NULL), (time_t)0); + if (expiry < secSinceEpoch) + { + /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/ + result = false; + } + else + { + /*Codes_SRS_SASTOKEN_25_030: [**SASToken_validate shall return true only if the format is obeyed and the token has not yet expired **]***/ + result = true; + } + } + free(expiryASCII); + } + } + } + } + + return result; +} + +static STRING_HANDLE construct_sas_token(const char* key, const char* scope, const char* keyname, size_t expiry) +{ + STRING_HANDLE result; + + char tokenExpirationTime[32] = { 0 }; + + BUFFER_HANDLE decodedKey; + + /*Codes_SRS_SASTOKEN_06_029: [The key parameter is decoded from base64.]*/ + if ((decodedKey = Base64_Decoder(key)) == NULL) + { + /*Codes_SRS_SASTOKEN_06_030: [If there is an error in the decoding then SASToken_Create shall return NULL.]*/ + LogError("Unable to decode the key for generating the SAS."); + result = NULL; + } + else + { + /*Codes_SRS_SASTOKEN_06_026: [If the conversion to string form fails for any reason then SASToken_Create shall return NULL.]*/ + if (size_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0) + { + LogError("For some reason converting seconds to a string failed. No SAS can be generated."); + result = NULL; + } + else + { + STRING_HANDLE toBeHashed = NULL; + BUFFER_HANDLE hash = NULL; + if (((hash = BUFFER_new()) == NULL) || + ((toBeHashed = STRING_new()) == NULL) || + ((result = STRING_new()) == NULL)) + { + LogError("Unable to allocate memory to prepare SAS token."); + result = NULL; + } + else + { + /*Codes_SRS_SASTOKEN_06_009: [The scope is the basis for creating a STRING_HANDLE.]*/ + /*Codes_SRS_SASTOKEN_06_010: [A "\n" is appended to that string.]*/ + /*Codes_SRS_SASTOKEN_06_011: [tokenExpirationTime is appended to that string.]*/ + if ((STRING_concat(toBeHashed, scope) != 0) || + (STRING_concat(toBeHashed, "\n") != 0) || + (STRING_concat(toBeHashed, tokenExpirationTime) != 0)) + { + LogError("Unable to build the input to the HMAC to prepare SAS token."); + STRING_delete(result); + result = NULL; + } + else + { + STRING_HANDLE base64Signature = NULL; + STRING_HANDLE urlEncodedSignature = NULL; + size_t inLen = STRING_length(toBeHashed); + const unsigned char* inBuf = (const unsigned char*)STRING_c_str(toBeHashed); + size_t outLen = BUFFER_length(decodedKey); + unsigned char* outBuf = BUFFER_u_char(decodedKey); + /*Codes_SRS_SASTOKEN_06_013: [If an error is returned from the HMAC256 function then NULL is returned from SASToken_Create.]*/ + /*Codes_SRS_SASTOKEN_06_012: [An HMAC256 hash is calculated using the decodedKey, over toBeHashed.]*/ + /*Codes_SRS_SASTOKEN_06_014: [If there are any errors from the following operations then NULL shall be returned.]*/ + /*Codes_SRS_SASTOKEN_06_015: [The hash is base 64 encoded.]*/ + /*Codes_SRS_SASTOKEN_06_028: [base64Signature shall be url encoded.]*/ + /*Codes_SRS_SASTOKEN_06_016: [The string "SharedAccessSignature sr=" is the first part of the result of SASToken_Create.]*/ + /*Codes_SRS_SASTOKEN_06_017: [The scope parameter is appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_018: [The string "&sig=" is appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_019: [The string urlEncodedSignature shall be appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_020: [The string "&se=" shall be appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_021: [tokenExpirationTime is appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_022: [If keyName is non-NULL, the string "&skn=" is appended to result.]*/ + /*Codes_SRS_SASTOKEN_06_023: [If keyName is non-NULL, the argument keyName is appended to result.]*/ + if ((HMACSHA256_ComputeHash(outBuf, outLen, inBuf, inLen, hash) != HMACSHA256_OK) || + ((base64Signature = Base64_Encoder(hash)) == NULL) || + ((urlEncodedSignature = URL_Encode(base64Signature)) == NULL) || + (STRING_copy(result, "SharedAccessSignature sr=") != 0) || + (STRING_concat(result, scope) != 0) || + (STRING_concat(result, "&sig=") != 0) || + (STRING_concat_with_STRING(result, urlEncodedSignature) != 0) || + (STRING_concat(result, "&se=") != 0) || + (STRING_concat(result, tokenExpirationTime) != 0) || + ((keyname != NULL) && (STRING_concat(result, "&skn=") != 0)) || + ((keyname != NULL) && (STRING_concat(result, keyname) != 0))) + { + LogError("Unable to build the SAS token."); + STRING_delete(result); + result = NULL; + } + else + { + /* everything OK */ + } + STRING_delete(base64Signature); + STRING_delete(urlEncodedSignature); + } + } + STRING_delete(toBeHashed); + BUFFER_delete(hash); + } + BUFFER_delete(decodedKey); + } + return result; +} + +STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry) +{ + STRING_HANDLE result; + + /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/ + /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/ + /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/ + if ((key == NULL) || + (scope == NULL)) + { + LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); + result = NULL; + } + else + { + const char* string_key = STRING_c_str(key); + const char* string_scope = STRING_c_str(scope); + const char* string_name = STRING_c_str(keyName); + result = construct_sas_token(string_key, string_scope, string_name, expiry); + } + return result; +} + +STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, size_t expiry) +{ + STRING_HANDLE result; + + /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/ + /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/ + /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/ + if ((key == NULL) || + (scope == NULL)) + { + LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); + result = NULL; + } + else + { + result = construct_sas_token(key, scope, keyName, expiry); + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/sha1.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,440 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/**************************** sha1.c ****************************/ +/******************** See RFC 4634 for details ******************/ +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The SHA-1 algorithm produces a 160-bit message digest for a +* given data stream. It should take about 2**n steps to find a +* message with the same digest as a given message and +* 2**(n/2) to find any two messages with the same digest, +* when n is the digest size in bits. Therefore, this +* algorithm can serve as a means of providing a +* "fingerprint" for a message. +* +* Portability Issues: +* SHA-1 is defined in terms of 32-bit "words". This code +* uses <stdint.h> (included via "sha.h") to define 32 and 8 +* bit unsigned integer types. If your C compiler does not +* support 32 bit unsigned integers, this code is not +* appropriate. +* +* Caveats: +* SHA-1 is designed to work with messages less than 2^64 bits +* long. This implementation uses SHA1Input() to hash the bits +* that are a multiple of the size of an 8-bit character, and then +* uses SHA1FinalBits() to hash the final few bits of the input. +*/ + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/sha.h" +#include "azure_c_shared_utility/sha-private.h" + +/* +* Define the SHA1 circular left shift macro +*/ +#define SHA1_ROTL(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* +* add "length" to the length +*/ +#define SHA1AddLength(context, length) \ + (addTemp = (context)->Length_Low, \ + (context)->Corrupted = \ + (((context)->Length_Low += (length)) < addTemp) && \ + (++(context)->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte); +static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte); +static void SHA1ProcessMessageBlock(SHA1Context *); + +/* +* SHA1Reset +* +* Description: +* This function will initialize the SHA1Context in preparation +* for computing a new SHA1 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +* +*/ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + return shaNull; + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + + /* Initial Hash Values: FIPS-180-2 section 5.3.1 */ + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* +* SHA1Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int SHA1Input(SHA1Context *context, + const uint8_t *message_array, unsigned length) +{ + uint32_t addTemp; + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA1AddLength(context, 8) && + (context->Message_Block_Index == SHA1_Message_Block_Size)) + SHA1ProcessMessageBlock(context); + + message_array++; + } + + return shaSuccess; +} + +/* +* SHA1FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits, + unsigned int length) +{ + uint32_t addTemp; + + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if (context->Computed || (length >= 8) || (length == 0)) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA1AddLength(context, length); + SHA1Finalize(context, + (uint8_t)((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* +* SHA1Result +* +* Description: +* This function will return the 160-bit message digest into the +* Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 19th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA-1 hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int SHA1Result(SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA1Finalize(context, 0x80); + + for (i = 0; i < SHA1HashSize; ++i) + Message_Digest[i] = (uint8_t)(context->Intermediate_Hash[i >> 2] + >> 8 * (3 - (i & 0x03))); + + return shaSuccess; +} + +/* +* SHA1Finalize +* +* Description: +* This helper function finishes off the digest calculations. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* sha Error Code. +* +*/ +static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte) +{ + int i; + SHA1PadMessage(context, Pad_Byte); + /* message may be sensitive, clear it out */ + for (i = 0; i < SHA1_Message_Block_Size; ++i) + context->Message_Block[i] = 0; + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; +} + +/* +* SHA1PadMessage +* +* Description: +* According to the standard, the message must be padded to an +* even 512 bits. The first padding bit must be a '1'. The last +* 64 bits represent the length of the original message. All bits +* in between should be 0. This helper function will pad the +* message according to those rules by filling the Message_Block +* array accordingly. When it returns, it can be assumed that the +* message digest has been computed. +* +* Parameters: +* context: [in/out] +* The context to pad +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* Nothing. +*/ +static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA1_Message_Block_Size - 8)) { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA1_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + + SHA1ProcessMessageBlock(context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA1_Message_Block_Size - 8)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[57] = (uint8_t)(context->Length_High >> 16); + + context->Message_Block[58] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[59] = (uint8_t)(context->Length_High); + context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[63] = (uint8_t)(context->Length_Low); + + SHA1ProcessMessageBlock(context); +} + +/* +* SHA1ProcessMessageBlock +* +* Description: +* This helper function will process the next 512 bits of the +* message stored in the Message_Block array. +* +* Parameters: +* None. +* +* Returns: +* Nothing. +* +* Comments: +* Many of the variable names in this code, especially the +* single character names, were used because those were the +* names used in the publication. +*/ +static void SHA1ProcessMessageBlock(SHA1Context *context) +{ + /* Constants defined in FIPS-180-2, section 4.2.1 */ + const uint32_t K[4] = { + 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) { + W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24; + W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]); + } + + for (t = 16; t < 80; t++) + W[t] = SHA1_ROTL(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) { + temp = SHA1_ROTL(5, A) + SHA_Ch(B, C, D) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1_ROTL(30, B); + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) { + temp = SHA1_ROTL(5, A) + SHA_Parity(B, C, D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1_ROTL(30, B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) { + temp = SHA1_ROTL(5, A) + SHA_Maj(B, C, D) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1_ROTL(30, B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) { + temp = SHA1_ROTL(5, A) + SHA_Parity(B, C, D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1_ROTL(30, B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/sha224.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,600 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*************************** sha224-256.c ***************************/ +/********************* See RFC 4634 for details *********************/ +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit +* message digests for a given data stream. It should take about +* 2**n steps to find a message with the same digest as a given +* message and 2**(n/2) to find any two messages with the same +* digest, when n is the digest size in bits. Therefore, this +* algorithm can serve as a means of providing a +* "fingerprint" for a message. +* +* Portability Issues: +* SHA-224 and SHA-256 are defined in terms of 32-bit "words". +* This code uses <stdint.h> (included via "sha.h") to define 32 +* and 8 bit unsigned integer types. If your C compiler does not +* support 32 bit unsigned integers, this code is not +* appropriate. +* +* Caveats: +* SHA-224 and SHA-256 are designed to work with messages less +* than 2^64 bits long. This implementation uses SHA224/256Input() +* to hash the bits that are a multiple of the size of an 8-bit +* character, and then uses SHA224/256FinalBits() to hash the +* final few bits of the input. +*/ + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/sha.h" +#include "azure_c_shared_utility/sha-private.h" +/* Define the SHA shift, rotate left and rotate right macro */ +#define SHA256_SHR(bits,word) ((word) >> (bits)) +#define SHA256_ROTL(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) +#define SHA256_ROTR(bits,word) \ + (((word) >> (bits)) | ((word) << (32-(bits)))) + +/* Define the SHA SIGMA and sigma macros */ +#define SHA256_SIGMA0(word) \ + (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word)) +#define SHA256_SIGMA1(word) \ + (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word)) +#define SHA256_sigma0(word) \ + (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word)) +#define SHA256_sigma1(word) \ + (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word)) + +/* +* add "length" to the length +*/ +#define SHA224_256AddLength(context, length) \ + (addTemp = (context)->Length_Low, (context)->Corrupted = \ + (((context)->Length_Low += (length)) < addTemp) && \ + (++(context)->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA224_256Finalize(SHA256Context *context, + uint8_t Pad_Byte); +static void SHA224_256PadMessage(SHA256Context *context, + uint8_t Pad_Byte); +static void SHA224_256ProcessMessageBlock(SHA256Context *context); +static int SHA224_256Reset(SHA256Context *context, uint32_t *H0); +static int SHA224_256ResultN(SHA256Context *context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 Change Notice 1 */ +static uint32_t SHA224_H0[SHA256HashSize / 4] = { + 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, + 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 +}; + +/* Initial Hash Values: FIPS-180-2 section 5.3.2 */ +static uint32_t SHA256_H0[SHA256HashSize / 4] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/* +* SHA224Reset +* +* Description: +* This function will initialize the SHA384Context in preparation +* for computing a new SHA224 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +*/ +int SHA224Reset(SHA224Context *context) +{ + return SHA224_256Reset(context, SHA224_H0); +} + +/* +* SHA224Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int SHA224Input(SHA224Context *context, const uint8_t *message_array, + unsigned int length) +{ + return SHA256Input(context, message_array, length); +} + +/* +* SHA224FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int SHA224FinalBits(SHA224Context *context, + const uint8_t message_bits, unsigned int length) +{ + return SHA256FinalBits(context, message_bits, length); +} + +/* +* SHA224Result +* +* Description: +* This function will return the 224-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 28th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +*/ +int SHA224Result(SHA224Context *context, + uint8_t Message_Digest[SHA224HashSize]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA224HashSize); +} + +/* +* SHA256Reset +* +* Description: +* This function will initialize the SHA256Context in preparation +* for computing a new SHA256 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +*/ +int SHA256Reset(SHA256Context *context) +{ + return SHA224_256Reset(context, SHA256_H0); +} + +/* +* SHA256Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +*/ +int SHA256Input(SHA256Context *context, const uint8_t *message_array, + unsigned int length) +{ + uint32_t addTemp; + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA224_256AddLength(context, 8) && + (context->Message_Block_Index == SHA256_Message_Block_Size)) + SHA224_256ProcessMessageBlock(context); + + message_array++; + } + + return shaSuccess; + +} + +/* +* SHA256FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int SHA256FinalBits(SHA256Context *context, + const uint8_t message_bits, unsigned int length) +{ + uint32_t addTemp; + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if ((context->Computed) || (length >= 8) || (length == 0)) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA224_256AddLength(context, length); + SHA224_256Finalize(context, (uint8_t) + ((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* +* SHA256Result +* +* Description: +* This function will return the 256-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 32nd element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +*/ +int SHA256Result(SHA256Context *context, uint8_t Message_Digest[]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA256HashSize); +} + +/* +* SHA224_256Finalize +* +* Description: +* This helper function finishes off the digest calculations. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* sha Error Code. +*/ +static void SHA224_256Finalize(SHA256Context *context, + uint8_t Pad_Byte) +{ + int i; + SHA224_256PadMessage(context, Pad_Byte); + /* message may be sensitive, so clear it out */ + for (i = 0; i < SHA256_Message_Block_Size; ++i) + context->Message_Block[i] = 0; + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; +} + +/* +* SHA224_256PadMessage +* +* Description: +* According to the standard, the message must be padded to an +* even 512 bits. The first padding bit must be a '1'. The +* last 64 bits represent the length of the original message. +* All bits in between should be 0. This helper function will pad +* the message according to those rules by filling the +* Message_Block array accordingly. When it returns, it can be +* assumed that the message digest has been computed. +* +* Parameters: +* context: [in/out] +* The context to pad +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* Nothing. +*/ +static void SHA224_256PadMessage(SHA256Context *context, + uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA256_Message_Block_Size - 8)) { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA256_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + SHA224_256ProcessMessageBlock(context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA256_Message_Block_Size - 8)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[57] = (uint8_t)(context->Length_High >> 16); + context->Message_Block[58] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[59] = (uint8_t)(context->Length_High); + context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[63] = (uint8_t)(context->Length_Low); + + SHA224_256ProcessMessageBlock(context); +} + +/* +* SHA224_256ProcessMessageBlock +* +* Description: +* This function will process the next 512 bits of the message +* stored in the Message_Block array. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* +* Returns: +* Nothing. +* +* Comments: +* Many of the variable names in this code, especially the +* single character names, were used because those were the +* names used in the publication. +*/ +static void SHA224_256ProcessMessageBlock(SHA256Context *context) +{ + /* Constants defined in FIPS-180-2, section 4.2.2 */ + static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, + 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, + 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, + 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, + 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + int t, t4; /* Loop counter */ + uint32_t temp1, temp2; /* Temporary word value */ + uint32_t W[64]; /* Word sequence */ + uint32_t A, B, C, D, E, F, G, H; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = t4 = 0; t < 16; t++, t4 += 4) + W[t] = (((uint32_t)context->Message_Block[t4]) << 24) | + (((uint32_t)context->Message_Block[t4 + 1]) << 16) | + (((uint32_t)context->Message_Block[t4 + 2]) << 8) | + (((uint32_t)context->Message_Block[t4 + 3])); + + for (t = 16; t < 64; t++) + W[t] = SHA256_sigma1(W[t - 2]) + W[t - 7] + + SHA256_sigma0(W[t - 15]) + W[t - 16]; + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + F = context->Intermediate_Hash[5]; + G = context->Intermediate_Hash[6]; + H = context->Intermediate_Hash[7]; + + for (t = 0; t < 64; t++) { + temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t]; + temp2 = SHA256_SIGMA0(A) + SHA_Maj(A, B, C); + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + context->Intermediate_Hash[5] += F; + context->Intermediate_Hash[6] += G; + context->Intermediate_Hash[7] += H; + + context->Message_Block_Index = 0; +} + +/* +* SHA224_256Reset +* +* Description: +* This helper function will initialize the SHA256Context in +* preparation for computing a new SHA256 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* H0 +* The initial hash value to use. +* +* Returns: +* sha Error Code. +*/ +static int SHA224_256Reset(SHA256Context *context, uint32_t *H0) +{ + if (!context) + return shaNull; + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = H0[0]; + context->Intermediate_Hash[1] = H0[1]; + context->Intermediate_Hash[2] = H0[2]; + context->Intermediate_Hash[3] = H0[3]; + context->Intermediate_Hash[4] = H0[4]; + context->Intermediate_Hash[5] = H0[5]; + context->Intermediate_Hash[6] = H0[6]; + context->Intermediate_Hash[7] = H0[7]; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* +* SHA224_256ResultN +* +* Description: +* This helper function will return the 224-bit or 256-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 28th/32nd element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* HashSize: [in] +* The size of the hash, either 28 or 32. +* +* Returns: +* sha Error Code. +*/ +static int SHA224_256ResultN(SHA256Context *context, + uint8_t Message_Digest[], int HashSize) +{ + int i; + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA224_256Finalize(context, 0x80); + + for (i = 0; i < HashSize; ++i) + Message_Digest[i] = (uint8_t) + (context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03))); + + return shaSuccess; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/sha384-512.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1046 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*************************** sha384-512.c ***************************/ +/********************* See RFC 4634 for details *********************/ +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The SHA-384 and SHA-512 algorithms produce 384-bit and 512-bit +* message digests for a given data stream. It should take about +* 2**n steps to find a message with the same digest as a given +* message and 2**(n/2) to find any two messages with the same +* digest, when n is the digest size in bits. Therefore, this +* algorithm can serve as a means of providing a +* "fingerprint" for a message. +* +* Portability Issues: +* SHA-384 and SHA-512 are defined in terms of 64-bit "words", +* but if USE_32BIT_ONLY is #defined, this code is implemented in +* terms of 32-bit "words". This code uses <stdint.h> (included +* via "sha.h") to define the 64, 32 and 8 bit unsigned integer +* types. If your C compiler does not support 64 bit unsigned +* integers, and you do not #define USE_32BIT_ONLY, this code is +* not appropriate. +* +* Caveats: +* SHA-384 and SHA-512 are designed to work with messages less +* than 2^128 bits long. This implementation uses +* SHA384/512Input() to hash the bits that are a multiple of the +* size of an 8-bit character, and then uses SHA384/256FinalBits() +* to hash the final few bits of the input. +* +*/ + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/sha.h" +#include "azure_c_shared_utility/sha-private.h" + +#ifdef USE_32BIT_ONLY +#error IoTHubClient does not support USE_32BIT_ONLY flag +/* +* Define 64-bit arithmetic in terms of 32-bit arithmetic. +* Each 64-bit number is represented in a 2-word array. +* All macros are defined such that the result is the last parameter. +*/ + +/* +* Define shift, rotate left and rotate right functions +*/ +#define SHA512_SHR(bits, word, ret) ( \ + /* (((uint64_t)((word))) >> (bits)) */ \ + (ret)[0] = (((bits) < 32) && ((bits) >= 0)) ? \ + ((word)[0] >> (bits)) : 0, \ + (ret)[1] = ((bits) > 32) ? ((word)[0] >> ((bits) - 32)) : \ + ((bits) == 32) ? (word)[0] : \ + ((bits) >= 0) ? \ + (((word)[0] << (32 - (bits))) | \ + ((word)[1] >> (bits))) : 0 ) + +#define SHA512_SHL(bits, word, ret) ( \ + /* (((uint64_t)(word)) << (bits)) */ \ + (ret)[0] = ((bits) > 32) ? ((word)[1] << ((bits) - 32)) : \ + ((bits) == 32) ? (word)[1] : \ + ((bits) >= 0) ? \ + (((word)[0] << (bits)) | \ + ((word)[1] >> (32 - (bits)))) : \ +0, \ +(ret)[1] = (((bits) < 32) && ((bits) >= 0)) ? \ +((word)[1] << (bits)) : 0) + +/* +* Define 64-bit OR +*/ +#define SHA512_OR(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] | (word2)[0], \ + (ret)[1] = (word1)[1] | (word2)[1] ) + +/* +* Define 64-bit XOR +*/ +#define SHA512_XOR(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] ^ (word2)[0], \ + (ret)[1] = (word1)[1] ^ (word2)[1] ) + +/* +* Define 64-bit AND +*/ +#define SHA512_AND(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] & (word2)[0], \ + (ret)[1] = (word1)[1] & (word2)[1] ) + +/* +* Define 64-bit TILDA +*/ +#define SHA512_TILDA(word, ret) \ + ( (ret)[0] = ~(word)[0], (ret)[1] = ~(word)[1] ) + +/* +* Define 64-bit ADD +*/ +#define SHA512_ADD(word1, word2, ret) ( \ + (ret)[1] = (word1)[1], (ret)[1] += (word2)[1], \ + (ret)[0] = (word1)[0] + (word2)[0] + ((ret)[1] < (word1)[1]) ) + +/* +* Add the 4word value in word2 to word1. +*/ +static uint32_t ADDTO4_temp, ADDTO4_temp2; +#define SHA512_ADDTO4(word1, word2) ( \ + ADDTO4_temp = (word1)[3], \ + (word1)[3] += (word2)[3], \ + ADDTO4_temp2 = (word1)[2], \ + (word1)[2] += (word2)[2] + ((word1)[3] < ADDTO4_temp), \ + ADDTO4_temp = (word1)[1], \ +(word1)[1] += (word2)[1] + ((word1)[2] < ADDTO4_temp2), \ +(word1)[0] += (word2)[0] + ((word1)[1] < ADDTO4_temp)) + +/* +* Add the 2word value in word2 to word1. +*/ +static uint32_t ADDTO2_temp; +#define SHA512_ADDTO2(word1, word2) ( \ + ADDTO2_temp = (word1)[1], \ + (word1)[1] += (word2)[1], \ + (word1)[0] += (word2)[0] + ((word1)[1] < ADDTO2_temp) ) + +/* +* SHA rotate ((word >> bits) | (word << (64-bits))) +*/ +static uint32_t ROTR_temp1[2], ROTR_temp2[2]; +#define SHA512_ROTR(bits, word, ret) ( \ + SHA512_SHR((bits), (word), ROTR_temp1), \ + SHA512_SHL(64-(bits), (word), ROTR_temp2), \ + SHA512_OR(ROTR_temp1, ROTR_temp2, (ret)) ) + +/* +* Define the SHA SIGMA and sigma macros +* SHA512_ROTR(28,word) ^ SHA512_ROTR(34,word) ^ SHA512_ROTR(39,word) +*/ +static uint32_t SIGMA0_temp1[2], SIGMA0_temp2[2], +SIGMA0_temp3[2], SIGMA0_temp4[2]; +#define SHA512_SIGMA0(word, ret) ( \ + SHA512_ROTR(28, (word), SIGMA0_temp1), \ + SHA512_ROTR(34, (word), SIGMA0_temp2), \ + SHA512_ROTR(39, (word), SIGMA0_temp3), \ + SHA512_XOR(SIGMA0_temp2, SIGMA0_temp3, SIGMA0_temp4), \ + SHA512_XOR(SIGMA0_temp1, SIGMA0_temp4, (ret)) ) + +/* +* SHA512_ROTR(14,word) ^ SHA512_ROTR(18,word) ^ SHA512_ROTR(41,word) +*/ +static uint32_t SIGMA1_temp1[2], SIGMA1_temp2[2], +SIGMA1_temp3[2], SIGMA1_temp4[2]; +#define SHA512_SIGMA1(word, ret) ( \ + SHA512_ROTR(14, (word), SIGMA1_temp1), \ + SHA512_ROTR(18, (word), SIGMA1_temp2), \ + SHA512_ROTR(41, (word), SIGMA1_temp3), \ + SHA512_XOR(SIGMA1_temp2, SIGMA1_temp3, SIGMA1_temp4), \ + SHA512_XOR(SIGMA1_temp1, SIGMA1_temp4, (ret)) ) + +/* +* (SHA512_ROTR( 1,word) ^ SHA512_ROTR( 8,word) ^ SHA512_SHR( 7,word)) +*/ +static uint32_t sigma0_temp1[2], sigma0_temp2[2], +sigma0_temp3[2], sigma0_temp4[2]; +#define SHA512_sigma0(word, ret) ( \ + SHA512_ROTR( 1, (word), sigma0_temp1), \ + SHA512_ROTR( 8, (word), sigma0_temp2), \ + SHA512_SHR( 7, (word), sigma0_temp3), \ + SHA512_XOR(sigma0_temp2, sigma0_temp3, sigma0_temp4), \ + SHA512_XOR(sigma0_temp1, sigma0_temp4, (ret)) ) + +/* +* (SHA512_ROTR(19,word) ^ SHA512_ROTR(61,word) ^ SHA512_SHR( 6,word)) +*/ +static uint32_t sigma1_temp1[2], sigma1_temp2[2], +sigma1_temp3[2], sigma1_temp4[2]; +#define SHA512_sigma1(word, ret) ( \ + SHA512_ROTR(19, (word), sigma1_temp1), \ + SHA512_ROTR(61, (word), sigma1_temp2), \ + SHA512_SHR( 6, (word), sigma1_temp3), \ + SHA512_XOR(sigma1_temp2, sigma1_temp3, sigma1_temp4), \ + SHA512_XOR(sigma1_temp1, sigma1_temp4, (ret)) ) + +#undef SHA_Ch +#undef SHA_Maj + +#ifndef USE_MODIFIED_MACROS +/* +* These definitions are the ones used in FIPS-180-2, section 4.1.3 +* Ch(x,y,z) ((x & y) ^ (~x & z)) +*/ +static uint32_t Ch_temp1[2], Ch_temp2[2], Ch_temp3[2]; +#define SHA_Ch(x, y, z, ret) ( \ + SHA512_AND(x, y, Ch_temp1), \ + SHA512_TILDA(x, Ch_temp2), \ + SHA512_AND(Ch_temp2, z, Ch_temp3), \ + SHA512_XOR(Ch_temp1, Ch_temp3, (ret)) ) +/* +* Maj(x,y,z) (((x)&(y)) ^ ((x)&(z)) ^ ((y)&(z))) +*/ +static uint32_t Maj_temp1[2], Maj_temp2[2], +Maj_temp3[2], Maj_temp4[2]; +#define SHA_Maj(x, y, z, ret) ( \ + SHA512_AND(x, y, Maj_temp1), \ + SHA512_AND(x, z, Maj_temp2), \ + SHA512_AND(y, z, Maj_temp3), \ + SHA512_XOR(Maj_temp2, Maj_temp3, Maj_temp4), \ + SHA512_XOR(Maj_temp1, Maj_temp4, (ret)) ) + +#else /* !USE_32BIT_ONLY */ +/* +* These definitions are potentially faster equivalents for the ones +* used in FIPS-180-2, section 4.1.3. +* ((x & y) ^ (~x & z)) becomes +* ((x & (y ^ z)) ^ z) +*/ +#define SHA_Ch(x, y, z, ret) ( \ + (ret)[0] = (((x)[0] & ((y)[0] ^ (z)[0])) ^ (z)[0]), \ + (ret)[1] = (((x)[1] & ((y)[1] ^ (z)[1])) ^ (z)[1]) ) + +/* +* ((x & y) ^ (x & z) ^ (y & z)) becomes +* ((x & (y | z)) | (y & z)) +*/ +#define SHA_Maj(x, y, z, ret) ( \ + ret[0] = (((x)[0] & ((y)[0] | (z)[0])) | ((y)[0] & (z)[0])), \ + ret[1] = (((x)[1] & ((y)[1] | (z)[1])) | ((y)[1] & (z)[1])) ) +#endif /* USE_MODIFIED_MACROS */ + +/* +* add "length" to the length +*/ +static uint32_t addTemp[4] = { 0, 0, 0, 0 }; +#define SHA384_512AddLength(context, length) ( \ + addTemp[3] = (length), SHA512_ADDTO4((context)->Length, addTemp), \ + (context)->Corrupted = (((context)->Length[3] == 0) && \ + ((context)->Length[2] == 0) && ((context)->Length[1] == 0) && \ + ((context)->Length[0] < 8)) ? 1 : 0 ) + +/* Local Function Prototypes */ +static void SHA384_512Finalize(SHA512Context *context, + uint8_t Pad_Byte); +static void SHA384_512PadMessage(SHA512Context *context, + uint8_t Pad_Byte); +static void SHA384_512ProcessMessageBlock(SHA512Context *context); +static int SHA384_512Reset(SHA512Context *context, uint32_t H0[]); +static int SHA384_512ResultN(SHA512Context *context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 sections 5.3.3 and 5.3.4 */ +static uint32_t SHA384_H0[SHA512HashSize / 4] = { + 0xCBBB9D5D, 0xC1059ED8, 0x629A292A, 0x367CD507, 0x9159015A, + 0x3070DD17, 0x152FECD8, 0xF70E5939, 0x67332667, 0xFFC00B31, + 0x8EB44A87, 0x68581511, 0xDB0C2E0D, 0x64F98FA7, 0x47B5481D, + 0xBEFA4FA4 +}; + +static uint32_t SHA512_H0[SHA512HashSize / 4] = { + 0x6A09E667, 0xF3BCC908, 0xBB67AE85, 0x84CAA73B, 0x3C6EF372, + 0xFE94F82B, 0xA54FF53A, 0x5F1D36F1, 0x510E527F, 0xADE682D1, + 0x9B05688C, 0x2B3E6C1F, 0x1F83D9AB, 0xFB41BD6B, 0x5BE0CD19, + 0x137E2179 +}; + +#else /* !USE_32BIT_ONLY */ + +/* Define the SHA shift, rotate left and rotate right macro */ +#define SHA512_SHR(bits,word) (((uint64_t)(word)) >> (bits)) +#define SHA512_ROTR(bits,word) ((((uint64_t)(word)) >> (bits)) | \ + (((uint64_t)(word)) << (64-(bits)))) + +/* Define the SHA SIGMA and sigma macros */ +#define SHA512_SIGMA0(word) \ + (SHA512_ROTR(28,word) ^ SHA512_ROTR(34,word) ^ SHA512_ROTR(39,word)) +#define SHA512_SIGMA1(word) \ + (SHA512_ROTR(14,word) ^ SHA512_ROTR(18,word) ^ SHA512_ROTR(41,word)) +#define SHA512_sigma0(word) \ + (SHA512_ROTR( 1,word) ^ SHA512_ROTR( 8,word) ^ SHA512_SHR( 7,word)) +#define SHA512_sigma1(word) \ + (SHA512_ROTR(19,word) ^ SHA512_ROTR(61,word) ^ SHA512_SHR( 6,word)) + +/* +* add "length" to the length +*/ +#define SHA384_512AddLength(context, length) \ + (addTemp = context->Length_Low, context->Corrupted = \ + ((context->Length_Low += length) < addTemp) && \ + (++context->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA384_512Finalize(SHA512Context *context, + uint8_t Pad_Byte); +static void SHA384_512PadMessage(SHA512Context *context, + uint8_t Pad_Byte); +static void SHA384_512ProcessMessageBlock(SHA512Context *context); +static int SHA384_512Reset(SHA512Context *context, uint64_t H0[]); +static int SHA384_512ResultN(SHA512Context *context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 sections 5.3.3 and 5.3.4 */ +static uint64_t SHA384_H0[] = { + 0xCBBB9D5DC1059ED8ull, 0x629A292A367CD507ull, 0x9159015A3070DD17ull, + 0x152FECD8F70E5939ull, 0x67332667FFC00B31ull, 0x8EB44A8768581511ull, + 0xDB0C2E0D64F98FA7ull, 0x47B5481DBEFA4FA4ull +}; +static uint64_t SHA512_H0[] = { + 0x6A09E667F3BCC908ull, 0xBB67AE8584CAA73Bull, 0x3C6EF372FE94F82Bull, + 0xA54FF53A5F1D36F1ull, 0x510E527FADE682D1ull, 0x9B05688C2B3E6C1Full, + 0x1F83D9ABFB41BD6Bull, 0x5BE0CD19137E2179ull +}; + +#endif /* USE_32BIT_ONLY */ + +/* +* SHA384Reset +* +* Description: +* This function will initialize the SHA384Context in preparation +* for computing a new SHA384 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +* +*/ +int SHA384Reset(SHA384Context *context) +{ + return SHA384_512Reset(context, SHA384_H0); +} + +/* +* SHA384Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int SHA384Input(SHA384Context *context, + const uint8_t *message_array, unsigned int length) +{ + return SHA512Input(context, message_array, length); +} + +/* +* SHA384FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +* +*/ +int SHA384FinalBits(SHA384Context *context, + const uint8_t message_bits, unsigned int length) +{ + return SHA512FinalBits(context, message_bits, length); +} + +/* +* SHA384Result +* +* Description: +* This function will return the 384-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 48th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int SHA384Result(SHA384Context *context, + uint8_t Message_Digest[SHA384HashSize]) +{ + return SHA384_512ResultN(context, Message_Digest, SHA384HashSize); +} + +/* +* SHA512Reset +* +* Description: +* This function will initialize the SHA512Context in preparation +* for computing a new SHA512 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +* +*/ +int SHA512Reset(SHA512Context *context) +{ + return SHA384_512Reset(context, SHA512_H0); +} + +/* +* SHA512Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int SHA512Input(SHA512Context *context, + const uint8_t *message_array, + unsigned int length) +{ + uint64_t addTemp; + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA384_512AddLength(context, 8) && + (context->Message_Block_Index == SHA512_Message_Block_Size)) + SHA384_512ProcessMessageBlock(context); + + message_array++; + } + + return shaSuccess; +} + +/* +* SHA512FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +* +*/ +int SHA512FinalBits(SHA512Context *context, + const uint8_t message_bits, unsigned int length) +{ + uint64_t addTemp; + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if ((context->Computed) || (length >= 8) || (length == 0)) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA384_512AddLength(context, length); + SHA384_512Finalize(context, (uint8_t) + ((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* +* SHA384_512Finalize +* +* Description: +* This helper function finishes off the digest calculations. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* sha Error Code. +* +*/ +static void SHA384_512Finalize(SHA512Context *context, + uint8_t Pad_Byte) +{ + int_least16_t i; + SHA384_512PadMessage(context, Pad_Byte); + /* message may be sensitive, clear it out */ + for (i = 0; i < SHA512_Message_Block_Size; ++i) + context->Message_Block[i] = 0; +#ifdef USE_32BIT_ONLY /* and clear length */ + context->Length[0] = context->Length[1] = 0; + context->Length[2] = context->Length[3] = 0; +#else /* !USE_32BIT_ONLY */ + context->Length_Low = 0; + context->Length_High = 0; +#endif /* USE_32BIT_ONLY */ + context->Computed = 1; +} + +/* +* SHA512Result +* +* Description: +* This function will return the 512-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 64th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int SHA512Result(SHA512Context *context, + uint8_t Message_Digest[SHA512HashSize]) +{ + return SHA384_512ResultN(context, Message_Digest, SHA512HashSize); +} + +/* +* SHA384_512PadMessage +* +* Description: +* According to the standard, the message must be padded to an +* even 1024 bits. The first padding bit must be a '1'. The +* last 128 bits represent the length of the original message. +* All bits in between should be 0. This helper function will +* pad the message according to those rules by filling the +* Message_Block array accordingly. When it returns, it can be +* assumed that the message digest has been computed. +* +* Parameters: +* context: [in/out] +* The context to pad +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* Nothing. +* +*/ +static void SHA384_512PadMessage(SHA512Context *context, + uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA512_Message_Block_Size - 16)) { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA512_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + + SHA384_512ProcessMessageBlock(context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA512_Message_Block_Size - 16)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 16 octets + */ +#ifdef USE_32BIT_ONLY + context->Message_Block[112] = (uint8_t)(context->Length[0] >> 24); + context->Message_Block[113] = (uint8_t)(context->Length[0] >> 16); + context->Message_Block[114] = (uint8_t)(context->Length[0] >> 8); + context->Message_Block[115] = (uint8_t)(context->Length[0]); + context->Message_Block[116] = (uint8_t)(context->Length[1] >> 24); + context->Message_Block[117] = (uint8_t)(context->Length[1] >> 16); + context->Message_Block[118] = (uint8_t)(context->Length[1] >> 8); + context->Message_Block[119] = (uint8_t)(context->Length[1]); + + context->Message_Block[120] = (uint8_t)(context->Length[2] >> 24); + context->Message_Block[121] = (uint8_t)(context->Length[2] >> 16); + context->Message_Block[122] = (uint8_t)(context->Length[2] >> 8); + context->Message_Block[123] = (uint8_t)(context->Length[2]); + context->Message_Block[124] = (uint8_t)(context->Length[3] >> 24); + context->Message_Block[125] = (uint8_t)(context->Length[3] >> 16); + context->Message_Block[126] = (uint8_t)(context->Length[3] >> 8); + context->Message_Block[127] = (uint8_t)(context->Length[3]); +#else /* !USE_32BIT_ONLY */ + context->Message_Block[112] = (uint8_t)(context->Length_High >> 56); + context->Message_Block[113] = (uint8_t)(context->Length_High >> 48); + context->Message_Block[114] = (uint8_t)(context->Length_High >> 40); + context->Message_Block[115] = (uint8_t)(context->Length_High >> 32); + context->Message_Block[116] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[117] = (uint8_t)(context->Length_High >> 16); + context->Message_Block[118] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[119] = (uint8_t)(context->Length_High); + + context->Message_Block[120] = (uint8_t)(context->Length_Low >> 56); + context->Message_Block[121] = (uint8_t)(context->Length_Low >> 48); + context->Message_Block[122] = (uint8_t)(context->Length_Low >> 40); + context->Message_Block[123] = (uint8_t)(context->Length_Low >> 32); + context->Message_Block[124] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[125] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[126] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[127] = (uint8_t)(context->Length_Low); +#endif /* USE_32BIT_ONLY */ + + SHA384_512ProcessMessageBlock(context); +} + +/* +* SHA384_512ProcessMessageBlock +* +* Description: +* This helper function will process the next 1024 bits of the +* message stored in the Message_Block array. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* +* Returns: +* Nothing. +* +* Comments: +* Many of the variable names in this code, especially the +* single character names, were used because those were the +* names used in the publication. +* +* +*/ +static void SHA384_512ProcessMessageBlock(SHA512Context *context) +{ + /* Constants defined in FIPS-180-2, section 4.2.3 */ +#ifdef USE_32BIT_ONLY + static const uint32_t K[80 * 2] = { + 0x428A2F98, 0xD728AE22, 0x71374491, 0x23EF65CD, 0xB5C0FBCF, + 0xEC4D3B2F, 0xE9B5DBA5, 0x8189DBBC, 0x3956C25B, 0xF348B538, + 0x59F111F1, 0xB605D019, 0x923F82A4, 0xAF194F9B, 0xAB1C5ED5, + 0xDA6D8118, 0xD807AA98, 0xA3030242, 0x12835B01, 0x45706FBE, + 0x243185BE, 0x4EE4B28C, 0x550C7DC3, 0xD5FFB4E2, 0x72BE5D74, + 0xF27B896F, 0x80DEB1FE, 0x3B1696B1, 0x9BDC06A7, 0x25C71235, + 0xC19BF174, 0xCF692694, 0xE49B69C1, 0x9EF14AD2, 0xEFBE4786, + 0x384F25E3, 0x0FC19DC6, 0x8B8CD5B5, 0x240CA1CC, 0x77AC9C65, + 0x2DE92C6F, 0x592B0275, 0x4A7484AA, 0x6EA6E483, 0x5CB0A9DC, + 0xBD41FBD4, 0x76F988DA, 0x831153B5, 0x983E5152, 0xEE66DFAB, + 0xA831C66D, 0x2DB43210, 0xB00327C8, 0x98FB213F, 0xBF597FC7, + 0xBEEF0EE4, 0xC6E00BF3, 0x3DA88FC2, 0xD5A79147, 0x930AA725, + 0x06CA6351, 0xE003826F, 0x14292967, 0x0A0E6E70, 0x27B70A85, + 0x46D22FFC, 0x2E1B2138, 0x5C26C926, 0x4D2C6DFC, 0x5AC42AED, + 0x53380D13, 0x9D95B3DF, 0x650A7354, 0x8BAF63DE, 0x766A0ABB, + 0x3C77B2A8, 0x81C2C92E, 0x47EDAEE6, 0x92722C85, 0x1482353B, + 0xA2BFE8A1, 0x4CF10364, 0xA81A664B, 0xBC423001, 0xC24B8B70, + 0xD0F89791, 0xC76C51A3, 0x0654BE30, 0xD192E819, 0xD6EF5218, + 0xD6990624, 0x5565A910, 0xF40E3585, 0x5771202A, 0x106AA070, + 0x32BBD1B8, 0x19A4C116, 0xB8D2D0C8, 0x1E376C08, 0x5141AB53, + 0x2748774C, 0xDF8EEB99, 0x34B0BCB5, 0xE19B48A8, 0x391C0CB3, + 0xC5C95A63, 0x4ED8AA4A, 0xE3418ACB, 0x5B9CCA4F, 0x7763E373, + 0x682E6FF3, 0xD6B2B8A3, 0x748F82EE, 0x5DEFB2FC, 0x78A5636F, + 0x43172F60, 0x84C87814, 0xA1F0AB72, 0x8CC70208, 0x1A6439EC, + 0x90BEFFFA, 0x23631E28, 0xA4506CEB, 0xDE82BDE9, 0xBEF9A3F7, + 0xB2C67915, 0xC67178F2, 0xE372532B, 0xCA273ECE, 0xEA26619C, + 0xD186B8C7, 0x21C0C207, 0xEADA7DD6, 0xCDE0EB1E, 0xF57D4F7F, + 0xEE6ED178, 0x06F067AA, 0x72176FBA, 0x0A637DC5, 0xA2C898A6, + 0x113F9804, 0xBEF90DAE, 0x1B710B35, 0x131C471B, 0x28DB77F5, + 0x23047D84, 0x32CAAB7B, 0x40C72493, 0x3C9EBE0A, 0x15C9BEBC, + 0x431D67C4, 0x9C100D4C, 0x4CC5D4BE, 0xCB3E42B6, 0x597F299C, + 0xFC657E2A, 0x5FCB6FAB, 0x3AD6FAEC, 0x6C44198C, 0x4A475817 + }; + int t, t2, t8; /* Loop counter */ + uint32_t temp1[2], temp2[2], /* Temporary word values */ + temp3[2], temp4[2], temp5[2]; + uint32_t W[2 * 80]; /* Word sequence */ + uint32_t A[2], B[2], C[2], D[2], /* Word buffers */ + E[2], F[2], G[2], H[2]; + + /* Initialize the first 16 words in the array W */ + for (t = t2 = t8 = 0; t < 16; t++, t8 += 8) { + W[t2++] = ((((uint32_t)context->Message_Block[t8])) << 24) | + ((((uint32_t)context->Message_Block[t8 + 1])) << 16) | + ((((uint32_t)context->Message_Block[t8 + 2])) << 8) | + ((((uint32_t)context->Message_Block[t8 + 3]))); + W[t2++] = ((((uint32_t)context->Message_Block[t8 + 4])) << 24) | + ((((uint32_t)context->Message_Block[t8 + 5])) << 16) | + ((((uint32_t)context->Message_Block[t8 + 6])) << 8) | + ((((uint32_t)context->Message_Block[t8 + 7]))); + } + + for (t = 16; t < 80; t++, t2 += 2) { + /* W[t] = SHA512_sigma1(W[t-2]) + W[t-7] + + SHA512_sigma0(W[t-15]) + W[t-16]; */ + uint32_t *Wt2 = &W[t2 - 2 * 2]; + uint32_t *Wt7 = &W[t2 - 7 * 2]; + uint32_t *Wt15 = &W[t2 - 15 * 2]; + uint32_t *Wt16 = &W[t2 - 16 * 2]; + SHA512_sigma1(Wt2, temp1); + SHA512_ADD(temp1, Wt7, temp2); + SHA512_sigma0(Wt15, temp1); + SHA512_ADD(temp1, Wt16, temp3); + SHA512_ADD(temp2, temp3, &W[t2]); + } + + A[0] = context->Intermediate_Hash[0]; + A[1] = context->Intermediate_Hash[1]; + B[0] = context->Intermediate_Hash[2]; + B[1] = context->Intermediate_Hash[3]; + C[0] = context->Intermediate_Hash[4]; + C[1] = context->Intermediate_Hash[5]; + D[0] = context->Intermediate_Hash[6]; + D[1] = context->Intermediate_Hash[7]; + E[0] = context->Intermediate_Hash[8]; + E[1] = context->Intermediate_Hash[9]; + F[0] = context->Intermediate_Hash[10]; + F[1] = context->Intermediate_Hash[11]; + G[0] = context->Intermediate_Hash[12]; + G[1] = context->Intermediate_Hash[13]; + H[0] = context->Intermediate_Hash[14]; + H[1] = context->Intermediate_Hash[15]; + + for (t = t2 = 0; t < 80; t++, t2 += 2) { + /* + * temp1 = H + SHA512_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t]; + */ + SHA512_SIGMA1(E, temp1); + SHA512_ADD(H, temp1, temp2); + SHA_Ch(E, F, G, temp3); + SHA512_ADD(temp2, temp3, temp4); + SHA512_ADD(&K[t2], &W[t2], temp5); + SHA512_ADD(temp4, temp5, temp1); + /* + * temp2 = SHA512_SIGMA0(A) + SHA_Maj(A,B,C); + */ + SHA512_SIGMA0(A, temp3); + SHA_Maj(A, B, C, temp4); + SHA512_ADD(temp3, temp4, temp2); + H[0] = G[0]; H[1] = G[1]; + G[0] = F[0]; G[1] = F[1]; + F[0] = E[0]; F[1] = E[1]; + SHA512_ADD(D, temp1, E); + D[0] = C[0]; D[1] = C[1]; + C[0] = B[0]; C[1] = B[1]; + B[0] = A[0]; B[1] = A[1]; + SHA512_ADD(temp1, temp2, A); + } + + SHA512_ADDTO2(&context->Intermediate_Hash[0], A); + SHA512_ADDTO2(&context->Intermediate_Hash[2], B); + SHA512_ADDTO2(&context->Intermediate_Hash[4], C); + SHA512_ADDTO2(&context->Intermediate_Hash[6], D); + SHA512_ADDTO2(&context->Intermediate_Hash[8], E); + SHA512_ADDTO2(&context->Intermediate_Hash[10], F); + SHA512_ADDTO2(&context->Intermediate_Hash[12], G); + SHA512_ADDTO2(&context->Intermediate_Hash[14], H); + +#else /* !USE_32BIT_ONLY */ + static const uint64_t K[80] = { + 0x428A2F98D728AE22ull, 0x7137449123EF65CDull, 0xB5C0FBCFEC4D3B2Full, + 0xE9B5DBA58189DBBCull, 0x3956C25BF348B538ull, 0x59F111F1B605D019ull, + 0x923F82A4AF194F9Bull, 0xAB1C5ED5DA6D8118ull, 0xD807AA98A3030242ull, + 0x12835B0145706FBEull, 0x243185BE4EE4B28Cull, 0x550C7DC3D5FFB4E2ull, + 0x72BE5D74F27B896Full, 0x80DEB1FE3B1696B1ull, 0x9BDC06A725C71235ull, + 0xC19BF174CF692694ull, 0xE49B69C19EF14AD2ull, 0xEFBE4786384F25E3ull, + 0x0FC19DC68B8CD5B5ull, 0x240CA1CC77AC9C65ull, 0x2DE92C6F592B0275ull, + 0x4A7484AA6EA6E483ull, 0x5CB0A9DCBD41FBD4ull, 0x76F988DA831153B5ull, + 0x983E5152EE66DFABull, 0xA831C66D2DB43210ull, 0xB00327C898FB213Full, + 0xBF597FC7BEEF0EE4ull, 0xC6E00BF33DA88FC2ull, 0xD5A79147930AA725ull, + 0x06CA6351E003826Full, 0x142929670A0E6E70ull, 0x27B70A8546D22FFCull, + 0x2E1B21385C26C926ull, 0x4D2C6DFC5AC42AEDull, 0x53380D139D95B3DFull, + 0x650A73548BAF63DEull, 0x766A0ABB3C77B2A8ull, 0x81C2C92E47EDAEE6ull, + 0x92722C851482353Bull, 0xA2BFE8A14CF10364ull, 0xA81A664BBC423001ull, + 0xC24B8B70D0F89791ull, 0xC76C51A30654BE30ull, 0xD192E819D6EF5218ull, + 0xD69906245565A910ull, 0xF40E35855771202Aull, 0x106AA07032BBD1B8ull, + 0x19A4C116B8D2D0C8ull, 0x1E376C085141AB53ull, 0x2748774CDF8EEB99ull, + 0x34B0BCB5E19B48A8ull, 0x391C0CB3C5C95A63ull, 0x4ED8AA4AE3418ACBull, + 0x5B9CCA4F7763E373ull, 0x682E6FF3D6B2B8A3ull, 0x748F82EE5DEFB2FCull, + 0x78A5636F43172F60ull, 0x84C87814A1F0AB72ull, 0x8CC702081A6439ECull, + 0x90BEFFFA23631E28ull, 0xA4506CEBDE82BDE9ull, 0xBEF9A3F7B2C67915ull, + 0xC67178F2E372532Bull, 0xCA273ECEEA26619Cull, 0xD186B8C721C0C207ull, + 0xEADA7DD6CDE0EB1Eull, 0xF57D4F7FEE6ED178ull, 0x06F067AA72176FBAull, + 0x0A637DC5A2C898A6ull, 0x113F9804BEF90DAEull, 0x1B710B35131C471Bull, + 0x28DB77F523047D84ull, 0x32CAAB7B40C72493ull, 0x3C9EBE0A15C9BEBCull, + 0x431D67C49C100D4Cull, 0x4CC5D4BECB3E42B6ull, 0x597F299CFC657E2Aull, + 0x5FCB6FAB3AD6FAECull, 0x6C44198C4A475817ull + }; + int t, t8; /* Loop counter */ + uint64_t temp1, temp2; /* Temporary word value */ + uint64_t W[80]; /* Word sequence */ + uint64_t A, B, C, D, E, F, G, H; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = t8 = 0; t < 16; t++, t8 += 8) + W[t] = ((uint64_t)(context->Message_Block[t8]) << 56) | + ((uint64_t)(context->Message_Block[t8 + 1]) << 48) | + ((uint64_t)(context->Message_Block[t8 + 2]) << 40) | + ((uint64_t)(context->Message_Block[t8 + 3]) << 32) | + ((uint64_t)(context->Message_Block[t8 + 4]) << 24) | + ((uint64_t)(context->Message_Block[t8 + 5]) << 16) | + ((uint64_t)(context->Message_Block[t8 + 6]) << 8) | + ((uint64_t)(context->Message_Block[t8 + 7])); + + for (t = 16; t < 80; t++) + W[t] = SHA512_sigma1(W[t - 2]) + W[t - 7] + + SHA512_sigma0(W[t - 15]) + W[t - 16]; + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + F = context->Intermediate_Hash[5]; + G = context->Intermediate_Hash[6]; + H = context->Intermediate_Hash[7]; + + for (t = 0; t < 80; t++) { + temp1 = H + SHA512_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t]; + temp2 = SHA512_SIGMA0(A) + SHA_Maj(A, B, C); + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + context->Intermediate_Hash[5] += F; + context->Intermediate_Hash[6] += G; + context->Intermediate_Hash[7] += H; +#endif /* USE_32BIT_ONLY */ + + context->Message_Block_Index = 0; +} + +/* +* SHA384_512Reset +* +* Description: +* This helper function will initialize the SHA512Context in +* preparation for computing a new SHA384 or SHA512 message +* digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* H0 +* The initial hash value to use. +* +* Returns: +* sha Error Code. +* +*/ +#ifdef USE_32BIT_ONLY +static int SHA384_512Reset(SHA512Context *context, uint32_t H0[]) +#else /* !USE_32BIT_ONLY */ +static int SHA384_512Reset(SHA512Context *context, uint64_t H0[]) +#endif /* USE_32BIT_ONLY */ +{ + int i; + if (!context) + return shaNull; + + context->Message_Block_Index = 0; + +#ifdef USE_32BIT_ONLY + context->Length[0] = context->Length[1] = 0; + context->Length[2] = context->Length[3] = 0; + + for (i = 0; i < SHA512HashSize / 4; i++) + context->Intermediate_Hash[i] = H0[i]; +#else /* !USE_32BIT_ONLY */ + context->Length_High = context->Length_Low = 0; + + for (i = 0; i < SHA512HashSize / 8; i++) + context->Intermediate_Hash[i] = H0[i]; +#endif /* USE_32BIT_ONLY */ + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* +* SHA384_512ResultN +* +* Description: +* This helper function will return the 384-bit or 512-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 48th/64th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* HashSize: [in] +* The size of the hash, either 48 or 64. +* +* Returns: +* sha Error Code. +* +*/ +static int SHA384_512ResultN(SHA512Context *context, + uint8_t Message_Digest[], int HashSize) +{ + int i; + +#ifdef USE_32BIT_ONLY + int i2; +#endif /* USE_32BIT_ONLY */ + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA384_512Finalize(context, 0x80); + +#ifdef USE_32BIT_ONLY + for (i = i2 = 0; i < HashSize;) { + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 24); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 16); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 8); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2++]); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 24); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 16); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 8); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2++]); + } +#else /* !USE_32BIT_ONLY */ + for (i = 0; i < HashSize; ++i) + Message_Digest[i] = (uint8_t) + (context->Intermediate_Hash[i >> 3] >> 8 * (7 - (i % 8))); +#endif /* USE_32BIT_ONLY */ + + return shaSuccess; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/singlylinkedlist.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,367 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +typedef struct LIST_ITEM_INSTANCE_TAG +{ + const void* item; + void* next; +} LIST_ITEM_INSTANCE; + +typedef struct SINGLYLINKEDLIST_INSTANCE_TAG +{ + LIST_ITEM_INSTANCE* head; + LIST_ITEM_INSTANCE* tail; +} LIST_INSTANCE; + +SINGLYLINKEDLIST_HANDLE singlylinkedlist_create(void) +{ + LIST_INSTANCE* result; + + /* Codes_SRS_LIST_01_001: [singlylinkedlist_create shall create a new list and return a non-NULL handle on success.] */ + result = (LIST_INSTANCE*)malloc(sizeof(LIST_INSTANCE)); + if (result != NULL) + { + /* Codes_SRS_LIST_01_002: [If any error occurs during the list creation, singlylinkedlist_create shall return NULL.] */ + result->head = NULL; + result->tail = NULL; + } + + return result; +} + +void singlylinkedlist_destroy(SINGLYLINKEDLIST_HANDLE list) +{ + /* Codes_SRS_LIST_01_004: [If the list argument is NULL, no freeing of resources shall occur.] */ + if (list != NULL) + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + + while (list_instance->head != NULL) + { + LIST_ITEM_INSTANCE* current_item = list_instance->head; + list_instance->head = (LIST_ITEM_INSTANCE*)current_item->next; + free(current_item); + } + + /* Codes_SRS_LIST_01_003: [singlylinkedlist_destroy shall free all resources associated with the list identified by the handle argument.] */ + free(list_instance); + } +} + +LIST_ITEM_HANDLE singlylinkedlist_add(SINGLYLINKEDLIST_HANDLE list, const void* item) +{ + LIST_ITEM_INSTANCE* result; + + /* Codes_SRS_LIST_01_006: [If any of the arguments is NULL, singlylinkedlist_add shall not add the item to the list and return NULL.] */ + if ((list == NULL) || + (item == NULL)) + { + LogError("Invalid argument (list=%p, item=%p)", list, item); + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + result = (LIST_ITEM_INSTANCE*)malloc(sizeof(LIST_ITEM_INSTANCE)); + + if (result == NULL) + { + /* Codes_SRS_LIST_01_007: [If allocating the new list node fails, singlylinkedlist_add shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_005: [singlylinkedlist_add shall add one item to the tail of the list and on success it shall return a handle to the added item.] */ + result->next = NULL; + result->item = item; + + if (list_instance->head == NULL) + { + list_instance->head = result; + list_instance->tail = result; + } + else + { + list_instance->tail->next = result; + list_instance->tail = result; + } + } + } + + return result; +} + +int singlylinkedlist_remove(SINGLYLINKEDLIST_HANDLE list, LIST_ITEM_HANDLE item) +{ + int result; + + /* Codes_SRS_LIST_01_024: [If any of the arguments list or item_handle is NULL, singlylinkedlist_remove shall fail and return a non-zero value.] */ + if ((list == NULL) || + (item == NULL)) + { + LogError("Invalid argument (list=%p, item=%p)", list, item); + result = __FAILURE__; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* current_item = list_instance->head; + LIST_ITEM_INSTANCE* previous_item = NULL; + + while (current_item != NULL) + { + if (current_item == item) + { + if (previous_item != NULL) + { + previous_item->next = current_item->next; + } + else + { + list_instance->head = (LIST_ITEM_INSTANCE*)current_item->next; + } + + if (current_item == list_instance->tail) + { + list_instance->tail = previous_item; + } + + free(current_item); + + break; + } + + previous_item = current_item; + current_item = (LIST_ITEM_INSTANCE*)current_item->next; + } + + if (current_item == NULL) + { + /* Codes_SRS_LIST_01_025: [If the item item_handle is not found in the list, then singlylinkedlist_remove shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + else + { + /* Codes_SRS_LIST_01_023: [singlylinkedlist_remove shall remove a list item from the list and on success it shall return 0.] */ + result = 0; + } + } + + return result; +} + +LIST_ITEM_HANDLE singlylinkedlist_get_head_item(SINGLYLINKEDLIST_HANDLE list) +{ + LIST_ITEM_HANDLE result; + + if (list == NULL) + { + /* Codes_SRS_LIST_01_009: [If the list argument is NULL, singlylinkedlist_get_head_item shall return NULL.] */ + LogError("Invalid argument (list=NULL)"); + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + + /* Codes_SRS_LIST_01_008: [singlylinkedlist_get_head_item shall return the head of the list.] */ + /* Codes_SRS_LIST_01_010: [If the list is empty, singlylinkedlist_get_head_item_shall_return NULL.] */ + result = list_instance->head; + } + + return result; +} + +LIST_ITEM_HANDLE singlylinkedlist_get_next_item(LIST_ITEM_HANDLE item_handle) +{ + LIST_ITEM_HANDLE result; + + if (item_handle == NULL) + { + LogError("Invalid argument (list is NULL)"); + /* Codes_SRS_LIST_01_019: [If item_handle is NULL then singlylinkedlist_get_next_item shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_018: [singlylinkedlist_get_next_item shall return the next item in the list following the item item_handle.] */ + result = (LIST_ITEM_HANDLE)((LIST_ITEM_INSTANCE*)item_handle)->next; + } + + return result; +} + +const void* singlylinkedlist_item_get_value(LIST_ITEM_HANDLE item_handle) +{ + const void* result; + + if (item_handle == NULL) + { + LogError("Invalid argument (item_handle is NULL)"); + /* Codes_SRS_LIST_01_021: [If item_handle is NULL, singlylinkedlist_item_get_value shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_020: [singlylinkedlist_item_get_value shall return the value associated with the list item identified by the item_handle argument.] */ + result = ((LIST_ITEM_INSTANCE*)item_handle)->item; + } + + return result; +} + +LIST_ITEM_HANDLE singlylinkedlist_find(SINGLYLINKEDLIST_HANDLE list, LIST_MATCH_FUNCTION match_function, const void* match_context) +{ + LIST_ITEM_HANDLE result; + + if ((list == NULL) || + (match_function == NULL)) + { + LogError("Invalid argument (list=%p, match_function=%p)", list, match_function); + /* Codes_SRS_LIST_01_012: [If the list or the match_function argument is NULL, singlylinkedlist_find shall return NULL.] */ + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* current = list_instance->head; + + /* Codes_SRS_LIST_01_011: [singlylinkedlist_find shall iterate through all items in a list and return the first one that satisfies a certain match function.] */ + while (current != NULL) + { + /* Codes_SRS_LIST_01_014: [list find shall determine whether an item satisfies the match criteria by invoking the match function for each item in the list until a matching item is found.] */ + /* Codes_SRS_LIST_01_013: [The match_function shall get as arguments the list item being attempted to be matched and the match_context as is.] */ + if (match_function((LIST_ITEM_HANDLE)current, match_context) == true) + { + /* Codes_SRS_LIST_01_017: [If the match function returns true, singlylinkedlist_find shall consider that item as matching.] */ + break; + } + + /* Codes_SRS_LIST_01_016: [If the match function returns false, singlylinkedlist_find shall consider that item as not matching.] */ + current = (LIST_ITEM_INSTANCE*)current->next; + } + + if (current == NULL) + { + /* Codes_SRS_LIST_01_015: [If the list is empty, singlylinkedlist_find shall return NULL.] */ + result = NULL; + } + else + { + result = current; + } + } + + return result; +} + +int singlylinkedlist_remove_if(SINGLYLINKEDLIST_HANDLE list, LIST_CONDITION_FUNCTION condition_function, const void* match_context) +{ + int result; + /* Codes_SRS_LIST_09_001: [ If the list or the condition_function argument is NULL, singlylinkedlist_remove_if shall return non-zero value. ] */ + if ((list == NULL) || + (condition_function == NULL)) + { + LogError("Invalid argument (list=%p, condition_function=%p)", list, condition_function); + result = __FAILURE__; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* current_item = list_instance->head; + LIST_ITEM_INSTANCE* next_item = NULL; + LIST_ITEM_INSTANCE* previous_item = NULL; + + /* Codes_SRS_LIST_09_002: [ singlylinkedlist_remove_if shall iterate through all items in a list and remove all that satisfies a certain condition function. ] */ + while (current_item != NULL) + { + bool continue_processing = false; + + next_item = (LIST_ITEM_INSTANCE*)current_item->next; + + /* Codes_SRS_LIST_09_003: [ singlylinkedlist_remove_if shall determine whether an item satisfies the condition criteria by invoking the condition function for that item. ] */ + /* Codes_SRS_LIST_09_004: [ If the condition function returns true, singlylinkedlist_find shall consider that item as to be removed. ] */ + if (condition_function(current_item->item, match_context, &continue_processing) == true) + { + if (previous_item != NULL) + { + previous_item->next = next_item; + } + else + { + list_instance->head = next_item; + } + + if (current_item == list_instance->tail) + { + list_instance->tail = previous_item; + } + + free(current_item); + } + /* Codes_SRS_LIST_09_005: [ If the condition function returns false, singlylinkedlist_find shall consider that item as not to be removed. ] */ + else + { + previous_item = current_item; + } + + /* Codes_SRS_LIST_09_006: [ If the condition function returns continue_processing as false, singlylinkedlist_remove_if shall stop iterating through the list and return. ] */ + if (continue_processing == false) + { + break; + } + + current_item = next_item; + } + + /* Codes_SRS_LIST_09_007: [ If no errors occur, singlylinkedlist_remove_if shall return zero. ] */ + result = 0; + } + + return result; +} + +int singlylinkedlist_foreach(SINGLYLINKEDLIST_HANDLE list, LIST_ACTION_FUNCTION action_function, const void* action_context) +{ + int result; + + /* Codes_SRS_LIST_09_008: [ If the list or the action_function argument is NULL, singlylinkedlist_foreach shall return non-zero value. ] */ + if ((list == NULL) || + (action_function == NULL)) + { + LogError("Invalid argument (list=%p, action_function=%p)", list, action_function); + result = __FAILURE__; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* list_item = list_instance->head; + + while (list_item != NULL) + { + bool continue_processing = false; + + /* Codes_SRS_LIST_09_009: [ singlylinkedlist_foreach shall iterate through all items in a list and invoke action_function for each one of them. ] */ + action_function(list_item->item, action_context, &continue_processing); + + /* Codes_SRS_LIST_09_010: [ If the condition function returns continue_processing as false, singlylinkedlist_foreach shall stop iterating through the list and return. ] */ + if (continue_processing == false) + { + break; + } + + list_item = (LIST_ITEM_INSTANCE*)list_item->next; + } + + /* Codes_SRS_LIST_09_011: [ If no errors occur, singlylinkedlist_foreach shall return zero. ] */ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/string_tokenizer.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include <stdbool.h> +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +typedef struct STRING_TOKEN_TAG +{ + const char* inputString; + const char* currentPos; + size_t sizeOfinputString; +} STRING_TOKEN; + +STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create(STRING_HANDLE handle) +{ + STRING_TOKENIZER_HANDLE result; + + /* Codes_SRS_STRING_04_001: [STRING_TOKENIZER_create shall return an NULL STRING_TOKENIZER_HANDLE if parameter handle is NULL] */ + if (handle == NULL) + { + LogError("Invalid Argument. Handle cannot be NULL."); + result = NULL; + } + else + { + /* Codes_SRS_STRING_04_002: [STRING_TOKENIZER_create shall allocate a new STRING_TOKENIZER_HANDLE having the content of the STRING_HANDLE copied and current position pointing at the beginning of the string] */ + result = STRING_TOKENIZER_create_from_char(STRING_c_str(handle)); + } + + return result; +} + +extern STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create_from_char(const char* input) +{ + STRING_TOKEN *result; + char* inputStringToMalloc; + + /* Codes_SRS_STRING_07_001: [STRING_TOKENIZER_create shall return an NULL STRING_TOKENIZER_HANDLE if parameter input is NULL] */ + if (input == NULL) + { + LogError("Invalid Argument. Handle cannot be NULL."); + result = NULL; + } + /* Codes_SRS_STRING_07_002: [STRING_TOKENIZER_create shall allocate a new STRING_TOKENIZER_HANDLE having the content of the STRING_HANDLE copied and current position pointing at the beginning of the string] */ + else if ((result = (STRING_TOKEN*)malloc(sizeof(STRING_TOKEN))) == NULL) + { + LogError("Memory Allocation failed. Cannot allocate STRING_TOKENIZER."); + } + else if ((mallocAndStrcpy_s(&inputStringToMalloc, input)) != 0) + { + LogError("Memory Allocation Failed. Cannot allocate and copy string Content."); + free(result); + result = NULL; + } + else + { + result->inputString = inputStringToMalloc; + result->currentPos = result->inputString; //Current Pos will point to the initial position of Token. + result->sizeOfinputString = strlen(result->inputString); //Calculate Size of Current String + } + return (STRING_TOKENIZER_HANDLE)result; +} + +int STRING_TOKENIZER_get_next_token(STRING_TOKENIZER_HANDLE tokenizer, STRING_HANDLE output, const char* delimiters) +{ + int result; + /* Codes_SRS_STRING_04_004: [STRING_TOKENIZER_get_next_token shall return a nonzero value if any of the 3 parameters is NULL] */ + if (tokenizer == NULL || output == NULL || delimiters == NULL) + { + result = __FAILURE__; + } + else + { + STRING_TOKEN* token = (STRING_TOKEN*)tokenizer; + /* Codes_SRS_STRING_04_011: [Each subsequent call to STRING_TOKENIZER_get_next_token starts searching from the saved position on t and behaves as described above.] */ + size_t remainingInputStringSize = token->sizeOfinputString - (token->currentPos - token->inputString); + size_t delimitterSize = strlen(delimiters); + + /* First Check if we reached the end of the string*/ + /* Codes_SRS_STRING_TOKENIZER_04_014: [STRING_TOKENIZER_get_next_token shall return nonzero value if t contains an empty string.] */ + if (remainingInputStringSize == 0) + { + result = __FAILURE__; + } + else if (delimitterSize == 0) + { + LogError("Empty delimiters parameter."); + result = __FAILURE__; + } + else + { + size_t i; + /* Codes_SRS_STRING_04_005: [STRING_TOKENIZER_get_next_token searches the string inside STRING_TOKENIZER_HANDLE for the first character that is NOT contained in the current delimiter] */ + for (i = 0; i < remainingInputStringSize; i++) + { + size_t j; + + bool foundDelimitter = false; + for (j = 0; j < delimitterSize; j++) + { + if (token->currentPos[i] == delimiters[j]) + { + foundDelimitter = true; + break; + } + } + + /* Codes_SRS_STRING_04_007: [If such a character is found, STRING_TOKENIZER_get_next_token consider it as the start of a token.] */ + if (!foundDelimitter) + { + break; + } + } + + /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ + //At this point update Current Pos to the character of the last token found or end of String. + token->currentPos += i; + + //Update the remainingInputStringSize + remainingInputStringSize -= i; + + /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ + if (remainingInputStringSize == 0) + { + result = __FAILURE__; + } + else + { + bool foundDelimitter = false; + const char* endOfTokenPosition=NULL; + size_t amountOfCharactersToCopy; + size_t j; + //At this point the Current Pos is pointing to a character that is point to a nonDelimiter. So, now search for a Delimiter, till the end of the String. + /*Codes_SRS_STRING_04_008: [STRING_TOKENIZER_get_next_token than searches from the start of a token for a character that is contained in the delimiters string.] */ + /* Codes_SRS_STRING_04_009: [If no such character is found, STRING_TOKENIZER_get_next_token extends the current token to the end of the string inside t, copies the token to output and returns 0.] */ + /* Codes_SRS_STRING_04_010: [If such a character is found, STRING_TOKENIZER_get_next_token consider it the end of the token and copy it's content to output, updates the current position inside t to the next character and returns 0.] */ + for (j = 0; j < delimitterSize; j++) + { + if ((endOfTokenPosition = strchr(token->currentPos, delimiters[j])) != NULL) + { + foundDelimitter = true; + break; + } + } + + //If token not found, than update the EndOfToken to the end of the inputString; + if (endOfTokenPosition == NULL) + { + amountOfCharactersToCopy = remainingInputStringSize; + } + else + { + amountOfCharactersToCopy = endOfTokenPosition - token->currentPos; + } + + //copy here the string to output. + if (STRING_copy_n(output, token->currentPos, amountOfCharactersToCopy) != 0) + { + LogError("Problem copying token to output String."); + result = __FAILURE__; + } + else + { + //Update the Current position. + //Check if end of String reached so, currentPos points to the end of String. + if (foundDelimitter) + { + token->currentPos += amountOfCharactersToCopy + 1; + } + else + { + token->currentPos += amountOfCharactersToCopy; + } + + result = 0; //Result will be on the output. + } + } + } + } + + return result; +} + + +/* Codes_SRS_STRING_TOKENIZER_04_012: [STRING_TOKENIZER_destroy shall free the memory allocated by the STRING_TOKENIZER_create ] */ +void STRING_TOKENIZER_destroy(STRING_TOKENIZER_HANDLE t) +{ + /* Codes_SRS_STRING_TOKENIZER_04_013: [When the t argument is NULL, then STRING_TOKENIZER_destroy shall not attempt to free] */ + if (t != NULL) + { + STRING_TOKEN* value = (STRING_TOKEN*)t; + free((char*)value->inputString); + value->inputString = NULL; + free(value); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/strings.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,859 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// PUT NO INCLUDES BEFORE HERE +// +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include <stddef.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +// +// PUT NO CLIENT LIBRARY INCLUDES BEFORE HERE +// + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +static const char hexToASCII[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +typedef struct STRING_TAG +{ + char* s; +} STRING; + +/*this function will allocate a new string with just '\0' in it*/ +/*return NULL if it fails*/ +/* Codes_SRS_STRING_07_001: [STRING_new shall allocate a new STRING_HANDLE pointing to an empty string.] */ +STRING_HANDLE STRING_new(void) +{ + STRING* result; + if ((result = (STRING*)malloc(sizeof(STRING))) != NULL) + { + if ((result->s = (char*)malloc(1)) != NULL) + { + result->s[0] = '\0'; + } + else + { + /* Codes_SRS_STRING_07_002: [STRING_new shall return an NULL STRING_HANDLE on any error that is encountered.] */ + LogError("Failure allocating in STRING_new."); + free(result); + result = NULL; + } + } + return (STRING_HANDLE)result; +} + +/*Codes_SRS_STRING_02_001: [STRING_clone shall produce a new string having the same content as the handle string.*/ +STRING_HANDLE STRING_clone(STRING_HANDLE handle) +{ + STRING* result; + /*Codes_SRS_STRING_02_002: [If parameter handle is NULL then STRING_clone shall return NULL.]*/ + if (handle == NULL) + { + result = NULL; + } + else + { + /*Codes_SRS_STRING_02_003: [If STRING_clone fails for any reason, it shall return NULL.] */ + if ((result = (STRING*)malloc(sizeof(STRING))) != NULL) + { + STRING* source = (STRING*)handle; + /*Codes_SRS_STRING_02_003: [If STRING_clone fails for any reason, it shall return NULL.] */ + size_t sourceLen = strlen(source->s); + if ((result->s = (char*)malloc(sourceLen + 1)) == NULL) + { + LogError("Failure allocating clone value."); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->s, source->s, sourceLen + 1); + } + } + else + { + /*not much to do, result is NULL from malloc*/ + } + } + return (STRING_HANDLE)result; +} + +/* Codes_SRS_STRING_07_003: [STRING_construct shall allocate a new string with the value of the specified const char*.] */ +STRING_HANDLE STRING_construct(const char* psz) +{ + STRING_HANDLE result; + if (psz == NULL) + { + /* Codes_SRS_STRING_07_005: [If the supplied const char* is NULL STRING_construct shall return a NULL value.] */ + result = NULL; + } + else + { + STRING* str; + if ((str = (STRING*)malloc(sizeof(STRING))) != NULL) + { + size_t nLen = strlen(psz) + 1; + if ((str->s = (char*)malloc(nLen)) != NULL) + { + (void)memcpy(str->s, psz, nLen); + result = (STRING_HANDLE)str; + } + /* Codes_SRS_STRING_07_032: [STRING_construct encounters any error it shall return a NULL value.] */ + else + { + LogError("Failure allocating constructed value."); + free(str); + result = NULL; + } + } + else + { + /* Codes_SRS_STRING_07_032: [STRING_construct encounters any error it shall return a NULL value.] */ + LogError("Failure allocating value."); + result = NULL; + } + } + return result; +} + +#if defined(__GNUC__) +__attribute__ ((format (printf, 1, 2))) +#endif +STRING_HANDLE STRING_construct_sprintf(const char* format, ...) +{ + STRING* result; + +#ifdef STRINGS_C_SPRINTF_BUFFER_SIZE + size_t maxBufSize = STRINGS_C_SPRINTF_BUFFER_SIZE; + char buf[STRINGS_C_SPRINTF_BUFFER_SIZE]; +#else + size_t maxBufSize = 0; + char* buf = NULL; +#endif + + if (format != NULL) + { + va_list arg_list; + int length; + va_start(arg_list, format); + + /* Codes_SRS_STRING_07_041: [STRING_construct_sprintf shall determine the size of the resulting string and allocate the necessary memory.] */ + length = vsnprintf(buf, maxBufSize, format, arg_list); + va_end(arg_list); + if (length > 0) + { + result = (STRING*)malloc(sizeof(STRING)); + if (result != NULL) + { + result->s = (char*)malloc(length+1); + if (result->s != NULL) + { + va_start(arg_list, format); + if (vsnprintf(result->s, length+1, format, arg_list) < 0) + { + /* Codes_SRS_STRING_07_040: [If any error is encountered STRING_construct_sprintf shall return NULL.] */ + free(result->s); + free(result); + result = NULL; + LogError("Failure: vsnprintf formatting failed."); + } + va_end(arg_list); + } + else + { + /* Codes_SRS_STRING_07_040: [If any error is encountered STRING_construct_sprintf shall return NULL.] */ + free(result); + result = NULL; + LogError("Failure: allocation sprintf value failed."); + } + } + else + { + LogError("Failure: allocation failed."); + } + } + else if (length == 0) + { + result = (STRING*)STRING_new(); + } + else + { + /* Codes_SRS_STRING_07_039: [If the parameter format is NULL then STRING_construct_sprintf shall return NULL.] */ + result = NULL; + LogError("Failure: vsnprintf return 0 length"); + } + } + else + { + LogError("Failure: invalid argument."); + result = NULL; + } + /* Codes_SRS_STRING_07_045: [STRING_construct_sprintf shall allocate a new string with the value of the specified printf formated const char. ] */ + return (STRING_HANDLE)result; +} + +/*this function will return a new STRING with the memory for the actual string passed in as a parameter.*/ +/*return NULL if it fails.*/ +/* The supplied memory must have been allocated with malloc! */ +/* Codes_SRS_STRING_07_006: [STRING_new_with_memory shall return a STRING_HANDLE by using the supplied char* memory.] */ +STRING_HANDLE STRING_new_with_memory(const char* memory) +{ + STRING* result; + if (memory == NULL) + { + /* Codes_SRS_STRING_07_007: [STRING_new_with_memory shall return a NULL STRING_HANDLE if the supplied char* is NULL.] */ + result = NULL; + } + else + { + if ((result = (STRING*)malloc(sizeof(STRING))) != NULL) + { + result->s = (char*)memory; + } + else + { + LogError("Failure: allocating memory string"); + } + } + return (STRING_HANDLE)result; +} + +/* Codes_SRS_STRING_07_008: [STRING_new_quoted shall return a valid STRING_HANDLE Copying the supplied const char* value surrounded by quotes.] */ +STRING_HANDLE STRING_new_quoted(const char* source) +{ + STRING* result; + if (source == NULL) + { + /* Codes_SRS_STRING_07_009: [STRING_new_quoted shall return a NULL STRING_HANDLE if the supplied const char* is NULL.] */ + result = NULL; + } + else if ((result = (STRING*)malloc(sizeof(STRING))) != NULL) + { + size_t sourceLength = strlen(source); + if ((result->s = (char*)malloc(sourceLength + 3)) != NULL) + { + result->s[0] = '"'; + (void)memcpy(result->s + 1, source, sourceLength); + result->s[sourceLength + 1] = '"'; + result->s[sourceLength + 2] = '\0'; + } + else + { + /* Codes_SRS_STRING_07_031: [STRING_new_quoted shall return a NULL STRING_HANDLE if any error is encountered.] */ + LogError("Failure allocating quoted string value."); + free(result); + result = NULL; + } + } + return (STRING_HANDLE)result; +} + +/*this function takes a regular const char* and turns in into "this is a\"JSON\" strings\u0008" (starting and ending quote included)*/ +/*the newly created handle needs to be disposed of with STRING_delete*/ +/*returns NULL if there are errors*/ +STRING_HANDLE STRING_new_JSON(const char* source) +{ + STRING* result; + if (source == NULL) + { + /*Codes_SRS_STRING_02_011: [If source is NULL then STRING_new_JSON shall return NULL.] */ + result = NULL; + LogError("invalid arg (NULL)"); + } + else + { + size_t i; + size_t nControlCharacters = 0; /*counts how many characters are to be expanded from 1 character to \uxxxx (6 characters)*/ + size_t nEscapeCharacters = 0; + size_t vlen = strlen(source); + + for (i = 0; i < vlen; i++) + { + /*Codes_SRS_STRING_02_014: [If any character has the value outside [1...127] then STRING_new_JSON shall fail and return NULL.] */ + if ((unsigned char)source[i] >= 128) /*this be a UNICODE character begin*/ + { + break; + } + else + { + if (source[i] <= 0x1F) + { + nControlCharacters++; + } + else if ( + (source[i] == '"') || + (source[i] == '\\') || + (source[i] == '/') + ) + { + nEscapeCharacters++; + } + } + } + + if (i < vlen) + { + result = NULL; + LogError("invalid character in input string"); + } + else + { + if ((result = (STRING*)malloc(sizeof(STRING))) == NULL) + { + /*Codes_SRS_STRING_02_021: [If the complete JSON representation cannot be produced, then STRING_new_JSON shall fail and return NULL.] */ + LogError("malloc json failure"); + } + else if ((result->s = (char*)malloc(vlen + 5 * nControlCharacters + nEscapeCharacters + 3)) == NULL) + { + /*Codes_SRS_STRING_02_021: [If the complete JSON representation cannot be produced, then STRING_new_JSON shall fail and return NULL.] */ + free(result); + result = NULL; + LogError("malloc failed"); + } + else + { + size_t pos = 0; + /*Codes_SRS_STRING_02_012: [The string shall begin with the quote character.] */ + result->s[pos++] = '"'; + for (i = 0; i < vlen; i++) + { + if (source[i] <= 0x1F) + { + /*Codes_SRS_STRING_02_019: [If the character code is less than 0x20 then it shall be represented as \u00xx, where xx is the hex representation of the character code.]*/ + result->s[pos++] = '\\'; + result->s[pos++] = 'u'; + result->s[pos++] = '0'; + result->s[pos++] = '0'; + result->s[pos++] = hexToASCII[(source[i] & 0xF0) >> 4]; /*high nibble*/ + result->s[pos++] = hexToASCII[source[i] & 0x0F]; /*low nibble*/ + } + else if (source[i] == '"') + { + /*Codes_SRS_STRING_02_016: [If the character is " (quote) then it shall be repsented as \".] */ + result->s[pos++] = '\\'; + result->s[pos++] = '"'; + } + else if (source[i] == '\\') + { + /*Codes_SRS_STRING_02_017: [If the character is \ (backslash) then it shall represented as \\.] */ + result->s[pos++] = '\\'; + result->s[pos++] = '\\'; + } + else if (source[i] == '/') + { + /*Codes_SRS_STRING_02_018: [If the character is / (slash) then it shall be represented as \/.] */ + result->s[pos++] = '\\'; + result->s[pos++] = '/'; + } + else + { + /*Codes_SRS_STRING_02_013: [The string shall copy the characters of source "as they are" (until the '\0' character) with the following exceptions:] */ + result->s[pos++] = source[i]; + } + } + /*Codes_SRS_STRING_02_020: [The string shall end with " (quote).] */ + result->s[pos++] = '"'; + /*zero terminating it*/ + result->s[pos] = '\0'; + } + } + + } + return (STRING_HANDLE)result; +} + +/*this function will concatenate to the string s1 the string s2, resulting in s1+s2*/ +/*returns 0 if success*/ +/*any other error code is failure*/ +/* Codes_SRS_STRING_07_012: [STRING_concat shall concatenate the given STRING_HANDLE and the const char* value and place the value in the handle.] */ +int STRING_concat(STRING_HANDLE handle, const char* s2) +{ + int result; + if ((handle == NULL) || (s2 == NULL)) + { + /* Codes_SRS_STRING_07_013: [STRING_concat shall return a nonzero number if an error is encountered.] */ + result = __FAILURE__; + } + else + { + STRING* s1 = (STRING*)handle; + size_t s1Length = strlen(s1->s); + size_t s2Length = strlen(s2); + char* temp = (char*)realloc(s1->s, s1Length + s2Length + 1); + if (temp == NULL) + { + /* Codes_SRS_STRING_07_013: [STRING_concat shall return a nonzero number if an error is encountered.] */ + LogError("Failure reallocating value."); + result = __FAILURE__; + } + else + { + s1->s = temp; + (void)memcpy(s1->s + s1Length, s2, s2Length + 1); + result = 0; + } + } + return result; +} + +/*this function will concatenate to the string s1 the string s2, resulting in s1+s2*/ +/*returns 0 if success*/ +/*any other error code is failure*/ +/* Codes_SRS_STRING_07_034: [String_Concat_with_STRING shall concatenate a given STRING_HANDLE variable with a source STRING_HANDLE.] */ +int STRING_concat_with_STRING(STRING_HANDLE s1, STRING_HANDLE s2) +{ + int result; + if ((s1 == NULL) || (s2 == NULL)) + { + /* Codes_SRS_STRING_07_035: [String_Concat_with_STRING shall return a nonzero number if an error is encountered.] */ + LogError("Invalid argument specified"); + result = __FAILURE__; + } + else + { + STRING* dest = (STRING*)s1; + STRING* src = (STRING*)s2; + + size_t s1Length = strlen(dest->s); + size_t s2Length = strlen(src->s); + char* temp = (char*)realloc(dest->s, s1Length + s2Length + 1); + if (temp == NULL) + { + /* Codes_SRS_STRING_07_035: [String_Concat_with_STRING shall return a nonzero number if an error is encountered.] */ + LogError("Failure reallocating value"); + result = __FAILURE__; + } + else + { + dest->s = temp; + /* Codes_SRS_STRING_07_034: [String_Concat_with_STRING shall concatenate a given STRING_HANDLE variable with a source STRING_HANDLE.] */ + (void)memcpy(dest->s + s1Length, src->s, s2Length + 1); + result = 0; + } + } + return result; +} + +/*this function will copy the string from s2 to s1*/ +/*returns 0 if success*/ +/*any other error code is failure*/ +/* Codes_SRS_STRING_07_016: [STRING_copy shall copy the const char* into the supplied STRING_HANDLE.] */ +int STRING_copy(STRING_HANDLE handle, const char* s2) +{ + int result; + if ((handle == NULL) || (s2 == NULL)) + { + /* Codes_SRS_STRING_07_017: [STRING_copy shall return a nonzero value if any of the supplied parameters are NULL.] */ + result = __FAILURE__; + } + else + { + STRING* s1 = (STRING*)handle; + /* Codes_SRS_STRING_07_026: [If the underlying char* refered to by s1 handle is equal to char* s2 than STRING_copy shall be a noop and return 0.] */ + if (s1->s != s2) + { + size_t s2Length = strlen(s2); + char* temp = (char*)realloc(s1->s, s2Length + 1); + if (temp == NULL) + { + LogError("Failure reallocating value."); + /* Codes_SRS_STRING_07_027: [STRING_copy shall return a nonzero value if any error is encountered.] */ + result = __FAILURE__; + } + else + { + s1->s = temp; + memmove(s1->s, s2, s2Length + 1); + result = 0; + } + } + else + { + /* Codes_SRS_STRING_07_033: [If overlapping pointer address is given to STRING_copy the behavior is undefined.] */ + result = 0; + } + } + return result; +} + +/*this function will copy n chars from s2 to the string s1, resulting in n chars only from s2 being stored in s1.*/ +/*returns 0 if success*/ +/*any other error code is failure*/ +/* Codes_SRS_STRING_07_018: [STRING_copy_n shall copy the number of characters in const char* or the size_t whichever is lesser.] */ +int STRING_copy_n(STRING_HANDLE handle, const char* s2, size_t n) +{ + int result; + if ((handle == NULL) || (s2 == NULL)) + { + /* Codes_SRS_STRING_07_019: [STRING_copy_n shall return a nonzero value if STRING_HANDLE or const char* is NULL.] */ + result = __FAILURE__; + } + else + { + STRING* s1 = (STRING*)handle; + size_t s2Length = strlen(s2); + char* temp; + if (s2Length > n) + { + s2Length = n; + } + + temp = (char*)realloc(s1->s, s2Length + 1); + if (temp == NULL) + { + LogError("Failure reallocating value."); + /* Codes_SRS_STRING_07_028: [STRING_copy_n shall return a nonzero value if any error is encountered.] */ + result = __FAILURE__; + } + else + { + s1->s = temp; + (void)memcpy(s1->s, s2, s2Length); + s1->s[s2Length] = 0; + result = 0; + } + + } + return result; +} + +#if defined(__GNUC__) +__attribute__ ((format (printf, 2, 3))) +#endif +int STRING_sprintf(STRING_HANDLE handle, const char* format, ...) +{ + int result; + +#ifdef STRINGS_C_SPRINTF_BUFFER_SIZE + size_t maxBufSize = STRINGS_C_SPRINTF_BUFFER_SIZE; + char buf[STRINGS_C_SPRINTF_BUFFER_SIZE]; +#else + size_t maxBufSize = 0; + char* buf = NULL; +#endif + + if (handle == NULL || format == NULL) + { + /* Codes_SRS_STRING_07_042: [if the parameters s1 or format are NULL then STRING_sprintf shall return non zero value.] */ + LogError("Invalid arg (NULL)"); + result = __FAILURE__; + } + else + { + va_list arg_list; + int s2Length; + va_start(arg_list, format); + + s2Length = vsnprintf(buf, maxBufSize, format, arg_list); + va_end(arg_list); + if (s2Length < 0) + { + /* Codes_SRS_STRING_07_043: [If any error is encountered STRING_sprintf shall return a non zero value.] */ + LogError("Failure vsnprintf return < 0"); + result = __FAILURE__; + } + else if (s2Length == 0) + { + // Don't need to reallocate and nothing should be added + result = 0; + } + else + { + STRING* s1 = (STRING*)handle; + char* temp; + size_t s1Length = strlen(s1->s); + temp = (char*)realloc(s1->s, s1Length + s2Length + 1); + if (temp != NULL) + { + s1->s = temp; + va_start(arg_list, format); + if (vsnprintf(s1->s + s1Length, s1Length + s2Length + 1, format, arg_list) < 0) + { + /* Codes_SRS_STRING_07_043: [If any error is encountered STRING_sprintf shall return a non zero value.] */ + LogError("Failure vsnprintf formatting error"); + s1->s[s1Length] = '\0'; + result = __FAILURE__; + } + else + { + /* Codes_SRS_STRING_07_044: [On success STRING_sprintf shall return 0.]*/ + result = 0; + } + va_end(arg_list); + } + else + { + /* Codes_SRS_STRING_07_043: [If any error is encountered STRING_sprintf shall return a non zero value.] */ + LogError("Failure unable to reallocate memory"); + result = __FAILURE__; + } + } + } + return result; +} + +/*this function will quote the string passed as argument string =>"string"*/ +/*returns 0 if success*/ /*doesn't change the string otherwise*/ +/*any other error code is failure*/ +/* Codes_SRS_STRING_07_014: [STRING_quote shall "quote" the supplied STRING_HANDLE and return 0 on success.] */ +int STRING_quote(STRING_HANDLE handle) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_STRING_07_015: [STRING_quote shall return a nonzero value if any of the supplied parameters are NULL.] */ + result = __FAILURE__; + } + else + { + STRING* s1 = (STRING*)handle; + size_t s1Length = strlen(s1->s); + char* temp = (char*)realloc(s1->s, s1Length + 2 + 1);/*2 because 2 quotes, 1 because '\0'*/ + if (temp == NULL) + { + LogError("Failure reallocating value."); + /* Codes_SRS_STRING_07_029: [STRING_quote shall return a nonzero value if any error is encountered.] */ + result = __FAILURE__; + } + else + { + s1->s = temp; + memmove(s1->s + 1, s1->s, s1Length); + s1->s[0] = '"'; + s1->s[s1Length + 1] = '"'; + s1->s[s1Length + 2] = '\0'; + result = 0; + } + } + return result; +} +/*this function will revert a string to an empty state*/ +/*Returns 0 if the revert was succesful*/ +/* Codes_SRS_STRING_07_022: [STRING_empty shall revert the STRING_HANDLE to an empty state.] */ +int STRING_empty(STRING_HANDLE handle) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_STRING_07_023: [STRING_empty shall return a nonzero value if the STRING_HANDLE is NULL.] */ + result = __FAILURE__; + } + else + { + STRING* s1 = (STRING*)handle; + char* temp = (char*)realloc(s1->s, 1); + if (temp == NULL) + { + LogError("Failure reallocating value."); + /* Codes_SRS_STRING_07_030: [STRING_empty shall return a nonzero value if the STRING_HANDLE is NULL.] */ + result = __FAILURE__; + } + else + { + s1->s = temp; + s1->s[0] = '\0'; + result = 0; + } + } + return result; +} + +/*this function will deallocate a string constructed by str_new*/ +/* Codes_SRS_STRING_07_010: [STRING_delete will free the memory allocated by the STRING_HANDLE.] */ +void STRING_delete(STRING_HANDLE handle) +{ + /* Codes_SRS_STRING_07_011: [STRING_delete will not attempt to free anything with a NULL STRING_HANDLE.] */ + if (handle != NULL) + { + STRING* value = (STRING*)handle; + free(value->s); + value->s = NULL; + free(value); + } +} + +/* Codes_SRS_STRING_07_020: [STRING_c_str shall return the const char* associated with the given STRING_HANDLE.] */ +const char* STRING_c_str(STRING_HANDLE handle) +{ + const char* result; + if (handle != NULL) + { + result = ((STRING*)handle)->s; + } + else + { + /* Codes_SRS_STRING_07_021: [STRING_c_str shall return NULL if the STRING_HANDLE is NULL.] */ + result = NULL; + } + return result; +} + +/* Codes_SRS_STRING_07_024: [STRING_length shall return the length of the underlying char* for the given handle] */ +size_t STRING_length(STRING_HANDLE handle) +{ + size_t result = 0; + /* Codes_SRS_STRING_07_025: [STRING_length shall return zero if the given handle is NULL.] */ + if (handle != NULL) + { + STRING* value = (STRING*)handle; + result = strlen(value->s); + } + return result; +} + +/*Codes_SRS_STRING_02_007: [STRING_construct_n shall construct a STRING_HANDLE from first "n" characters of the string pointed to by psz parameter.]*/ +STRING_HANDLE STRING_construct_n(const char* psz, size_t n) +{ + STRING_HANDLE result; + /*Codes_SRS_STRING_02_008: [If psz is NULL then STRING_construct_n shall return NULL.] */ + if (psz == NULL) + { + result = NULL; + LogError("invalid arg (NULL)"); + } + else + { + size_t len = strlen(psz); + /*Codes_SRS_STRING_02_009: [If n is bigger than the size of the string psz, then STRING_construct_n shall return NULL.] */ + if (n > len) + { + result = NULL; + LogError("invalig arg (n is bigger than the size of the string)"); + } + else + { + STRING* str; + if ((str = (STRING*)malloc(sizeof(STRING))) != NULL) + { + if ((str->s = (char*)malloc(len + 1)) != NULL) + { + (void)memcpy(str->s, psz, n); + str->s[n] = '\0'; + result = (STRING_HANDLE)str; + } + /* Codes_SRS_STRING_02_010: [In all other error cases, STRING_construct_n shall return NULL.] */ + else + { + LogError("Failure allocating value."); + free(str); + result = NULL; + } + } + else + { + /* Codes_SRS_STRING_02_010: [In all other error cases, STRING_construct_n shall return NULL.] */ + result = NULL; + } + } + } + return result; +} + +/* Codes_SRS_STRING_07_034: [STRING_compare returns an integer greater than, equal to, or less than zero, accordingly as the string pointed to by s1 is greater than, equal to, or less than the string s2.] */ +int STRING_compare(STRING_HANDLE s1, STRING_HANDLE s2) +{ + int result; + if (s1 == NULL && s2 == NULL) + { + /* Codes_SRS_STRING_07_035: [If h1 and h2 are both NULL then STRING_compare shall return 0.]*/ + result = 0; + } + else if (s1 == NULL) + { + /* Codes_SRS_STRING_07_036: [If h1 is NULL and h2 is nonNULL then STRING_compare shall return 1.]*/ + result = 1; + } + else if (s2 == NULL) + { + /* Codes_SRS_STRING_07_037: [If h2 is NULL and h1 is nonNULL then STRING_compare shall return -1.] */ + result = -1; + } + else + { + /* Codes_SRS_STRING_07_038: [STRING_compare shall compare the char s variable using the strcmp function.] */ + STRING* value1 = (STRING*)s1; + STRING* value2 = (STRING*)s2; + result = strcmp(value1->s, value2->s); + } + return result; +} + +STRING_HANDLE STRING_from_byte_array(const unsigned char* source, size_t size) +{ + STRING* result; + /*Codes_SRS_STRING_02_022: [ If source is NULL and size > 0 then STRING_from_BUFFER shall fail and return NULL. ]*/ + if ((source == NULL) && (size > 0)) + { + LogError("invalid parameter (NULL)"); + result = NULL; + } + else + { + /*Codes_SRS_STRING_02_023: [ Otherwise, STRING_from_BUFFER shall build a string that has the same content (byte-by-byte) as source and return a non-NULL handle. ]*/ + result = (STRING*)malloc(sizeof(STRING)); + if (result == NULL) + { + /*Codes_SRS_STRING_02_024: [ If building the string fails, then STRING_from_BUFFER shall fail and return NULL. ]*/ + LogError("oom - unable to malloc"); + /*return as is*/ + } + else + { + /*Codes_SRS_STRING_02_023: [ Otherwise, STRING_from_BUFFER shall build a string that has the same content (byte-by-byte) as source and return a non-NULL handle. ]*/ + result->s = (char*)malloc(size + 1); + if (result->s == NULL) + { + /*Codes_SRS_STRING_02_024: [ If building the string fails, then STRING_from_BUFFER shall fail and return NULL. ]*/ + LogError("oom - unable to malloc"); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->s, source, size); + result->s[size] = '\0'; /*all is fine*/ + } + } + } + return (STRING_HANDLE)result; +} + +int STRING_replace(STRING_HANDLE handle, char target, char replace) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_STRING_07_046: [ If handle is NULL STRING_replace shall return a non-zero value. ] */ + result = __FAILURE__; + } + else if (target == replace) + { + /* Codes_SRS_STRING_07_048: [ If target and replace are equal STRING_replace, shall do nothing shall return zero. ] */ + result = 0; + } + else + { + size_t length; + size_t index; + /* Codes_SRS_STRING_07_047: [ STRING_replace shall replace all instances of target with replace. ] */ + STRING* str_value = (STRING*)handle; + length = strlen(str_value->s); + for (index = 0; index < length; index++) + { + if (str_value->s[index] == target) + { + str_value->s[index] = replace; + } + } + /* Codes_SRS_STRING_07_049: [ On success STRING_replace shall return zero. ] */ + result = 0; + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/urlencode.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,352 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/urlencode.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +#define NIBBLE_TO_STRING(c) (char)((c) < 10 ? (c) + '0' : (c) - 10 + 'a') +#define NIBBLE_FROM_STRING(c) (char)(ISDIGIT(c) ? (c) - '0' : TOUPPER(c) + 10 - 'A') +#define IS_HEXDIGIT(c) ( \ + ((c >= '0') && (c <= '9')) || \ + ((c >= 'A') && (c <= 'F')) || \ + ((c >= 'a') && (c <= 'f')) \ +) +#define IS_PRINTABLE(c) ( \ + (c == 0) || \ + (c == '!') || \ + (c == '(') || (c == ')') || (c == '*') || \ + (c == '-') || (c == '.') || \ + ((c >= '0') && (c <= '9')) || \ + ((c >= 'A') && (c <= 'Z')) || \ + (c == '_') || \ + ((c >= 'a') && (c <= 'z')) \ +) + +/*The below macros are to be called on the big nibble of a hex value*/ +#define IS_IN_ASCII_RANGE(c) ( \ + (c >= '0') && (c <= '7') \ +) +#define IS_IN_EXTENDED_ASCII_RANGE(c) ( \ + ((c >= '8') && (c <= '9')) || \ + ((c >= 'A') && (c <= 'F')) || \ + ((c >= 'a') && (c <= 'f')) \ +) +#define IS_IN_CONTINUATION_BYTE_RANGE(c) ( \ + (c == '8') || (c == '9') || \ + (c == 'A') || (c == 'B') || \ + (c == 'a') || (c == 'b') \ +) +#define IS_IN_LEADING_BYTE_RANGE(c) ( \ + ((c >= 'C') && (c <= 'F')) || \ + ((c >= 'c') && (c <= 'f')) \ +) +#define IS_IN_UNSUPPORTED_LEADING_BYTE_RANGE(c) ( \ + ((c >= 'D') && (c <= 'F')) || \ + ((c >= 'd') && (c <= 'f')) \ +) + +static size_t URL_PrintableChar(unsigned char charVal, char* buffer) +{ + size_t size; + if (IS_PRINTABLE(charVal)) + { + buffer[0] = (char)charVal; + size = 1; + } + else + { + char bigNibbleStr; + char littleNibbleStr; + unsigned char bigNibbleVal = charVal >> 4; + unsigned char littleNibbleVal = charVal & 0x0F; + + if (bigNibbleVal >= 0x0C) + { + bigNibbleVal -= 0x04; + } + + bigNibbleStr = NIBBLE_TO_STRING(bigNibbleVal); + littleNibbleStr = NIBBLE_TO_STRING(littleNibbleVal); + + buffer[0] = '%'; + + if (charVal < 0x80) + { + buffer[1] = bigNibbleStr; + buffer[2] = littleNibbleStr; + size = 3; + } + else + { + buffer[1] = 'c'; + buffer[3] = '%'; + buffer[4] = bigNibbleStr; + buffer[5] = littleNibbleStr; + if (charVal < 0xC0) + { + buffer[2] = '2'; + } + else + { + buffer[2] = '3'; + } + size = 6; + } + } + + return size; +} + +static size_t calculateDecodedStringSize(const char* encodedString, size_t len) +{ + size_t decodedSize = 0; + + if (encodedString == NULL) + { + LogError("Null encoded string"); + } + else if (len == 0) + { + decodedSize = 1; //for null terminator + } + else + { + size_t remaining_len = len; + size_t next_step = 0; + size_t i = 0; + while (i < len) + { + //percent encoded character + if (encodedString[i] == '%') + { + if (remaining_len < 3 || !IS_HEXDIGIT(encodedString[i+1]) || !IS_HEXDIGIT(encodedString[i+2])) + { + LogError("Incomplete or invalid percent encoding"); + break; + } + else if (!IS_IN_ASCII_RANGE(encodedString[i+1])) + { + LogError("Out of range of characters accepted by this decoder"); + break; + } + else + { + decodedSize++; + next_step = 3; + } + } + else if (!IS_PRINTABLE(encodedString[i])) + { + LogError("Unprintable value in encoded string"); + break; + } + //safe character + else + { + decodedSize++; + next_step = 1; + } + + i += next_step; + remaining_len -= next_step; + } + + if (encodedString[i] != '\0') //i.e. broke out of above loop due to error + { + decodedSize = 0; + } + else + { + decodedSize++; //add space for the null terminator + } + } + return decodedSize; +} + +static unsigned char charFromNibbles(char bigNibbleStr, char littleNibbleStr) +{ + unsigned char bigNibbleVal = NIBBLE_FROM_STRING(bigNibbleStr); + unsigned char littleNibbleVal = NIBBLE_FROM_STRING(littleNibbleStr); + + return bigNibbleVal << 4 | littleNibbleVal; +} + +static void createDecodedString(const char* input, size_t input_size, char* output) +{ + /* Note that there is no danger of reckless indexing here, as calculateDecodedStringSize() + has already checked lengths of strings to ensure the formatting is always correct*/ + size_t i = 0; + while (i <= input_size) //the <= instead of < ensures the '\0' will be copied + { + if (input[i] != '%') + { + *output++ = input[i]; + i++; + } + else + { + *output++ = charFromNibbles(input[i+1], input[i+2]); + i += 3; + } + } +} + +static size_t URL_PrintableCharSize(unsigned char charVal) +{ + size_t size; + if (IS_PRINTABLE(charVal)) + { + size = 1; + } + else + { + if (charVal < 0x80) + { + size = 3; + } + else + { + size = 6; + } + } + return size; +} + +STRING_HANDLE URL_EncodeString(const char* textEncode) +{ + STRING_HANDLE result; + if (textEncode == NULL) + { + result = NULL; + } + else + { + STRING_HANDLE tempString = STRING_construct(textEncode); + if (tempString == NULL) + { + result = NULL; + } + else + { + result = URL_Encode(tempString); + STRING_delete(tempString); + } + } + return result; +} + +STRING_HANDLE URL_Encode(STRING_HANDLE input) +{ + STRING_HANDLE result; + if (input == NULL) + { + /*Codes_SRS_URL_ENCODE_06_001: [If input is NULL then URL_Encode will return NULL.]*/ + result = NULL; + LogError("URL_Encode:: NULL input"); + } + else + { + size_t lengthOfResult = 0; + char* encodedURL; + const char* currentInput; + unsigned char currentUnsignedChar; + currentInput = STRING_c_str(input); + /*Codes_SRS_URL_ENCODE_06_003: [If input is a zero length string then URL_Encode will return a zero length string.]*/ + do + { + currentUnsignedChar = (unsigned char)(*currentInput++); + lengthOfResult += URL_PrintableCharSize(currentUnsignedChar); + } while (currentUnsignedChar != 0); + if ((encodedURL = (char*)malloc(lengthOfResult)) == NULL) + { + /*Codes_SRS_URL_ENCODE_06_002: [If an error occurs during the encoding of input then URL_Encode will return NULL.]*/ + result = NULL; + LogError("URL_Encode:: MALLOC failure on encode."); + } + else + { + size_t currentEncodePosition = 0; + currentInput = STRING_c_str(input); + do + { + currentUnsignedChar = (unsigned char)(*currentInput++); + currentEncodePosition += URL_PrintableChar(currentUnsignedChar, &encodedURL[currentEncodePosition]); + } while (currentUnsignedChar != 0); + + result = STRING_new_with_memory(encodedURL); + if (result == NULL) + { + LogError("URL_Encode:: MALLOC failure on encode."); + free(encodedURL); + } + } + } + return result; +} + +STRING_HANDLE URL_DecodeString(const char* textDecode) +{ + STRING_HANDLE result; + if (textDecode == NULL) + { + result = NULL; + } + else + { + STRING_HANDLE tempString = STRING_construct(textDecode); + if (tempString == NULL) + { + result = NULL; + } + else + { + result = URL_Decode(tempString); + STRING_delete(tempString); + } + } + return result; +} + +STRING_HANDLE URL_Decode(STRING_HANDLE input) +{ + STRING_HANDLE result; + if (input == NULL) + { + LogError("URL_Decode:: NULL input"); + result = NULL; + } + else + { + size_t decodedStringSize; + char* decodedString; + const char* inputString = STRING_c_str(input); + size_t inputLen = strlen(inputString); + if ((decodedStringSize = calculateDecodedStringSize(inputString, inputLen)) == 0) + { + LogError("URL_Decode:: Invalid input string"); + result = NULL; + } + else if ((decodedString = (char*)malloc(decodedStringSize)) == NULL) + { + LogError("URL_Decode:: MALLOC failure on decode."); + result = NULL; + } + else + { + createDecodedString(inputString, inputLen, decodedString); + result = STRING_new_with_memory(decodedString); + if (result == NULL) + { + LogError("URL_Decode:: MALLOC failure on decode"); + free(decodedString); + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/usha.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/**************************** usha.c ****************************/ +/******************** See RFC 4634 for details ******************/ +/* +* Description: +* This file implements a unified interface to the SHA algorithms. +*/ + +#include "azure_c_shared_utility/sha.h" + +/* +* USHAReset +* +* Description: +* This function will initialize the SHA Context in preparation +* for computing a new SHA message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* whichSha: [in] +* Selects which SHA reset to call +* +* Returns: +* sha Error Code. +* +*/ +int USHAReset(USHAContext *ctx, enum SHAversion whichSha) +{ + if (ctx) { + ctx->whichSha = whichSha; + switch (whichSha) { + case SHA1: return SHA1Reset((SHA1Context*)&ctx->ctx); + case SHA224: return SHA224Reset((SHA224Context*)&ctx->ctx); + case SHA256: return SHA256Reset((SHA256Context*)&ctx->ctx); + case SHA384: return SHA384Reset((SHA384Context*)&ctx->ctx); + case SHA512: return SHA512Reset((SHA512Context*)&ctx->ctx); + default: return shaBadParam; + } + } + else { + return shaNull; + } +} + +/* +* USHAInput +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int USHAInput(USHAContext *ctx, + const uint8_t *bytes, unsigned int bytecount) +{ + if (ctx) { + switch (ctx->whichSha) { + case SHA1: + return SHA1Input((SHA1Context*)&ctx->ctx, bytes, bytecount); + case SHA224: + return SHA224Input((SHA224Context*)&ctx->ctx, bytes, + bytecount); + case SHA256: + return SHA256Input((SHA256Context*)&ctx->ctx, bytes, + bytecount); + case SHA384: + return SHA384Input((SHA384Context*)&ctx->ctx, bytes, + bytecount); + case SHA512: + return SHA512Input((SHA512Context*)&ctx->ctx, bytes, + bytecount); + default: return shaBadParam; + } + } + else { + return shaNull; + } +} + +/* +* USHAFinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int USHAFinalBits(USHAContext *ctx, +const uint8_t bits, unsigned int bitcount) +{ + if (ctx) { + switch (ctx->whichSha) { + case SHA1: + return SHA1FinalBits((SHA1Context*)&ctx->ctx, bits, bitcount); + case SHA224: + return SHA224FinalBits((SHA224Context*)&ctx->ctx, bits, + bitcount); + case SHA256: + return SHA256FinalBits((SHA256Context*)&ctx->ctx, bits, + bitcount); + case SHA384: + return SHA384FinalBits((SHA384Context*)&ctx->ctx, bits, + bitcount); + case SHA512: + return SHA512FinalBits((SHA512Context*)&ctx->ctx, bits, + bitcount); + default: return shaBadParam; + } + } + else { + return shaNull; + } +} + +/* +* USHAResult +* +* Description: +* This function will return the 160-bit message digest into the +* Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 19th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA-1 hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int USHAResult(USHAContext *ctx, + uint8_t Message_Digest[USHAMaxHashSize]) +{ + if (ctx) { + switch (ctx->whichSha) { + case SHA1: + return SHA1Result((SHA1Context*)&ctx->ctx, Message_Digest); + case SHA224: + return SHA224Result((SHA224Context*)&ctx->ctx, Message_Digest); + case SHA256: + return SHA256Result((SHA256Context*)&ctx->ctx, Message_Digest); + case SHA384: + return SHA384Result((SHA384Context*)&ctx->ctx, Message_Digest); + case SHA512: + return SHA512Result((SHA512Context*)&ctx->ctx, Message_Digest); + default: return shaBadParam; + } + } + else { + return shaNull; + } +} + +/* +* USHABlockSize +* +* Description: +* This function will return the blocksize for the given SHA +* algorithm. +* +* Parameters: +* whichSha: +* which SHA algorithm to query +* +* Returns: +* block size +* +*/ +int USHABlockSize(enum SHAversion whichSha) +{ + switch (whichSha) { + case SHA1: return SHA1_Message_Block_Size; + case SHA224: return SHA224_Message_Block_Size; + case SHA256: return SHA256_Message_Block_Size; + case SHA384: return SHA384_Message_Block_Size; + default: + case SHA512: return SHA512_Message_Block_Size; + } +} + +/* +* USHAHashSize +* +* Description: +* This function will return the hashsize for the given SHA +* algorithm. +* +* Parameters: +* whichSha: +* which SHA algorithm to query +* +* Returns: +* hash size +* +*/ +int USHAHashSize(enum SHAversion whichSha) +{ + switch (whichSha) { + case SHA1: return SHA1HashSize; + case SHA224: return SHA224HashSize; + case SHA256: return SHA256HashSize; + case SHA384: return SHA384HashSize; + default: + case SHA512: return SHA512HashSize; + } +} + +/* +* USHAHashSizeBits +* +* Description: +* This function will return the hashsize for the given SHA +* algorithm, expressed in bits. +* +* Parameters: +* whichSha: +* which SHA algorithm to query +* +* Returns: +* hash size in bits +* +*/ +int USHAHashSizeBits(enum SHAversion whichSha) +{ + switch (whichSha) { + case SHA1: return SHA1HashSizeBits; + case SHA224: return SHA224HashSizeBits; + case SHA256: return SHA256HashSizeBits; + case SHA384: return SHA384HashSizeBits; + default: + case SHA512: return SHA512HashSizeBits; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/utf8_checker.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,177 @@ + // Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef __cplusplus +#include <cstdlib> +#include <cstddef> +#include <cstdint> +#else +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#endif + +#include "azure_c_shared_utility/utf8_checker.h" + +bool utf8_checker_is_valid_utf8(const unsigned char* utf8_str, size_t length) +{ + bool result; + + if (utf8_str == NULL) + { + /* Codes_SRS_UTF8_CHECKER_01_002: [ If `utf8_checker_is_valid_utf8` is called with NULL `utf8_str` it shall return false. ]*/ + result = false; + } + else + { + size_t pos = 0; + + /* Codes_SRS_UTF8_CHECKER_01_003: [ If `length` is 0, `utf8_checker_is_valid_utf8` shall consider `utf8_str` to be valid UTF-8 and return true. ]*/ + result = true; + + while ((result == true) && + (pos < length)) + { + /* Codes_SRS_UTF8_CHECKER_01_001: [ `utf8_checker_is_valid_utf8` shall verify that the sequence of chars pointed to by `utf8_str` represent UTF-8 encoded codepoints. ]*/ + if ((utf8_str[pos] >> 3) == 0x1E) + { + /* 4 bytes */ + /* Codes_SRS_UTF8_CHECKER_01_009: [ 000uuuuu zzzzyyyy yyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ]*/ + uint32_t code_point = (utf8_str[pos] & 0x07); + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + if (code_point <= 0xFFFF) + { + result = false; + } + else + { + /* Codes_SRS_UTF8_CHECKER_01_005: [ On success it shall return true. ]*/ + result = true; + pos++; + } + } + else + { + result = false; + } + } + else + { + result = false; + } + } + else + { + result = false; + } + } + else if ((utf8_str[pos] >> 4) == 0x0E) + { + /* 3 bytes */ + /* Codes_SRS_UTF8_CHECKER_01_008: [ zzzzyyyy yyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx ]*/ + uint32_t code_point = (utf8_str[pos] & 0x0F); + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + if (code_point <= 0x7FF) + { + result = false; + } + else + { + /* Codes_SRS_UTF8_CHECKER_01_005: [ On success it shall return true. ]*/ + result = true; + pos++; + } + } + else + { + result = false; + } + } + else + { + result = false; + } + } + else if ((utf8_str[pos] >> 5) == 0x06) + { + /* 2 bytes */ + /* Codes_SRS_UTF8_CHECKER_01_007: [ 00000yyy yyxxxxxx 110yyyyy 10xxxxxx ]*/ + uint32_t code_point = (utf8_str[pos] & 0x1F); + + pos++; + if ((pos < length) && + ((utf8_str[pos] >> 6) == 0x02)) + { + code_point <<= 6; + code_point += utf8_str[pos] & 0x3F; + + if (code_point <= 0x7F) + { + result = false; + } + else + { + /* Codes_SRS_UTF8_CHECKER_01_005: [ On success it shall return true. ]*/ + result = true; + pos++; + } + } + else + { + result = false; + } + } + else if ((utf8_str[pos] >> 7) == 0x00) + { + /* 1 byte */ + /* Codes_SRS_UTF8_CHECKER_01_006: [ 00000000 0xxxxxxx 0xxxxxxx ]*/ + /* Codes_SRS_UTF8_CHECKER_01_005: [ On success it shall return true. ]*/ + result = true; + pos++; + } + else + { + /* error */ + result = false; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/uuid.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdio.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/uuid.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +#define UUID_STRING_LENGTH 36 +#define UUID_STRING_SIZE (UUID_STRING_LENGTH + 1) +#define __SUCCESS__ 0 +#define UUID_FORMAT_STRING "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" + +int UUID_from_string(const char* uuid_string, UUID* uuid) +{ + int result; + + // Codes_SRS_UUID_09_007: [ If `uuid_string` or `uuid` are NULL, UUID_from_string shall return a non-zero value ] + if (uuid_string == NULL || uuid == NULL) + { + LogError("Invalid argument (uuid_string=%p, uuid=%p)", uuid_string, uuid); + result = __FAILURE__; + } + else + { + size_t uuid_string_length = strlen(uuid_string); + + if (uuid_string_length != UUID_STRING_LENGTH) + { + LogError("Unexpected size for an UUID string (%d)", uuid_string_length); + result = __FAILURE__; + } + else + { + // Codes_SRS_UUID_09_008: [ Each pair of digits in `uuid_string`, excluding dashes, shall be read as a single HEX value and saved on the respective position in `uuid` ] + size_t i, j; + unsigned char* uuid_bytes; + + uuid_bytes = (unsigned char*)uuid; + // Codes_SRS_UUID_09_010: [ If no failures occur, UUID_from_string shall return zero ] + result = __SUCCESS__; + + for (i = 0, j = 0; i < uuid_string_length; ) + { + if (uuid_string[i] == '-') + { + i++; + } + else + { + char double_hex_digit[3] = { 0, 0, 0 }; + + (void)memcpy(double_hex_digit, uuid_string + i, 2); + + if (sscanf(double_hex_digit, "%02hhx", uuid_bytes + j) != 1) + { + // Codes_SRS_UUID_09_009: [ If `uuid` fails to be generated, UUID_from_string shall return a non-zero value ] + LogError("Failed decoding UUID string (%d)", i); + result = __FAILURE__; + break; + } + else + { + i += 2; + j++; + } + } + } + } + } + + return result; +} + +char* UUID_to_string(UUID* uuid) +{ + char* result; + + // Codes_SRS_UUID_09_011: [ If `uuid` is NULL, UUID_to_string shall return a non-zero value ] + if (uuid == NULL) + { + LogError("Invalid argument (uuid is NULL)"); + result = NULL; + } + // Codes_SRS_UUID_09_012: [ UUID_to_string shall allocate a valid UUID string (`uuid_string`) as per RFC 4122 ] + else if ((result = (char*)malloc(sizeof(char) * UUID_STRING_SIZE)) == NULL) + { + // Codes_SRS_UUID_09_013: [ If `uuid_string` fails to be allocated, UUID_to_string shall return NULL ] + LogError("Failed allocating UUID string"); + } + else + { + unsigned char* uuid_bytes; + int number_of_chars_written; + + uuid_bytes = (unsigned char*)uuid; + + // Codes_SRS_UUID_09_014: [ Each character in `uuid` shall be written in the respective positions of `uuid_string` as a 2-digit HEX value ] + number_of_chars_written = sprintf(result, UUID_FORMAT_STRING, + uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3], + uuid_bytes[4], uuid_bytes[5], + uuid_bytes[6], uuid_bytes[7], + uuid_bytes[8], uuid_bytes[9], + uuid_bytes[10], uuid_bytes[11], uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]); + + if (number_of_chars_written != UUID_STRING_LENGTH) + { + // Tests_SRS_UUID_09_015: [ If `uuid_string` fails to be set, UUID_to_string shall return NULL ] + LogError("Failed encoding UUID string"); + free(result); + result = NULL; + } + } + + // Codes_SRS_UUID_09_016: [ If no failures occur, UUID_to_string shall return `uuid_string` ] + return result; +} + +int UUID_generate(UUID* uuid) +{ + int result; + + // Codes_SRS_UUID_09_001: [ If `uuid` is NULL, UUID_generate shall return a non-zero value ] + if (uuid == NULL) + { + LogError("Invalid argument (uuid is NULL)"); + result = __FAILURE__; + } + else + { + char* uuid_string; + + if ((uuid_string = (char*)malloc(sizeof(char) * UUID_STRING_SIZE)) == NULL) + { + // Codes_SRS_UUID_09_003: [ If the UUID string fails to be obtained, UUID_generate shall fail and return a non-zero value ] + LogError("Failed allocating UUID string"); + result = __FAILURE__; + } + else + { + memset(uuid_string, 0, sizeof(char) * UUID_STRING_SIZE); + + // Codes_SRS_UUID_09_002: [ UUID_generate shall obtain an UUID string from UniqueId_Generate ] + if (UniqueId_Generate(uuid_string, UUID_STRING_SIZE) != UNIQUEID_OK) + { + // Codes_SRS_UUID_09_003: [ If the UUID string fails to be obtained, UUID_generate shall fail and return a non-zero value ] + LogError("Failed generating UUID"); + result = __FAILURE__; + } + // Codes_SRS_UUID_09_004: [ The UUID string shall be parsed into an UUID type (16 unsigned char array) and filled in `uuid` ] + else if (UUID_from_string(uuid_string, uuid) != 0) + { + // Codes_SRS_UUID_09_005: [ If `uuid` fails to be set, UUID_generate shall fail and return a non-zero value ] + LogError("Failed parsing UUID string"); + result = __FAILURE__; + } + else + { + // Codes_SRS_UUID_09_006: [ If no failures occur, UUID_generate shall return zero ] + result = __SUCCESS__; + } + + free(uuid_string); + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/uws_client.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2112 @@ + // Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <ctype.h> +#include <limits.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/uws_client.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/socketio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/uws_frame_encoder.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/utf8_checker.h" +#include "azure_c_shared_utility/gb_rand.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/optionhandler.h" + +static const char* UWS_CLIENT_OPTIONS = "uWSClientOptions"; + +/* Requirements not needed as they are optional: +Codes_SRS_UWS_CLIENT_01_254: [ If an endpoint receives a Ping frame and has not yet sent Pong frame(s) in response to previous Ping frame(s), the endpoint MAY elect to send a Pong frame for only the most recently processed Ping frame. ] +Codes_SRS_UWS_CLIENT_01_255: [ A Pong frame MAY be sent unsolicited. ] +Codes_SRS_UWS_CLIENT_01_256: [ A response to an unsolicited Pong frame is not expected. ] +*/ + +/* Requirements satisfied by the underlying TLS/socket stack +Codes_SRS_UWS_CLIENT_01_362: [ To achieve reasonable levels of protection, clients should use only Strong TLS algorithms. ] +Codes_SRS_UWS_CLIENT_01_289: [ An endpoint SHOULD use a method that cleanly closes the TCP connection, as well as the TLS session, if applicable, discarding any trailing bytes that may have been received. ] +Codes_SRS_UWS_CLIENT_01_078: [ Otherwise, all further communication on this channel MUST run through the encrypted tunnel [RFC5246]. ] +Codes_SRS_UWS_CLIENT_01_141: [ masking is done whether or not the WebSocket Protocol is running over TLS. ] +*/ + +/* Requirements satisfied by the way the APIs are designed: +Codes_SRS_UWS_CLIENT_01_211: [One implication of this is that in absence of extensions, senders and receivers must not depend on the presence of specific frame boundaries.] +*/ + +typedef enum UWS_STATE_TAG +{ + UWS_STATE_CLOSED, + UWS_STATE_OPENING_UNDERLYING_IO, + UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE, + UWS_STATE_OPEN, + UWS_STATE_CLOSING_WAITING_FOR_CLOSE, + UWS_STATE_CLOSING_SENDING_CLOSE, + UWS_STATE_CLOSING_UNDERLYING_IO, + UWS_STATE_ERROR +} UWS_STATE; + +typedef struct WS_INSTANCE_PROTOCOL_TAG +{ + char* protocol; +} WS_INSTANCE_PROTOCOL; + +typedef struct WS_PENDING_SEND_TAG +{ + ON_WS_SEND_FRAME_COMPLETE on_ws_send_frame_complete; + void* context; + UWS_CLIENT_HANDLE uws_client; +} WS_PENDING_SEND; + +typedef struct UWS_CLIENT_INSTANCE_TAG +{ + SINGLYLINKEDLIST_HANDLE pending_sends; + XIO_HANDLE underlying_io; + char* hostname; + char* resource_name; + WS_INSTANCE_PROTOCOL* protocols; + size_t protocol_count; + int port; + UWS_STATE uws_state; + ON_WS_OPEN_COMPLETE on_ws_open_complete; + void* on_ws_open_complete_context; + ON_WS_FRAME_RECEIVED on_ws_frame_received; + void* on_ws_frame_received_context; + ON_WS_PEER_CLOSED on_ws_peer_closed; + void* on_ws_peer_closed_context; + ON_WS_ERROR on_ws_error; + void* on_ws_error_context; + ON_WS_CLOSE_COMPLETE on_ws_close_complete; + void* on_ws_close_complete_context; + unsigned char* stream_buffer; + size_t stream_buffer_count; + unsigned char* fragment_buffer; + size_t fragment_buffer_count; + unsigned char fragmented_frame_type; +} UWS_CLIENT_INSTANCE; + +/* Codes_SRS_UWS_CLIENT_01_360: [ Connection confidentiality and integrity is provided by running the WebSocket Protocol over TLS (wss URIs). ]*/ +/* Codes_SRS_UWS_CLIENT_01_361: [ WebSocket implementations MUST support TLS and SHOULD employ it when communicating with their peers. ]*/ +/* Codes_SRS_UWS_CLIENT_01_063: [ A client will need to supply a /host/, /port/, /resource name/, and a /secure/ flag, which are the components of a WebSocket URI as discussed in Section 3, along with a list of /protocols/ and /extensions/ to be used. ]*/ +UWS_CLIENT_HANDLE uws_client_create(const char* hostname, unsigned int port, const char* resource_name, bool use_ssl, const WS_PROTOCOL* protocols, size_t protocol_count) +{ + UWS_CLIENT_HANDLE result; + + /* Codes_SRS_UWS_CLIENT_01_002: [ If any of the arguments `hostname` and `resource_name` is NULL then `uws_client_create` shall return NULL. ]*/ + if ((hostname == NULL) || + (resource_name == NULL) || + /* Codes_SRS_UWS_CLIENT_01_411: [ If `protocol_count` is non zero and `protocols` is NULL then `uws_client_create` shall fail and return NULL. ]*/ + ((protocols == NULL) && (protocol_count > 0))) + { + LogError("Invalid arguments: hostname = %p, resource_name = %p, protocols = %p, protocol_count = %lu", hostname, resource_name, protocols, protocol_count); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_412: [ If the `protocol` member of any of the items in the `protocols` argument is NULL, then `uws_client_create` shall fail and return NULL. ]*/ + size_t i; + for (i = 0; i < protocol_count; i++) + { + if (protocols[i].protocol == NULL) + { + break; + } + } + + if (i < protocol_count) + { + LogError("Protocol index %lu has NULL name", i); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_001: [`uws_client_create` shall create an instance of uws and return a non-NULL handle to it.]*/ + result = (UWS_CLIENT_HANDLE)malloc(sizeof(UWS_CLIENT_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_003: [ If allocating memory for the new uws instance fails then `uws_client_create` shall return NULL. ]*/ + LogError("Could not allocate uWS instance"); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_004: [ The argument `hostname` shall be copied for later use. ]*/ + if (mallocAndStrcpy_s(&result->hostname, hostname) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_392: [ If allocating memory for the copy of the `hostname` argument fails, then `uws_client_create` shall return NULL. ]*/ + LogError("Could not copy hostname."); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_404: [ The argument `resource_name` shall be copied for later use. ]*/ + if (mallocAndStrcpy_s(&result->resource_name, resource_name) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_405: [ If allocating memory for the copy of the `resource` argument fails, then `uws_client_create` shall return NULL. ]*/ + LogError("Could not copy resource."); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_017: [ `uws_client_create` shall create a pending send IO list that is to be used to queue send packets by calling `singlylinkedlist_create`. ]*/ + result->pending_sends = singlylinkedlist_create(); + if (result->pending_sends == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_018: [ If `singlylinkedlist_create` fails then `uws_client_create` shall fail and return NULL. ]*/ + LogError("Could not allocate pending send frames list"); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + if (use_ssl == true) + { + TLSIO_CONFIG tlsio_config; + + /* Codes_SRS_UWS_CLIENT_01_006: [ If `use_ssl` is true then `uws_client_create` shall obtain the interface used to create a tlsio instance by calling `platform_get_default_tlsio`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_076: [ If /secure/ is true, the client MUST perform a TLS handshake over the connection after opening the connection and before sending the handshake data [RFC2818]. ]*/ + const IO_INTERFACE_DESCRIPTION* tlsio_interface = platform_get_default_tlsio(); + if (tlsio_interface == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_007: [ If obtaining the underlying IO interface fails, then `uws_client_create` shall fail and return NULL. ]*/ + LogError("NULL TLSIO interface description"); + result->underlying_io = NULL; + } + else + { + SOCKETIO_CONFIG socketio_config; + + /* Codes_SRS_UWS_CLIENT_01_013: [ The create arguments for the tls IO (when `use_ssl` is 1) shall have: ]*/ + /* Codes_SRS_UWS_CLIENT_01_014: [ - `hostname` set to the `hostname` argument passed to `uws_client_create`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_015: [ - `port` set to the `port` argument passed to `uws_client_create`. ]*/ + socketio_config.hostname = hostname; + socketio_config.port = port; + socketio_config.accepted_socket = NULL; + + tlsio_config.hostname = hostname; + tlsio_config.port = port; + tlsio_config.underlying_io_interface = socketio_get_interface_description(); + tlsio_config.underlying_io_parameters = &socketio_config; + + result->underlying_io = xio_create(tlsio_interface, &tlsio_config); + if (result->underlying_io == NULL) + { + LogError("Cannot create underlying TLS IO."); + } + } + } + else + { + SOCKETIO_CONFIG socketio_config; + /* Codes_SRS_UWS_CLIENT_01_005: [ If `use_ssl` is false then `uws_client_create` shall obtain the interface used to create a socketio instance by calling `socketio_get_interface_description`. ]*/ + const IO_INTERFACE_DESCRIPTION* socketio_interface = socketio_get_interface_description(); + if (socketio_interface == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_007: [ If obtaining the underlying IO interface fails, then `uws_client_create` shall fail and return NULL. ]*/ + LogError("NULL socketio interface description"); + result->underlying_io = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_010: [ The create arguments for the socket IO (when `use_ssl` is 0) shall have: ]*/ + /* Codes_SRS_UWS_CLIENT_01_011: [ - `hostname` set to the `hostname` argument passed to `uws_client_create`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_012: [ - `port` set to the `port` argument passed to `uws_client_create`. ]*/ + socketio_config.hostname = hostname; + socketio_config.port = port; + socketio_config.accepted_socket = NULL; + + /* Codes_SRS_UWS_CLIENT_01_008: [ The obtained interface shall be used to create the IO used as underlying IO by the newly created uws instance. ]*/ + /* Codes_SRS_UWS_CLIENT_01_009: [ The underlying IO shall be created by calling `xio_create`. ]*/ + result->underlying_io = xio_create(socketio_interface, &socketio_config); + if (result->underlying_io == NULL) + { + LogError("Cannot create underlying socket IO."); + } + } + } + + if (result->underlying_io == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_016: [ If `xio_create` fails, then `uws_client_create` shall fail and return NULL. ]*/ + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->uws_state = UWS_STATE_CLOSED; + /* Codes_SRS_UWS_CLIENT_01_403: [ The argument `port` shall be copied for later use. ]*/ + result->port = port; + + result->on_ws_open_complete = NULL; + result->on_ws_open_complete_context = NULL; + result->on_ws_frame_received = NULL; + result->on_ws_frame_received_context = NULL; + result->on_ws_error = NULL; + result->on_ws_error_context = NULL; + result->on_ws_close_complete = NULL; + result->on_ws_close_complete_context = NULL; + result->stream_buffer = NULL; + result->stream_buffer_count = 0; + result->fragment_buffer = NULL; + result->fragment_buffer_count = 0; + result->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN; + + result->protocol_count = protocol_count; + + /* Codes_SRS_UWS_CLIENT_01_410: [ The `protocols` argument shall be allowed to be NULL, in which case no protocol is to be specified by the client in the upgrade request. ]*/ + if (protocols == NULL) + { + result->protocols = NULL; + } + else + { + result->protocols = (WS_INSTANCE_PROTOCOL*)malloc(sizeof(WS_INSTANCE_PROTOCOL) * protocol_count); + if (result->protocols == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_414: [ If allocating memory for the copied protocol information fails then `uws_client_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for the protocols array."); + xio_destroy(result->underlying_io); + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_413: [ The protocol information indicated by `protocols` and `protocol_count` shall be copied for later use (for constructing the upgrade request). ]*/ + for (i = 0; i < protocol_count; i++) + { + if (mallocAndStrcpy_s(&result->protocols[i].protocol, protocols[i].protocol) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_414: [ If allocating memory for the copied protocol information fails then `uws_client_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for the protocol index %u.", (unsigned int)i); + break; + } + } + + if (i < protocol_count) + { + size_t j; + + for (j = 0; j < i; j++) + { + free(result->protocols[j].protocol); + } + + free(result->protocols); + xio_destroy(result->underlying_io); + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->protocol_count = protocol_count; + } + } + } + } + } + } + } + } + } + } + + return result; +} + +UWS_CLIENT_HANDLE uws_client_create_with_io(const IO_INTERFACE_DESCRIPTION* io_interface, void* io_create_parameters, const char* hostname, unsigned int port, const char* resource_name, const WS_PROTOCOL* protocols, size_t protocol_count) +{ + UWS_CLIENT_HANDLE result; + + /* Codes_SRS_UWS_CLIENT_01_516: [ If any of the arguments `io_interface`, `hostname` and `resource_name` is NULL then `uws_client_create_with_io` shall return NULL. ]*/ + if ((hostname == NULL) || + (io_interface == NULL) || + (resource_name == NULL) || + /* Codes_SRS_UWS_CLIENT_01_525: [ If `protocol_count` is non zero and `protocols` is NULL then `uws_client_create_with_io` shall fail and return NULL. ]*/ + ((protocols == NULL) && (protocol_count > 0))) + { + LogError("Invalid arguments: io_interface = %p, resource_name = %p, protocols = %p, protocol_count = %lu", io_interface, resource_name, protocols, protocol_count); + result = NULL; + } + else + { + size_t i; + for (i = 0; i < protocol_count; i++) + { + if (protocols[i].protocol == NULL) + { + break; + } + } + + if (i < protocol_count) + { + /* Codes_SRS_UWS_CLIENT_01_526: [ If the `protocol` member of any of the items in the `protocols` argument is NULL, then `uws_client_create_with_io` shall fail and return NULL. ]*/ + LogError("Protocol index %lu has NULL name", i); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_515: [ `uws_client_create_with_io` shall create an instance of uws and return a non-NULL handle to it. ]*/ + result = (UWS_CLIENT_HANDLE)malloc(sizeof(UWS_CLIENT_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_517: [ If allocating memory for the new uws instance fails then `uws_client_create_with_io` shall return NULL. ]*/ + LogError("Could not allocate uWS instance"); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_518: [ The argument `hostname` shall be copied for later use. ]*/ + if (mallocAndStrcpy_s(&result->hostname, hostname) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_519: [ If allocating memory for the copy of the `hostname` argument fails, then `uws_client_create` shall return NULL. ]*/ + LogError("Could not copy hostname."); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_523: [ The argument `resource_name` shall be copied for later use. ]*/ + if (mallocAndStrcpy_s(&result->resource_name, resource_name) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_529: [ If allocating memory for the copy of the `resource_name` argument fails, then `uws_client_create_with_io` shall return NULL. ]*/ + LogError("Could not copy resource."); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_530: [ `uws_client_create_with_io` shall create a pending send IO list that is to be used to queue send packets by calling `singlylinkedlist_create`. ]*/ + result->pending_sends = singlylinkedlist_create(); + if (result->pending_sends == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_531: [ If `singlylinkedlist_create` fails then `uws_client_create_with_io` shall fail and return NULL. ]*/ + LogError("Could not allocate pending send frames list"); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_521: [ The underlying IO shall be created by calling `xio_create`, while passing as arguments the `io_interface` and `io_create_parameters` argument values. ]*/ + result->underlying_io = xio_create(io_interface, io_create_parameters); + if (result->underlying_io == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_522: [ If `xio_create` fails, then `uws_client_create_with_io` shall fail and return NULL. ]*/ + LogError("Cannot create underlying IO."); + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->uws_state = UWS_STATE_CLOSED; + + /* Codes_SRS_UWS_CLIENT_01_520: [ The argument `port` shall be copied for later use. ]*/ + result->port = port; + + result->on_ws_open_complete = NULL; + result->on_ws_open_complete_context = NULL; + result->on_ws_frame_received = NULL; + result->on_ws_frame_received_context = NULL; + result->on_ws_error = NULL; + result->on_ws_error_context = NULL; + result->on_ws_close_complete = NULL; + result->on_ws_close_complete_context = NULL; + result->stream_buffer = NULL; + result->stream_buffer_count = 0; + result->fragment_buffer = NULL; + result->fragment_buffer_count = 0; + result->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN; + + result->protocol_count = protocol_count; + + /* Codes_SRS_UWS_CLIENT_01_524: [ The `protocols` argument shall be allowed to be NULL, in which case no protocol is to be specified by the client in the upgrade request. ]*/ + if (protocols == NULL) + { + result->protocols = NULL; + } + else + { + result->protocols = (WS_INSTANCE_PROTOCOL*)malloc(sizeof(WS_INSTANCE_PROTOCOL) * protocol_count); + if (result->protocols == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_414: [ If allocating memory for the copied protocol information fails then `uws_client_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for the protocols array."); + xio_destroy(result->underlying_io); + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_527: [ The protocol information indicated by `protocols` and `protocol_count` shall be copied for later use (for constructing the upgrade request). ]*/ + for (i = 0; i < protocol_count; i++) + { + if (mallocAndStrcpy_s(&result->protocols[i].protocol, protocols[i].protocol) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_528: [ If allocating memory for the copied protocol information fails then `uws_client_create_with_io` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for the protocol index %u.", (unsigned int)i); + break; + } + } + + if (i < protocol_count) + { + size_t j; + + for (j = 0; j < i; j++) + { + free(result->protocols[j].protocol); + } + + free(result->protocols); + xio_destroy(result->underlying_io); + singlylinkedlist_destroy(result->pending_sends); + free(result->resource_name); + free(result->hostname); + free(result); + result = NULL; + } + else + { + result->protocol_count = protocol_count; + } + } + } + } + } + } + } + } + } + } + + return result; +} + +void uws_client_destroy(UWS_CLIENT_HANDLE uws_client) +{ + /* Codes_SRS_UWS_CLIENT_01_020: [ If `uws_client` is NULL, `uws_client_destroy` shall do nothing. ]*/ + if (uws_client == NULL) + { + LogError("NULL uws handle"); + } + else + { + free(uws_client->stream_buffer); + free(uws_client->fragment_buffer); + + /* Codes_SRS_UWS_CLIENT_01_021: [ `uws_client_destroy` shall perform a close action if the uws instance has already been open. ]*/ + switch (uws_client->uws_state) + { + default: + break; + + case UWS_STATE_OPEN: + case UWS_STATE_ERROR: + uws_client_close_async(uws_client, NULL, NULL); + break; + } + + if (uws_client->protocol_count > 0) + { + size_t i; + + /* Codes_SRS_UWS_CLIENT_01_437: [ `uws_client_destroy` shall free the protocols array allocated in `uws_client_create`. ]*/ + for (i = 0; i < uws_client->protocol_count; i++) + { + free(uws_client->protocols[i].protocol); + } + + free(uws_client->protocols); + } + + /* Codes_SRS_UWS_CLIENT_01_019: [ `uws_client_destroy` shall free all resources associated with the uws instance. ]*/ + /* Codes_SRS_UWS_CLIENT_01_023: [ `uws_client_destroy` shall ensure the underlying IO created in `uws_client_open_async` is destroyed by calling `xio_destroy`. ]*/ + if (uws_client->underlying_io != NULL) + { + xio_destroy(uws_client->underlying_io); + uws_client->underlying_io = NULL; + } + + /* Codes_SRS_UWS_CLIENT_01_024: [ `uws_client_destroy` shall free the list used to track the pending sends by calling `singlylinkedlist_destroy`. ]*/ + singlylinkedlist_destroy(uws_client->pending_sends); + free(uws_client->resource_name); + free(uws_client->hostname); + free(uws_client); + } +} + +static void indicate_ws_open_complete_error(UWS_CLIENT_INSTANCE* uws_client, WS_OPEN_RESULT ws_open_result) +{ + /* Codes_SRS_UWS_CLIENT_01_409: [ After any error is indicated by `on_ws_open_complete`, a subsequent `uws_client_open_async` shall be possible. ]*/ + uws_client->uws_state = UWS_STATE_CLOSED; + uws_client->on_ws_open_complete(uws_client->on_ws_open_complete_context, ws_open_result); +} + +static void indicate_ws_open_complete_error_and_close(UWS_CLIENT_INSTANCE* uws_client, WS_OPEN_RESULT ws_open_result) +{ + (void)xio_close(uws_client->underlying_io, NULL, NULL); + indicate_ws_open_complete_error(uws_client, ws_open_result); +} + +static void indicate_ws_error(UWS_CLIENT_INSTANCE* uws_client, WS_ERROR error_code) +{ + uws_client->uws_state = UWS_STATE_ERROR; + uws_client->on_ws_error(uws_client->on_ws_error_context, error_code); +} + +static void indicate_ws_close_complete(UWS_CLIENT_INSTANCE* uws_client) +{ + uws_client->uws_state = UWS_STATE_CLOSED; + + /* Codes_SRS_UWS_CLIENT_01_496: [ If the close was initiated by the peer no `on_ws_close_complete` shall be called. ]*/ + if (uws_client->on_ws_close_complete != NULL) + { + /* Codes_SRS_UWS_CLIENT_01_491: [ When calling `on_ws_close_complete` callback, the `on_ws_close_complete_context` argument shall be passed to it. ]*/ + uws_client->on_ws_close_complete(uws_client->on_ws_close_complete_context); + } +} + +// This callback usage needs to be either verified and commented or integrated into +// the state machine. +static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + (void)context; + (void)send_result; +} + +static int send_close_frame(UWS_CLIENT_INSTANCE* uws_client, unsigned int close_error_code) +{ + unsigned char* close_frame; + unsigned char close_frame_payload[2]; + size_t close_frame_length; + int result; + BUFFER_HANDLE close_frame_buffer; + + close_frame_payload[0] = (unsigned char)(close_error_code >> 8); + close_frame_payload[1] = (unsigned char)(close_error_code & 0xFF); + + /* Codes_SRS_UWS_CLIENT_01_140: [ To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons that are further discussed in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). ]*/ + close_frame_buffer = uws_frame_encoder_encode(WS_CLOSE_FRAME, close_frame_payload, sizeof(close_frame_payload), true, true, 0); + if (close_frame_buffer == NULL) + { + LogError("Encoding of CLOSE failed."); + result = __FAILURE__; + } + else + { + close_frame = BUFFER_u_char(close_frame_buffer); + close_frame_length = BUFFER_length(close_frame_buffer); + + /* Codes_SRS_UWS_CLIENT_01_471: [ The callback `on_underlying_io_close_sent` shall be passed as argument to `xio_send`. ]*/ + if (xio_send(uws_client->underlying_io, close_frame, close_frame_length, unchecked_on_send_complete, NULL) != 0) + { + LogError("Sending CLOSE frame failed."); + result = __FAILURE__; + } + else + { + result = 0; + } + + BUFFER_delete(close_frame_buffer); + } + + return result; +} + +static void indicate_ws_error_and_close(UWS_CLIENT_INSTANCE* uws_client, WS_ERROR error_code, unsigned int close_error_code) +{ + uws_client->uws_state = UWS_STATE_ERROR; + + (void)send_close_frame(uws_client, close_error_code); + + uws_client->on_ws_error(uws_client->on_ws_error_context, error_code); +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + UWS_CLIENT_HANDLE uws_client = (UWS_CLIENT_HANDLE)context; + /* Codes_SRS_UWS_CLIENT_01_401: [ If `on_underlying_io_open_complete` is called with a NULL context, `on_underlying_io_open_complete` shall do nothing. ]*/ + if (uws_client == NULL) + { + LogError("NULL context"); + } + else + { + switch (uws_client->uws_state) + { + default: + case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE: + /* Codes_SRS_UWS_CLIENT_01_407: [ When `on_underlying_io_open_complete` is called when the uws instance has send the upgrade request but it is waiting for the response, an error shall be reported to the user by calling the `on_ws_open_complete` with `WS_OPEN_ERROR_MULTIPLE_UNDERLYING_IO_OPEN_EVENTS`. ]*/ + LogError("underlying on_io_open_complete was called again after upgrade request was sent."); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_MULTIPLE_UNDERLYING_IO_OPEN_EVENTS); + break; + case UWS_STATE_OPENING_UNDERLYING_IO: + switch (open_result) + { + default: + case IO_OPEN_ERROR: + /* Codes_SRS_UWS_CLIENT_01_369: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_ERROR` while uws is OPENING (`uws_client_open_async` was called), uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_UNDERLYING_IO_OPEN_FAILED`. ]*/ + indicate_ws_open_complete_error(uws_client, WS_OPEN_ERROR_UNDERLYING_IO_OPEN_FAILED); + break; + + case IO_OPEN_CANCELLED: + /* Codes_SRS_UWS_CLIENT_01_402: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_CANCELLED` while uws is OPENING (`uws_client_open_async` was called), uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_UNDERLYING_IO_OPEN_CANCELLED`. ]*/ + indicate_ws_open_complete_error(uws_client, WS_OPEN_ERROR_UNDERLYING_IO_OPEN_CANCELLED); + break; + + case IO_OPEN_OK: + { + int upgrade_request_length; + char* upgrade_request; + size_t i; + unsigned char nonce[16]; + STRING_HANDLE base64_nonce; + + /* Codes_SRS_UWS_CLIENT_01_089: [ The value of this header field MUST be a nonce consisting of a randomly selected 16-byte value that has been base64-encoded (see Section 4 of [RFC4648]). ]*/ + /* Codes_SRS_UWS_CLIENT_01_090: [ The nonce MUST be selected randomly for each connection. ]*/ + for (i = 0; i < sizeof(nonce); i++) + { + nonce[i] = (unsigned char)gb_rand(); + } + + /* Codes_SRS_UWS_CLIENT_01_497: [ The nonce needed for the upgrade request shall be Base64 encoded with `Base64_Encode_Bytes`. ]*/ + base64_nonce = Base64_Encode_Bytes(nonce, sizeof(nonce)); + if (base64_nonce == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_498: [ If Base64 encoding the nonce for the upgrade request fails, then the uws client shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BASE64_ENCODE_FAILED`. ]*/ + LogError("Cannot construct the WebSocket upgrade request"); + indicate_ws_open_complete_error(uws_client, WS_OPEN_ERROR_BASE64_ENCODE_FAILED); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_371: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_OK` while uws is OPENING (`uws_client_open_async` was called), uws shall prepare the WebSockets upgrade request. ]*/ + /* Codes_SRS_UWS_CLIENT_01_081: [ The handshake consists of an HTTP Upgrade request, along with a list of required and optional header fields. ]*/ + /* Codes_SRS_UWS_CLIENT_01_082: [ The handshake MUST be a valid HTTP request as specified by [RFC2616]. ]*/ + /* Codes_SRS_UWS_CLIENT_01_083: [ The method of the request MUST be GET, and the HTTP version MUST be at least 1.1. ]*/ + /* Codes_SRS_UWS_CLIENT_01_084: [ The "Request-URI" part of the request MUST match the /resource name/ defined in Section 3 (a relative URI) or be an absolute http/https URI that, when parsed, has a /resource name/, /host/, and /port/ that match the corresponding ws/wss URI. ]*/ + /* Codes_SRS_UWS_CLIENT_01_085: [ The request MUST contain a |Host| header field whose value contains /host/ plus optionally ":" followed by /port/ (when not using the default port). ]*/ + /* Codes_SRS_UWS_CLIENT_01_086: [ The request MUST contain an |Upgrade| header field whose value MUST include the "websocket" keyword. ]*/ + /* Codes_SRS_UWS_CLIENT_01_087: [ The request MUST contain a |Connection| header field whose value MUST include the "Upgrade" token. ]*/ + /* Codes_SRS_UWS_CLIENT_01_088: [ The request MUST include a header field with the name |Sec-WebSocket-Key|. ]*/ + /* Codes_SRS_UWS_CLIENT_01_094: [ The request MUST include a header field with the name |Sec-WebSocket-Version|. ]*/ + /* Codes_SRS_UWS_CLIENT_01_095: [ The value of this header field MUST be 13. ]*/ + /* Codes_SRS_UWS_CLIENT_01_096: [ The request MAY include a header field with the name |Sec-WebSocket-Protocol|. ]*/ + /* Codes_SRS_UWS_CLIENT_01_100: [ The request MAY include a header field with the name |Sec-WebSocket-Extensions|. ]*/ + const char upgrade_request_format[] = "GET %s HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: %s\r\n" + "Sec-WebSocket-Protocol: %s\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + const char* base64_nonce_chars = STRING_c_str(base64_nonce); + + upgrade_request_length = (int)(strlen(upgrade_request_format) + strlen(uws_client->resource_name)+strlen(uws_client->hostname) + strlen(base64_nonce_chars) + strlen(uws_client->protocols[0].protocol)+5); + if (upgrade_request_length < 0) + { + /* Codes_SRS_UWS_CLIENT_01_408: [ If constructing of the WebSocket upgrade request fails, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_CONSTRUCTING_UPGRADE_REQUEST`. ]*/ + LogError("Cannot construct the WebSocket upgrade request"); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_CONSTRUCTING_UPGRADE_REQUEST); + } + else + { + upgrade_request = (char*)malloc(upgrade_request_length + 1); + if (upgrade_request == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_406: [ If not enough memory can be allocated to construct the WebSocket upgrade request, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_NOT_ENOUGH_MEMORY`. ]*/ + LogError("Cannot allocate memory for the WebSocket upgrade request"); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_NOT_ENOUGH_MEMORY); + } + else + { + + upgrade_request_length = sprintf(upgrade_request, upgrade_request_format, + uws_client->resource_name, + uws_client->hostname, + uws_client->port, + base64_nonce_chars, + uws_client->protocols[0].protocol); + + /* No need to have any send complete here, as we are monitoring the received bytes */ + /* Codes_SRS_UWS_CLIENT_01_372: [ Once prepared the WebSocket upgrade request shall be sent by calling `xio_send`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_080: [ Once a connection to the server has been established (including a connection via a proxy or over a TLS-encrypted tunnel), the client MUST send an opening handshake to the server. ]*/ + if (xio_send(uws_client->underlying_io, upgrade_request, upgrade_request_length, unchecked_on_send_complete, NULL) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_373: [ If `xio_send` fails then uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_CANNOT_SEND_UPGRADE_REQUEST`. ]*/ + LogError("Cannot send upgrade request"); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_CANNOT_SEND_UPGRADE_REQUEST); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_102: [ Once the client's opening handshake has been sent, the client MUST wait for a response from the server before sending any further data. ]*/ + uws_client->uws_state = UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE; + } + + free(upgrade_request); + } + } + + STRING_delete(base64_nonce); + } + + break; + } + } + } + } +} + +static void consume_stream_buffer_bytes(UWS_CLIENT_INSTANCE* uws_client, size_t consumed_bytes) +{ + if (consumed_bytes < uws_client->stream_buffer_count) + { + (void)memmove(uws_client->stream_buffer, uws_client->stream_buffer + consumed_bytes, uws_client->stream_buffer_count - consumed_bytes); + } + + uws_client->stream_buffer_count -= consumed_bytes; +} + +static void on_underlying_io_close_complete(void* context) +{ + if (context == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_477: [ When `on_underlying_io_close_complete` is called with a NULL context, it shall do nothing. ]*/ + LogError("NULL context for on_underlying_io_close_complete"); + } + else + { + UWS_CLIENT_HANDLE uws_client = (UWS_CLIENT_HANDLE)context; + if (uws_client->uws_state == UWS_STATE_CLOSING_UNDERLYING_IO) + { + /* Codes_SRS_UWS_CLIENT_01_475: [ When `on_underlying_io_close_complete` is called while closing the underlying IO a subsequent `uws_client_open_async` shall succeed. ]*/ + indicate_ws_close_complete(uws_client); + uws_client->uws_state = UWS_STATE_CLOSED; + } + } +} + +static void on_underlying_io_close_sent(void* context, IO_SEND_RESULT io_send_result) +{ + if (context == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_489: [ When `on_underlying_io_close_sent` is called with NULL context, it shall do nothing. ] */ + LogError("NULL context in "); + } + else + { + UWS_CLIENT_INSTANCE* uws_client = (UWS_CLIENT_HANDLE)context; + + switch (io_send_result) + { + case IO_SEND_OK: + case IO_SEND_CANCELLED: + if (uws_client->uws_state == UWS_STATE_CLOSING_SENDING_CLOSE) + { + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + + /* Codes_SRS_UWS_CLIENT_01_490: [ When `on_underlying_io_close_sent` is called while the uws client is CLOSING, `on_underlying_io_close_sent` shall close the underlying IO by calling `xio_close`. ]*/ + if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_496: [ If the close was initiated by the peer no `on_ws_close_complete` shall be called. ]*/ + indicate_ws_close_complete(uws_client); + } + } + + case IO_SEND_ERROR: + break; + } + } +} + +/*the following function does the same as sscanf(pos2, "%d", &sec)*/ +/*this function only exists because some of platforms do not have sscanf. */ +static int ParseStringToDecimal(const char *src, int* dst) +{ + int result; + char* next; + + (*dst) = (int)strtol(src, &next, 0); + if ((src == next) || ((((*dst) == INT_MAX) || ((*dst) == INT_MIN)) && (errno != 0))) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + return result; +} + +/*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */ +/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */ +static int ParseHttpResponse(const char* src, int* dst) +{ + int result; + static const char HTTPPrefix[] = "HTTP/"; + bool fail; + const char* runPrefix; + + if ((src == NULL) || (dst == NULL)) + { + result = __FAILURE__; + } + else + { + fail = false; + runPrefix = HTTPPrefix; + + while ((*runPrefix) != '\0') + { + if ((*runPrefix) != (*src)) + { + fail = true; + break; + } + src++; + runPrefix++; + } + + if (!fail) + { + while ((*src) != '.') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (!fail) + { + while ((*src) != ' ') + { + if ((*src) == '\0') + { + fail = true; + break; + } + src++; + } + } + + if (fail) + { + result = __FAILURE__; + } + else + { + if (ParseStringToDecimal(src, dst) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +static int process_frame_fragment(UWS_CLIENT_INSTANCE *uws_client, size_t length, size_t needed_bytes) +{ + int result; + unsigned char *new_fragment_bytes = (unsigned char *)realloc(uws_client->fragment_buffer, uws_client->fragment_buffer_count + length); + if (new_fragment_bytes == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_379: [ If allocating memory for accumulating the bytes fails, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_NOT_ENOUGH_MEMORY`. ]*/ + LogError("Cannot allocate memory for received data"); + indicate_ws_error(uws_client, WS_ERROR_NOT_ENOUGH_MEMORY); + result = __FAILURE__; + } + else + { + uws_client->fragment_buffer = new_fragment_bytes; + (void)memcpy(uws_client->fragment_buffer + uws_client->fragment_buffer_count, uws_client->stream_buffer + needed_bytes - length, length); + uws_client->fragment_buffer_count += length; + result = 0; + } + + return result; +} + +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + /* Codes_SRS_UWS_CLIENT_01_415: [ If called with a NULL `context` argument, `on_underlying_io_bytes_received` shall do nothing. ]*/ + if (context != NULL) + { + UWS_CLIENT_HANDLE uws_client = (UWS_CLIENT_HANDLE)context; + + if ((buffer == NULL) || + (size == 0)) + { + /* Codes_SRS_UWS_CLIENT_01_416: [ If called with NULL `buffer` or zero `size` and the state of the iws is OPENING, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_INVALID_BYTES_RECEIVED_ARGUMENTS`. ]*/ + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_INVALID_BYTES_RECEIVED_ARGUMENTS); + } + else + { + unsigned char decode_stream = 1; + + switch (uws_client->uws_state) + { + default: + case UWS_STATE_CLOSED: + decode_stream = 0; + break; + + case UWS_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_UWS_CLIENT_01_417: [ When `on_underlying_io_bytes_received` is called while OPENING but before the `on_underlying_io_open_complete` has been called, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN`. ]*/ + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN); + decode_stream = 0; + break; + + case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE: + { + /* Codes_SRS_UWS_CLIENT_01_378: [ When `on_underlying_io_bytes_received` is called while the uws is OPENING, the received bytes shall be accumulated in order to attempt parsing the WebSocket Upgrade response. ]*/ + unsigned char* new_received_bytes = (unsigned char*)realloc(uws_client->stream_buffer, uws_client->stream_buffer_count + size + 1); + if (new_received_bytes == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_379: [ If allocating memory for accumulating the bytes fails, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_NOT_ENOUGH_MEMORY`. ]*/ + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_NOT_ENOUGH_MEMORY); + decode_stream = 0; + } + else + { + uws_client->stream_buffer = new_received_bytes; + (void)memcpy(uws_client->stream_buffer + uws_client->stream_buffer_count, buffer, size); + uws_client->stream_buffer_count += size; + + decode_stream = 1; + } + + break; + } + + case UWS_STATE_OPEN: + case UWS_STATE_CLOSING_WAITING_FOR_CLOSE: + { + /* Codes_SRS_UWS_CLIENT_01_385: [ If the state of the uws instance is OPEN, the received bytes shall be used for decoding WebSocket frames. ]*/ + unsigned char* new_received_bytes = (unsigned char*)realloc(uws_client->stream_buffer, uws_client->stream_buffer_count + size + 1); + if (new_received_bytes == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_418: [ If allocating memory for the bytes accumulated for decoding WebSocket frames fails, an error shall be indicated by calling the `on_ws_error` callback with `WS_ERROR_NOT_ENOUGH_MEMORY`. ]*/ + LogError("Cannot allocate memory for received data"); + indicate_ws_error(uws_client, WS_ERROR_NOT_ENOUGH_MEMORY); + + decode_stream = 0; + } + else + { + uws_client->stream_buffer = new_received_bytes; + (void)memcpy(uws_client->stream_buffer + uws_client->stream_buffer_count, buffer, size); + uws_client->stream_buffer_count += size; + + decode_stream = 1; + } + + break; + } + } + + while (decode_stream) + { + decode_stream = 0; + + switch (uws_client->uws_state) + { + default: + case UWS_STATE_CLOSED: + break; + + case UWS_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_UWS_CLIENT_01_417: [ When `on_underlying_io_bytes_received` is called while OPENING but before the `on_underlying_io_open_complete` has been called, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN`. ]*/ + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN); + break; + + case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE: + { + const char* request_end_ptr; + + /* Make sure it is zero terminated */ + uws_client->stream_buffer[uws_client->stream_buffer_count] = '\0'; + + /* Codes_SRS_UWS_CLIENT_01_380: [ If an WebSocket Upgrade request can be parsed from the accumulated bytes, the status shall be read from the WebSocket upgrade response. ]*/ + /* Codes_SRS_UWS_CLIENT_01_381: [ If the status is 101, uws shall be considered OPEN and this shall be indicated by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `IO_OPEN_OK`. ]*/ + if ((uws_client->stream_buffer_count >= 4) && + ((request_end_ptr = strstr((const char*)uws_client->stream_buffer, "\r\n\r\n")) != NULL)) + { + int status_code; + + /* This part should really be done with the HTTPAPI, but that has to be done as a separate step + as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */ + + /* Codes_SRS_UWS_CLIENT_01_382: [ If a negative status is decoded from the WebSocket upgrade request, an error shall be indicated by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BAD_RESPONSE_STATUS`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_478: [ A Status-Line with a 101 response code as per RFC 2616 [RFC2616]. ]*/ + if (ParseHttpResponse((const char*)uws_client->stream_buffer, &status_code) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_383: [ If the WebSocket upgrade request cannot be decoded an error shall be indicated by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BAD_UPGRADE_RESPONSE`. ]*/ + LogError("Cannot decode HTTP response"); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BAD_UPGRADE_RESPONSE); + } + else if (status_code != 101) + { + /* Codes_SRS_UWS_CLIENT_01_382: [ If a negative status is decoded from the WebSocket upgrade request, an error shall be indicated by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_BAD_RESPONSE_STATUS`. ]*/ + LogError("Bad status (%d) received in WebSocket Upgrade response", status_code); + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BAD_RESPONSE_STATUS); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_384: [ Any extra bytes that are left unconsumed after decoding a succesfull WebSocket upgrade response shall be used for decoding WebSocket frames ]*/ + consume_stream_buffer_bytes(uws_client, request_end_ptr - (char*)uws_client->stream_buffer + 4); + + /* Codes_SRS_UWS_CLIENT_01_381: [ If the status is 101, uws shall be considered OPEN and this shall be indicated by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `IO_OPEN_OK`. ]*/ + uws_client->uws_state = UWS_STATE_OPEN; + + /* Codes_SRS_UWS_CLIENT_01_065: [ When the client is to _Establish a WebSocket Connection_ given a set of (/host/, /port/, /resource name/, and /secure/ flag), along with a list of /protocols/ and /extensions/ to be used, and an /origin/ in the case of web browsers, it MUST open a connection, send an opening handshake, and read the server's handshake in response. ]*/ + /* Codes_SRS_UWS_CLIENT_01_115: [ If the server's response is validated as provided for above, it is said that _The WebSocket Connection is Established_ and that the WebSocket Connection is in the OPEN state. ]*/ + uws_client->on_ws_open_complete(uws_client->on_ws_open_complete_context, WS_OPEN_OK); + + decode_stream = 1; + } + } + + break; + } + + case UWS_STATE_OPEN: + case UWS_STATE_CLOSING_WAITING_FOR_CLOSE: + { + size_t needed_bytes = 2; + size_t length; + + /* Codes_SRS_UWS_CLIENT_01_277: [ To receive WebSocket data, an endpoint listens on the underlying network connection. ]*/ + /* Codes_SRS_UWS_CLIENT_01_278: [ Incoming data MUST be parsed as WebSocket frames as defined in Section 5.2. ]*/ + if (uws_client->stream_buffer_count >= needed_bytes) + { + unsigned char has_error = 0; + + /* Codes_SRS_UWS_CLIENT_01_160: [ Defines whether the "Payload data" is masked. ]*/ + if ((uws_client->stream_buffer[1] & 0x80) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_144: [ A client MUST close a connection if it detects a masked frame. ]*/ + /* Codes_SRS_UWS_CLIENT_01_145: [ In this case, it MAY use the status code 1002 (protocol error) as defined in Section 7.4.1. (These rules might be relaxed in a future specification.) ]*/ + LogError("Masked frame detected by WebSocket client"); + indicate_ws_error_and_close(uws_client, WS_ERROR_BAD_FRAME_RECEIVED, 1002); + } + + /* Codes_SRS_UWS_CLIENT_01_163: [ The length of the "Payload data", in bytes: ]*/ + /* Codes_SRS_UWS_CLIENT_01_164: [ if 0-125, that is the payload length. ]*/ + length = uws_client->stream_buffer[1]; + + if (length == 126) + { + /* Codes_SRS_UWS_CLIENT_01_165: [ If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. ]*/ + needed_bytes += 2; + if (uws_client->stream_buffer_count >= needed_bytes) + { + /* Codes_SRS_UWS_CLIENT_01_167: [ Multibyte length quantities are expressed in network byte order. ]*/ + length = ((size_t)(uws_client->stream_buffer[2]) << 8) + (size_t)uws_client->stream_buffer[3]; + + if (length < 126) + { + /* Codes_SRS_UWS_CLIENT_01_168: [ Note that in all cases, the minimal number of bytes MUST be used to encode the length, for example, the length of a 124-byte-long string can't be encoded as the sequence 126, 0, 124. ]*/ + LogError("Bad frame: received a %u length on the 16 bit length", (unsigned int)length); + + /* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the `on_ws_error` callback with `WS_ERROR_BAD_FRAME_RECEIVED`. ]*/ + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + has_error = 1; + } + else + { + needed_bytes += (size_t)length; + } + } + } + else if (length == 127) + { + /* Codes_SRS_UWS_CLIENT_01_166: [ If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length. ]*/ + needed_bytes += 8; + if (uws_client->stream_buffer_count >= needed_bytes) + { + if ((uws_client->stream_buffer[2] & 0x80) != 0) + { + LogError("Bad frame: received a 64 bit length frame with the highest bit set"); + + /* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the `on_ws_error` callback with `WS_ERROR_BAD_FRAME_RECEIVED`. ]*/ + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + has_error = 1; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_167: [ Multibyte length quantities are expressed in network byte order. ]*/ + length = (size_t)(((uint64_t)(uws_client->stream_buffer[2]) << 56) + + (((uint64_t)uws_client->stream_buffer[3]) << 48) + + (((uint64_t)uws_client->stream_buffer[4]) << 40) + + (((uint64_t)uws_client->stream_buffer[5]) << 32) + + (((uint64_t)uws_client->stream_buffer[6]) << 24) + + (((uint64_t)uws_client->stream_buffer[7]) << 16) + + (((uint64_t)uws_client->stream_buffer[8]) << 8) + + (uint64_t)(uws_client->stream_buffer[9])); + + if (length < 65536) + { + /* Codes_SRS_UWS_CLIENT_01_168: [ Note that in all cases, the minimal number of bytes MUST be used to encode the length, for example, the length of a 124-byte-long string can't be encoded as the sequence 126, 0, 124. ]*/ + LogError("Bad frame: received a %u length on the 64 bit length", (unsigned int)length); + + /* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the `on_ws_error` callback with `WS_ERROR_BAD_FRAME_RECEIVED`. ]*/ + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + has_error = 1; + } + else + { + needed_bytes += length; + } + } + } + } + else + { + needed_bytes += length; + } + + if ((has_error == 0) && + (uws_client->stream_buffer_count >= needed_bytes)) + { + unsigned char opcode = uws_client->stream_buffer[0] & 0xF; + + /* Codes_SRS_UWS_CLIENT_01_147: [ Indicates that this is the final fragment in a message. ]*/ + bool is_final = (uws_client->stream_buffer[0] & 0x80) != 0; + + switch (opcode) + { + default: + break; + /* Codes_SRS_UWS_CLIENT_01_152: [* * %x0 denotes a continuation frame *]*/ + case (unsigned char)WS_CONTINUATION_FRAME: + { + /* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/ + /* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/ + /* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/ + if (process_frame_fragment(uws_client, length, needed_bytes) != 0) + { + break; + } + + if (is_final) + { + /* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/ + if (uws_client->fragmented_frame_type == WS_FRAME_TYPE_UNKNOWN) + { + LogError("Continuation fragment received without initial fragment specifying frame data type"); + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + decode_stream = 1; + break; + } + uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, uws_client->fragmented_frame_type, uws_client->fragment_buffer, uws_client->fragment_buffer_count); + uws_client->fragment_buffer_count = 0; + uws_client->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN; + } + decode_stream = 1; + break; + } + /* Codes_SRS_UWS_CLIENT_01_153: [ * %x1 denotes a text frame ]*/ + /* Codes_SRS_UWS_CLIENT_01_258: [** Currently defined opcodes for data frames include 0x1 (Text), 0x2 (Binary). ]*/ + case (unsigned char)WS_TEXT_FRAME: + { + /* Codes_SRS_UWS_CLIENT_01_386: [ When a WebSocket data frame is decoded succesfully it shall be indicated via the callback `on_ws_frame_received`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_169: [ The payload length is the length of the "Extension data" + the length of the "Application data". ]*/ + /* Codes_SRS_UWS_CLIENT_01_173: [ The "Payload data" is defined as "Extension data" concatenated with "Application data". ]*/ + /* Codes_SRS_UWS_CLIENT_01_280: [ Upon receiving a data frame (Section 5.6), the endpoint MUST note the /type/ of the data as defined by the opcode (frame-opcode) from Section 5.2. ]*/ + /* Codes_SRS_UWS_CLIENT_01_281: [ The "Application data" from this frame is defined as the /data/ of the message. ]*/ + /* Codes_SRS_UWS_CLIENT_01_282: [ If the frame comprises an unfragmented message (Section 5.4), it is said that _A WebSocket Message Has Been Received_ with type /type/ and data /data/. ]*/ + if (is_final) + { + uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, WS_FRAME_TYPE_TEXT, uws_client->stream_buffer + needed_bytes - length, length); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_217: [ The fragments of one message MUST NOT be interleaved between the fragments of another message unless an extension has been negotiated that can interpret the interleaving. ]*/ + if (uws_client->fragmented_frame_type != WS_FRAME_TYPE_UNKNOWN) + { + LogError("Fragmented frame received interleaved between the fragments of another message"); + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + decode_stream = 1; + break; + } + /* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/ + /* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/ + /* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/ + if (process_frame_fragment(uws_client, length, needed_bytes) != 0) + { + break; + } + + /* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/ + /* Codes_SRS_UWS_CLIENT_01_226: [ Since control frames cannot be fragmented, the type for all fragments in a message MUST be either text, binary, or one of the reserved opcodes. ]*/ + uws_client->fragmented_frame_type = WS_FRAME_TYPE_TEXT; + } + decode_stream = 1; + break; + } + + /* Codes_SRS_UWS_CLIENT_01_154: [ * %x2 denotes a binary frame ]*/ + case (unsigned char)WS_BINARY_FRAME: + { + /* Codes_SRS_UWS_CLIENT_01_386: [ When a WebSocket data frame is decoded succesfully it shall be indicated via the callback `on_ws_frame_received`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_169: [ The payload length is the length of the "Extension data" + the length of the "Application data". ]*/ + /* Codes_SRS_UWS_CLIENT_01_173: [ The "Payload data" is defined as "Extension data" concatenated with "Application data". ]*/ + /* Codes_SRS_UWS_CLIENT_01_264: [ The "Payload data" is arbitrary binary data whose interpretation is solely up to the application layer. ]*/ + /* Codes_SRS_UWS_CLIENT_01_280: [ Upon receiving a data frame (Section 5.6), the endpoint MUST note the /type/ of the data as defined by the opcode (frame-opcode) from Section 5.2. ]*/ + /* Codes_SRS_UWS_CLIENT_01_281: [ The "Application data" from this frame is defined as the /data/ of the message. ]*/ + /* Codes_SRS_UWS_CLIENT_01_282: [ If the frame comprises an unfragmented message (Section 5.4), it is said that _A WebSocket Message Has Been Received_ with type /type/ and data /data/. ]*/ + if (is_final) + { + uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, WS_FRAME_TYPE_BINARY, uws_client->stream_buffer + needed_bytes - length, length); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_217: [ The fragments of one message MUST NOT be interleaved between the fragments of another message unless an extension has been negotiated that can interpret the interleaving. ]*/ + if (uws_client->fragmented_frame_type != WS_FRAME_TYPE_UNKNOWN) + { + LogError("Fragmented frame received interleaved between the fragments of another message"); + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + decode_stream = 1; + break; + } + /* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/ + /* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/ + /* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/ + if (process_frame_fragment(uws_client, length, needed_bytes) != 0) + { + break; + } + + /* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/ + /* Codes_SRS_UWS_CLIENT_01_226: [ Since control frames cannot be fragmented, the type for all fragments in a message MUST be either text, binary, or one of the reserved opcodes. ]*/ + uws_client->fragmented_frame_type = WS_FRAME_TYPE_BINARY; + } + decode_stream = 1; + break; + } + + /* Codes_SRS_UWS_CLIENT_01_156: [ * %x8 denotes a connection close ]*/ + /* Codes_SRS_UWS_CLIENT_01_234: [ The Close frame contains an opcode of 0x8. ]*/ + /* Codes_SRS_UWS_CLIENT_01_214: [ Control frames (see Section 5.5) MAY be injected in the middle of a fragmented message. ]*/ + case (unsigned char)WS_CLOSE_FRAME: + { + uint16_t close_code; + uint16_t* close_code_ptr; + const unsigned char* data_ptr = uws_client->stream_buffer + needed_bytes - length; + const unsigned char* extra_data_ptr; + size_t extra_data_length; + unsigned char* close_frame_bytes; + size_t close_frame_length; + bool utf8_error = false; + + /* Codes_SRS_UWS_CLIENT_01_215: [ Control frames themselves MUST NOT be fragmented. ]*/ + if (!is_final) + { + LogError("Fragmented control frame received."); + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + break; + } + + /* Codes_SRS_UWS_CLIENT_01_235: [ The Close frame MAY contain a body (the "Application data" portion of the frame) that indicates a reason for closing, such as an endpoint shutting down, an endpoint having received a frame too large, or an endpoint having received a frame that does not conform to the format expected by the endpoint. ]*/ + if (length >= 2) + { + /* Codes_SRS_UWS_CLIENT_01_236: [ If there is a body, the first two bytes of the body MUST be a 2-byte unsigned integer (in network byte order) representing a status code with value /code/ defined in Section 7.4. ]*/ + close_code = (data_ptr[0] << 8) + data_ptr[1]; + + /* Codes_SRS_UWS_CLIENT_01_461: [ The argument `close_code` shall be set to point to the code extracted from the CLOSE frame. ]*/ + close_code_ptr = &close_code; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_462: [ If no code can be extracted then `close_code` shall be NULL. ]*/ + close_code_ptr = NULL; + } + + if (length > 2) + { + /* Codes_SRS_UWS_CLIENT_01_463: [ The extra bytes (besides the close code) shall be passed to the `on_ws_peer_closed` callback by using `extra_data` and `extra_data_length`. ]*/ + extra_data_ptr = data_ptr + 2; + extra_data_length = length - 2; + + /* Codes_SRS_UWS_CLIENT_01_238: [ As the data is not guaranteed to be human readable, clients MUST NOT show it to end users. ]*/ + /* Codes_SRS_UWS_CLIENT_01_237: [ Following the 2-byte integer, the body MAY contain UTF-8-encoded data with value /reason/, the interpretation of which is not defined by this specification. ]*/ + if (utf8_checker_is_valid_utf8(extra_data_ptr, extra_data_length) != true) + { + LogError("Reason in CLOSE frame is not UTF-8."); + extra_data_ptr = NULL; + extra_data_length = 0; + utf8_error = true; + } + } + else + { + extra_data_ptr = NULL; + extra_data_length = 0; + } + + if (utf8_error) + { + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0) + { + LogError("Could not close underlying IO"); + indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO); + uws_client->uws_state = UWS_STATE_CLOSED; + } + } + else + { + BUFFER_HANDLE close_frame_buffer; + + if (uws_client->uws_state == UWS_STATE_CLOSING_WAITING_FOR_CLOSE) + { + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0) + { + indicate_ws_close_complete(uws_client); + uws_client->uws_state = UWS_STATE_CLOSED; + } + } + else + { + /* Codes_SRS_UWS_CLIENT_01_296: [ Upon either sending or receiving a Close control frame, it is said that _The WebSocket Closing Handshake is Started_ and that the WebSocket connection is in the CLOSING state. ]*/ + /* Codes_SRS_UWS_CLIENT_01_240: [ The application MUST NOT send any more data frames after sending a Close frame. ]*/ + uws_client->uws_state = UWS_STATE_CLOSING_SENDING_CLOSE; + } + + /* Codes_SRS_UWS_CLIENT_01_241: [ If an endpoint receives a Close frame and did not previously send a Close frame, the endpoint MUST send a Close frame in response. ]*/ + /* Codes_SRS_UWS_CLIENT_01_242: [ It SHOULD do so as soon as practical. ]*/ + /* Codes_SRS_UWS_CLIENT_01_239: [ Close frames sent from client to server must be masked as per Section 5.3. ]*/ + /* Codes_SRS_UWS_CLIENT_01_140: [ To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons that are further discussed in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). ]*/ + close_frame_buffer = uws_frame_encoder_encode(WS_CLOSE_FRAME, NULL, 0, true, true, 0); + if (close_frame_buffer == NULL) + { + LogError("Cannot encode the response CLOSE frame"); + + /* Codes_SRS_UWS_CLIENT_01_288: [ To _Close the WebSocket Connection_, an endpoint closes the underlying TCP connection. ]*/ + /* Codes_SRS_UWS_CLIENT_01_290: [ An endpoint MAY close the connection via any means available when necessary, such as when under attack. ]*/ + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0) + { + indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO); + uws_client->uws_state = UWS_STATE_CLOSED; + } + } + else + { + close_frame_bytes = BUFFER_u_char(close_frame_buffer); + close_frame_length = BUFFER_length(close_frame_buffer); + if (xio_send(uws_client->underlying_io, close_frame_bytes, close_frame_length, on_underlying_io_close_sent, uws_client) != 0) + { + LogError("Cannot send the response CLOSE frame"); + + /* Codes_SRS_UWS_CLIENT_01_288: [ To _Close the WebSocket Connection_, an endpoint closes the underlying TCP connection. ]*/ + /* Codes_SRS_UWS_CLIENT_01_290: [ An endpoint MAY close the connection via any means available when necessary, such as when under attack. ]*/ + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0) + { + indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO); + uws_client->uws_state = UWS_STATE_CLOSED; + } + } + + BUFFER_delete(close_frame_buffer); + } + } + + /* Codes_SRS_UWS_CLIENT_01_460: [ When a CLOSE frame is received the callback `on_ws_peer_closed` passed to `uws_client_open_async` shall be called, while passing to it the argument `on_ws_peer_closed_context`. ]*/ + uws_client->on_ws_peer_closed(uws_client->on_ws_peer_closed_context, close_code_ptr, extra_data_ptr, extra_data_length); + + break; + } + + /* Codes_SRS_UWS_CLIENT_01_157: [ * %x9 denotes a ping ]*/ + /* Codes_SRS_UWS_CLIENT_01_247: [ The Ping frame contains an opcode of 0x9. ]*/ + /* Codes_SRS_UWS_CLIENT_01_251: [ An endpoint MAY send a Ping frame any time after the connection is established and before the connection is closed. ]*/ + /* Codes_SRS_UWS_CLIENT_01_214: [ Control frames (see Section 5.5) MAY be injected in the middle of a fragmented message. ]*/ + case (unsigned char)WS_PING_FRAME: + { + /* Codes_SRS_UWS_CLIENT_01_249: [ Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response ]*/ + /* Codes_SRS_UWS_CLIENT_01_250: [ It SHOULD respond with Pong frame as soon as is practical. ]*/ + unsigned char* pong_frame; + size_t pong_frame_length; + BUFFER_HANDLE pong_frame_buffer; + + /* Codes_SRS_UWS_CLIENT_01_215: [ Control frames themselves MUST NOT be fragmented. ]*/ + if (!is_final) + { + LogError("Fragmented control frame received."); + indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED); + break; + } + + /* Codes_SRS_UWS_CLIENT_01_140: [ To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons that are further discussed in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). ]*/ + pong_frame_buffer = uws_frame_encoder_encode(WS_PONG_FRAME, uws_client->stream_buffer + needed_bytes - length, length, true, true, 0); + if (pong_frame_buffer == NULL) + { + LogError("Encoding of PONG failed."); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_248: [ A Ping frame MAY include "Application data". ]*/ + pong_frame = BUFFER_u_char(pong_frame_buffer); + pong_frame_length = BUFFER_length(pong_frame_buffer); + if (xio_send(uws_client->underlying_io, pong_frame, pong_frame_length, unchecked_on_send_complete, NULL) != 0) + { + LogError("Sending PONG frame failed."); + } + + BUFFER_delete(pong_frame_buffer); + } + + break; + } + /* Codes_SRS_UWS_CLIENT_01_252: [ The Pong frame contains an opcode of 0xA. ]*/ + case (unsigned char)WS_PONG_FRAME: + break; + } + + consume_stream_buffer_bytes(uws_client, needed_bytes); + } + } + + break; + } + } + } + } + } +} + +static void on_underlying_io_error(void* context) +{ + UWS_CLIENT_HANDLE uws_client = (UWS_CLIENT_HANDLE)context; + + switch (uws_client->uws_state) + { + default: + break; + + case UWS_STATE_CLOSING_WAITING_FOR_CLOSE: + case UWS_STATE_CLOSING_SENDING_CLOSE: + case UWS_STATE_CLOSING_UNDERLYING_IO: + /* Codes_SRS_UWS_CLIENT_01_500: [ The callback `on_ws_close_complete` shall be called, while passing the `on_ws_close_complete_context` argument to it. ]*/ + /* Codes_SRS_UWS_CLIENT_01_377: [ When `on_underlying_io_error` is called while the uws instance is CLOSING the underlying IO shall be closed by calling `xio_close`. ]*/ + (void)xio_close(uws_client->underlying_io, NULL, NULL); + + /* Codes_SRS_UWS_CLIENT_01_499: [ If the CLOSE was due to the peer closing, the callback `on_ws_close_complete` shall not be called. ]*/ + indicate_ws_close_complete(uws_client); + break; + + case UWS_STATE_OPENING_UNDERLYING_IO: + case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE: + /* Codes_SRS_UWS_CLIENT_01_375: [ When `on_underlying_io_error` is called while uws is OPENING, uws shall report that the open failed by calling the `on_ws_open_complete` callback passed to `uws_client_open_async` with `WS_OPEN_ERROR_UNDERLYING_IO_ERROR`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_077: [ If this fails (e.g., the server's certificate could not be verified), then the client MUST _Fail the WebSocket Connection_ and abort the connection. ]*/ + indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_UNDERLYING_IO_ERROR); + break; + + case UWS_STATE_OPEN: + /* Codes_SRS_UWS_CLIENT_01_376: [ When `on_underlying_io_error` is called while the uws instance is OPEN, an error shall be reported to the user by calling the `on_ws_error` callback that was passed to `uws_client_open_async` with the argument `WS_ERROR_UNDERLYING_IO_ERROR`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_318: [ Servers MAY close the WebSocket connection whenever desired. ]*/ + /* Codes_SRS_UWS_CLIENT_01_269: [ If at any point the state of the WebSocket connection changes, the endpoint MUST abort the following steps. ]*/ + indicate_ws_error(uws_client, WS_ERROR_UNDERLYING_IO_ERROR); + break; + } +} + +int uws_client_open_async(UWS_CLIENT_HANDLE uws_client, ON_WS_OPEN_COMPLETE on_ws_open_complete, void* on_ws_open_complete_context, ON_WS_FRAME_RECEIVED on_ws_frame_received, void* on_ws_frame_received_context, ON_WS_PEER_CLOSED on_ws_peer_closed, void* on_ws_peer_closed_context, ON_WS_ERROR on_ws_error, void* on_ws_error_context) +{ + int result; + + /* Codes_SRS_UWS_CLIENT_01_393: [ The context arguments for the callbacks shall be allowed to be NULL. ]*/ + if ((uws_client == NULL) || + (on_ws_open_complete == NULL) || + (on_ws_frame_received == NULL) || + (on_ws_peer_closed == NULL) || + (on_ws_error == NULL)) + { + /* Codes_SRS_UWS_CLIENT_01_027: [ If `uws_client`, `on_ws_open_complete`, `on_ws_frame_received`, `on_ws_peer_closed` or `on_ws_error` is NULL, `uws_client_open_async` shall fail and return a non-zero value. ]*/ + LogError("Invalid arguments: uws=%p, on_ws_open_complete=%p, on_ws_frame_received=%p, on_ws_error=%p", + uws_client, on_ws_open_complete, on_ws_frame_received, on_ws_error); + result = __FAILURE__; + } + else + { + if (uws_client->uws_state != UWS_STATE_CLOSED) + { + /* Codes_SRS_UWS_CLIENT_01_400: [ `uws_client_open_async` while CLOSING shall fail and return a non-zero value. ]*/ + /* Codes_SRS_UWS_CLIENT_01_394: [ `uws_client_open_async` while the uws instance is already OPEN or OPENING shall fail and return a non-zero value. ]*/ + LogError("Invalid uWS state while trying to open: %d", (int)uws_client->uws_state); + result = __FAILURE__; + } + else + { + uws_client->uws_state = UWS_STATE_OPENING_UNDERLYING_IO; + + uws_client->stream_buffer_count = 0; + uws_client->fragment_buffer_count = 0; + uws_client->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN; + + uws_client->on_ws_open_complete = on_ws_open_complete; + uws_client->on_ws_open_complete_context = on_ws_open_complete_context; + uws_client->on_ws_frame_received = on_ws_frame_received; + uws_client->on_ws_frame_received_context = on_ws_frame_received_context; + uws_client->on_ws_peer_closed = on_ws_peer_closed; + uws_client->on_ws_peer_closed_context = on_ws_peer_closed_context; + uws_client->on_ws_error = on_ws_error; + uws_client->on_ws_error_context = on_ws_error_context; + + /* Codes_SRS_UWS_CLIENT_01_025: [ `uws_client_open_async` shall open the underlying IO by calling `xio_open` and providing the IO handle created in `uws_client_create` as argument. ]*/ + /* Codes_SRS_UWS_CLIENT_01_367: [ The callbacks `on_underlying_io_open_complete`, `on_underlying_io_bytes_received` and `on_underlying_io_error` shall be passed as arguments to `xio_open`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_061: [ To _Establish a WebSocket Connection_, a client opens a connection and sends a handshake as defined in this section. ]*/ + if (xio_open(uws_client->underlying_io, on_underlying_io_open_complete, uws_client, on_underlying_io_bytes_received, uws_client, on_underlying_io_error, uws_client) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_028: [ If opening the underlying IO fails then `uws_client_open_async` shall fail and return a non-zero value. ]*/ + /* Codes_SRS_UWS_CLIENT_01_075: [ If the connection could not be opened, either because a direct connection failed or because any proxy used returned an error, then the client MUST _Fail the WebSocket Connection_ and abort the connection attempt. ]*/ + LogError("Opening the underlying IO failed"); + uws_client->uws_state = UWS_STATE_CLOSED; + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_026: [ On success, `uws_client_open_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static int complete_send_frame(WS_PENDING_SEND* ws_pending_send, LIST_ITEM_HANDLE pending_send_frame_item, WS_SEND_FRAME_RESULT ws_send_frame_result) +{ + int result; + UWS_CLIENT_INSTANCE* uws_client = ws_pending_send->uws_client; + + /* Codes_SRS_UWS_CLIENT_01_432: [ The indicated sent frame shall be removed from the list by calling `singlylinkedlist_remove`. ]*/ + if (singlylinkedlist_remove(uws_client->pending_sends, pending_send_frame_item) != 0) + { + LogError("Failed removing item from list"); + result = __FAILURE__; + } + else + { + if (ws_pending_send->on_ws_send_frame_complete != NULL) + { + /* Codes_SRS_UWS_CLIENT_01_037: [ When indicating pending send frames as cancelled the callback context passed to the `on_ws_send_frame_complete` callback shall be the context given to `uws_client_send_frame_async`. ]*/ + ws_pending_send->on_ws_send_frame_complete(ws_pending_send->context, ws_send_frame_result); + } + + /* Codes_SRS_UWS_CLIENT_01_434: [ The memory associated with the sent frame shall be freed. ]*/ + free(ws_pending_send); + + result = 0; + } + + return result; +} + +/* Codes_SRS_UWS_CLIENT_01_029: [ `uws_client_close_async` shall close the uws instance connection if an open action is either pending or has completed successfully (if the IO is open). ]*/ +/* Codes_SRS_UWS_CLIENT_01_317: [ Clients SHOULD NOT close the WebSocket connection arbitrarily. ]*/ +int uws_client_close_async(UWS_CLIENT_HANDLE uws_client, ON_WS_CLOSE_COMPLETE on_ws_close_complete, void* on_ws_close_complete_context) +{ + int result; + + /* Codes_SRS_UWS_CLIENT_01_397: [ The `on_ws_close_complete` argument shall be allowed to be NULL, in which case no callback shall be called when the close is complete. ]*/ + /* Codes_SRS_UWS_CLIENT_01_398: [ `on_ws_close_complete_context` shall also be allows to be NULL. ]*/ + if (uws_client == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_030: [ if `uws_client` is NULL, `uws_client_close_async` shall return a non-zero value. ]*/ + LogError("NULL uWS handle."); + result = __FAILURE__; + } + else + { + if ((uws_client->uws_state == UWS_STATE_CLOSED) || + (uws_client->uws_state == UWS_STATE_CLOSING_SENDING_CLOSE) || + (uws_client->uws_state == UWS_STATE_CLOSING_WAITING_FOR_CLOSE) || + (uws_client->uws_state == UWS_STATE_CLOSING_UNDERLYING_IO)) + { + /* Codes_SRS_UWS_CLIENT_01_032: [ `uws_client_close_async` when no open action has been issued shall fail and return a non-zero value. ]*/ + LogError("close has been called when already CLOSED"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_399: [ `on_ws_close_complete` and `on_ws_close_complete_context` shall be saved and the callback `on_ws_close_complete` shall be triggered when the close is complete. ]*/ + uws_client->on_ws_close_complete = on_ws_close_complete; + uws_client->on_ws_close_complete_context = on_ws_close_complete_context; + + uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO; + + /* Codes_SRS_UWS_CLIENT_01_031: [ `uws_client_close_async` shall close the connection by calling `xio_close` while passing as argument the IO handle created in `uws_client_create`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_368: [ The callback `on_underlying_io_close` shall be passed as argument to `xio_close`. ]*/ + if (xio_close(uws_client->underlying_io, (on_ws_close_complete == NULL) ? NULL : on_underlying_io_close_complete, (on_ws_close_complete == NULL) ? NULL : uws_client) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_395: [ If `xio_close` fails, `uws_client_close_async` shall fail and return a non-zero value. ]*/ + LogError("Closing the underlying IO failed."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_034: [ `uws_client_close_async` shall obtain all the pending send frames by repetitively querying for the head of the pending IO list and freeing that head item. ]*/ + LIST_ITEM_HANDLE first_pending_send; + + /* Codes_SRS_UWS_CLIENT_01_035: [ Obtaining the head of the pending send frames list shall be done by calling `singlylinkedlist_get_head_item`. ]*/ + while ((first_pending_send = singlylinkedlist_get_head_item(uws_client->pending_sends)) != NULL) + { + WS_PENDING_SEND* ws_pending_send = (WS_PENDING_SEND*)singlylinkedlist_item_get_value(first_pending_send); + + /* Codes_SRS_UWS_CLIENT_01_036: [ For each pending send frame the send complete callback shall be called with `UWS_SEND_FRAME_CANCELLED`. ]*/ + complete_send_frame(ws_pending_send, first_pending_send, WS_SEND_FRAME_CANCELLED); + } + + /* Codes_SRS_UWS_CLIENT_01_396: [ On success `uws_client_close_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +/* Codes_SRS_UWS_CLIENT_01_317: [ Clients SHOULD NOT close the WebSocket connection arbitrarily. ]*/ +int uws_client_close_handshake_async(UWS_CLIENT_HANDLE uws_client, uint16_t close_code, const char* close_reason, ON_WS_CLOSE_COMPLETE on_ws_close_complete, void* on_ws_close_complete_context) +{ + int result; + + if (uws_client == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_467: [ if `uws_client` is NULL, `uws_client_close_handshake_async` shall return a non-zero value. ]*/ + LogError("NULL uws_client"); + result = __FAILURE__; + } + else + { + if ((uws_client->uws_state == UWS_STATE_CLOSED) || + /* Codes_SRS_UWS_CLIENT_01_474: [ `uws_client_close_handshake_async` when already CLOSING shall fail and return a non-zero value. ]*/ + (uws_client->uws_state == UWS_STATE_CLOSING_WAITING_FOR_CLOSE) || + (uws_client->uws_state == UWS_STATE_CLOSING_SENDING_CLOSE) || + (uws_client->uws_state == UWS_STATE_CLOSING_UNDERLYING_IO)) + { + /* Codes_SRS_UWS_CLIENT_01_473: [ `uws_client_close_handshake_async` when no open action has been issued shall fail and return a non-zero value. ]*/ + LogError("uws_client_close_handshake_async has been called when already CLOSED"); + result = __FAILURE__; + } + else + { + (void)close_reason; + + /* Codes_SRS_UWS_CLIENT_01_468: [ `on_ws_close_complete` and `on_ws_close_complete_context` shall be saved and the callback `on_ws_close_complete` shall be triggered when the close is complete. ]*/ + /* Codes_SRS_UWS_CLIENT_01_469: [ The `on_ws_close_complete` argument shall be allowed to be NULL, in which case no callback shall be called when the close is complete. ]*/ + /* Codes_SRS_UWS_CLIENT_01_470: [ `on_ws_close_complete_context` shall also be allowed to be NULL. ]*/ + uws_client->on_ws_close_complete = on_ws_close_complete; + uws_client->on_ws_close_complete_context = on_ws_close_complete_context; + + uws_client->uws_state = UWS_STATE_CLOSING_WAITING_FOR_CLOSE; + + /* Codes_SRS_UWS_CLIENT_01_465: [ `uws_client_close_handshake_async` shall initiate the close handshake by sending a close frame to the peer. ]*/ + if (send_close_frame(uws_client, close_code) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_472: [ If `xio_send` fails, `uws_client_close_handshake_async` shall fail and return a non-zero value. ]*/ + LogError("Sending CLOSE frame failed"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE first_pending_send; + + while ((first_pending_send = singlylinkedlist_get_head_item(uws_client->pending_sends)) != NULL) + { + WS_PENDING_SEND* ws_pending_send = (WS_PENDING_SEND*)singlylinkedlist_item_get_value(first_pending_send); + + complete_send_frame(ws_pending_send, first_pending_send, WS_SEND_FRAME_CANCELLED); + } + + /* Codes_SRS_UWS_CLIENT_01_466: [ On success `uws_client_close_handshake_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static void on_underlying_io_send_complete(void* context, IO_SEND_RESULT send_result) +{ + if (context == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_435: [ When `on_underlying_io_send_complete` is called with a NULL `context`, it shall do nothing. ]*/ + LogError("on_underlying_io_send_complete called with NULL context"); + } + else + { + LIST_ITEM_HANDLE ws_pending_send_list_item = (LIST_ITEM_HANDLE)context; + WS_PENDING_SEND* ws_pending_send = (WS_PENDING_SEND*)singlylinkedlist_item_get_value(ws_pending_send_list_item); + UWS_CLIENT_HANDLE uws_client = ws_pending_send->uws_client; + WS_SEND_FRAME_RESULT ws_send_frame_result; + + switch (send_result) + { + /* Codes_SRS_UWS_CLIENT_01_436: [ When `on_underlying_io_send_complete` is called with any other error code, the send shall be indicated to the uws user by calling `on_ws_send_frame_complete` with `WS_SEND_FRAME_ERROR`. ]*/ + default: + case IO_SEND_ERROR: + /* Codes_SRS_UWS_CLIENT_01_390: [ When `on_underlying_io_send_complete` is called with `IO_SEND_ERROR` as a result of sending a WebSocket frame to the underlying IO, the send shall be indicated to the uws user by calling `on_ws_send_frame_complete` with `WS_SEND_FRAME_ERROR`. ]*/ + ws_send_frame_result = WS_SEND_FRAME_ERROR; + break; + + case IO_SEND_OK: + /* Codes_SRS_UWS_CLIENT_01_389: [ When `on_underlying_io_send_complete` is called with `IO_SEND_OK` as a result of sending a WebSocket frame to the underlying IO, the send shall be indicated to the uws user by calling `on_ws_send_frame_complete` with `WS_SEND_FRAME_OK`. ]*/ + ws_send_frame_result = WS_SEND_FRAME_OK; + break; + + case IO_SEND_CANCELLED: + /* Codes_SRS_UWS_CLIENT_01_391: [ When `on_underlying_io_send_complete` is called with `IO_SEND_CANCELLED` as a result of sending a WebSocket frame to the underlying IO, the send shall be indicated to the uws user by calling `on_ws_send_frame_complete` with `WS_SEND_FRAME_CANCELLED`. ]*/ + ws_send_frame_result = WS_SEND_FRAME_CANCELLED; + break; + } + + if (complete_send_frame(ws_pending_send, ws_pending_send_list_item, ws_send_frame_result) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_433: [ If `singlylinkedlist_remove` fails an error shall be indicated by calling the `on_ws_error` callback with `WS_ERROR_CANNOT_REMOVE_SENT_ITEM_FROM_LIST`. ]*/ + indicate_ws_error(uws_client, WS_ERROR_CANNOT_REMOVE_SENT_ITEM_FROM_LIST); + } + } +} + +static bool find_list_node(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + return list_item == (LIST_ITEM_HANDLE)match_context; +} + +int uws_client_send_frame_async(UWS_CLIENT_HANDLE uws_client, unsigned char frame_type, const unsigned char* buffer, size_t size, bool is_final, ON_WS_SEND_FRAME_COMPLETE on_ws_send_frame_complete, void* on_ws_send_frame_complete_context) +{ + int result; + + if (uws_client == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_044: [ If any the arguments `uws_client` is NULL, `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("NULL uws handle."); + result = __FAILURE__; + } + else if ((buffer == NULL) && + (size > 0)) + { + /* Codes_SRS_UWS_CLIENT_01_045: [ If `size` is non-zero and `buffer` is NULL then `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("NULL buffer with %u size.", (unsigned int)size); + result = __FAILURE__; + } + /* Codes_SRS_UWS_CLIENT_01_146: [ A data frame MAY be transmitted by either the client or the server at any time after opening handshake completion and before that endpoint has sent a Close frame (Section 5.5.1). ]*/ + /* Codes_SRS_UWS_CLIENT_01_268: [ The endpoint MUST ensure the WebSocket connection is in the OPEN state ]*/ + else if (uws_client->uws_state != UWS_STATE_OPEN) + { + /* Codes_SRS_UWS_CLIENT_01_043: [ If the uws instance is not OPEN (open has not been called or is still in progress) then `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("uws not in OPEN state."); + result = __FAILURE__; + } + else + { + WS_PENDING_SEND* ws_pending_send = (WS_PENDING_SEND*)malloc(sizeof(WS_PENDING_SEND)); + if (ws_pending_send == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_047: [ If allocating memory for the newly queued item fails, `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("Cannot allocate memory for frame to be sent."); + result = __FAILURE__; + } + else + { + BUFFER_HANDLE non_control_frame_buffer; + + /* Codes_SRS_UWS_CLIENT_01_425: [ Encoding shall be done by calling `uws_frame_encoder_encode` and passing to it the `buffer` and `size` argument for payload, the `is_final` flag and setting `is_masked` to true. ]*/ + /* Codes_SRS_UWS_CLIENT_01_270: [ An endpoint MUST encapsulate the /data/ in a WebSocket frame as defined in Section 5.2. ]*/ + /* Codes_SRS_UWS_CLIENT_01_272: [ The opcode (frame-opcode) of the first frame containing the data MUST be set to the appropriate value from Section 5.2 for data that is to be interpreted by the recipient as text or binary data. ]*/ + /* Codes_SRS_UWS_CLIENT_01_274: [ If the data is being sent by the client, the frame(s) MUST be masked as defined in Section 5.3. ]*/ + non_control_frame_buffer = uws_frame_encoder_encode((WS_FRAME_TYPE)frame_type, buffer, size, true, is_final, 0); + if (non_control_frame_buffer == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_426: [ If `uws_frame_encoder_encode` fails, `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("Failed encoding WebSocket frame"); + free(ws_pending_send); + result = __FAILURE__; + } + else + { + const unsigned char* encoded_frame; + size_t encoded_frame_length; + LIST_ITEM_HANDLE new_pending_send_list_item; + + /* Codes_SRS_UWS_CLIENT_01_428: [ The encoded frame buffer memory shall be obtained by calling `BUFFER_u_char` on the encode buffer. ]*/ + encoded_frame = BUFFER_u_char(non_control_frame_buffer); + /* Codes_SRS_UWS_CLIENT_01_429: [ The encoded frame size shall be obtained by calling `BUFFER_length` on the encode buffer. ]*/ + encoded_frame_length = BUFFER_length(non_control_frame_buffer); + + /* Codes_SRS_UWS_CLIENT_01_038: [ `uws_client_send_frame_async` shall create and queue a structure that contains: ]*/ + /* Codes_SRS_UWS_CLIENT_01_050: [ The argument `on_ws_send_frame_complete` shall be optional, if NULL is passed by the caller then no send complete callback shall be triggered. ]*/ + /* Codes_SRS_UWS_CLIENT_01_040: [ - the send complete callback `on_ws_send_frame_complete` ]*/ + /* Codes_SRS_UWS_CLIENT_01_041: [ - the send complete callback context `on_ws_send_frame_complete_context` ]*/ + ws_pending_send->on_ws_send_frame_complete = on_ws_send_frame_complete; + ws_pending_send->context = on_ws_send_frame_complete_context; + ws_pending_send->uws_client = uws_client; + + /* Codes_SRS_UWS_CLIENT_01_048: [ Queueing shall be done by calling `singlylinkedlist_add`. ]*/ + new_pending_send_list_item = singlylinkedlist_add(uws_client->pending_sends, ws_pending_send); + if (new_pending_send_list_item == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_049: [ If `singlylinkedlist_add` fails, `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("Could not allocate memory for pending frames"); + free(ws_pending_send); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_431: [ Once encoded the frame shall be sent by using `xio_send` with the following arguments: ]*/ + /* Codes_SRS_UWS_CLIENT_01_053: [ - the io handle shall be the underlyiong IO handle created in `uws_client_create`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_054: [ - the `buffer` argument shall point to the complete websocket frame to be sent. ]*/ + /* Codes_SRS_UWS_CLIENT_01_055: [ - the `size` argument shall indicate the websocket frame length. ]*/ + /* Codes_SRS_UWS_CLIENT_01_056: [ - the `send_complete` callback shall be the `on_underlying_io_send_complete` function. ]*/ + /* Codes_SRS_UWS_CLIENT_01_057: [ - the `send_complete_context` argument shall identify the pending send. ]*/ + /* Codes_SRS_UWS_CLIENT_01_276: [ The frame(s) that have been formed MUST be transmitted over the underlying network connection. ]*/ + if (xio_send(uws_client->underlying_io, encoded_frame, encoded_frame_length, on_underlying_io_send_complete, new_pending_send_list_item) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_058: [ If `xio_send` fails, `uws_client_send_frame_async` shall fail and return a non-zero value. ]*/ + LogError("Could not send bytes through the underlying IO"); + + /* Codes_SRS_UWS_CLIENT_09_001: [ If `xio_send` fails and the message is still queued, it shall be de-queued and destroyed. ] */ + if (singlylinkedlist_find(uws_client->pending_sends, find_list_node, new_pending_send_list_item) != NULL) + { + // Guards against double free in case the underlying I/O invoked 'on_underlying_io_send_complete' within xio_send. + (void)singlylinkedlist_remove(uws_client->pending_sends, new_pending_send_list_item); + free(ws_pending_send); + } + + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_042: [ On success, `uws_client_send_frame_async` shall return 0. ]*/ + result = 0; + } + } + + BUFFER_delete(non_control_frame_buffer); + } + } + } + + return result; +} + +void uws_client_dowork(UWS_CLIENT_HANDLE uws_client) +{ + if (uws_client == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_059: [ If the `uws_client` argument is NULL, `uws_client_dowork` shall do nothing. ]*/ + LogError("NULL uws handle."); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_060: [ If the IO is not yet open, `uws_client_dowork` shall do nothing. ]*/ + if (uws_client->uws_state != UWS_STATE_CLOSED) + { + /* Codes_SRS_UWS_CLIENT_01_430: [ `uws_client_dowork` shall call `xio_dowork` with the IO handle argument set to the underlying IO created in `uws_client_create`. ]*/ + xio_dowork(uws_client->underlying_io); + } + } +} + +int uws_client_set_option(UWS_CLIENT_HANDLE uws_client, const char* option_name, const void* value) +{ + int result; + + if ( + (uws_client == NULL) || + (option_name == NULL) + ) + { + /* Codes_SRS_UWS_CLIENT_01_440: [ If any of the arguments `uws_client` or `option_name` is NULL `uws_client_set_option` shall return a non-zero value. ]*/ + LogError("invalid parameter (NULL) passed to uws_client_set_option"); + result = __FAILURE__; + } + else + { + if (strcmp(UWS_CLIENT_OPTIONS, option_name) == 0) + { + /* Codes_SRS_UWS_CLIENT_01_510: [ If the option name is `uWSClientOptions` then `uws_client_set_option` shall call `OptionHandler_FeedOptions` and pass to it the underlying IO handle and the `value` argument. ]*/ + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, uws_client->underlying_io) != OPTIONHANDLER_OK) + { + /* Codes_SRS_UWS_CLIENT_01_511: [ If `OptionHandler_FeedOptions` fails, `uws_client_set_option` shall fail and return a non-zero value. ]*/ + LogError("OptionHandler_FeedOptions failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_442: [ On success, `uws_client_set_option` shall return 0. ]*/ + result = 0; + } + } + else + { + /* Codes_SRS_UWS_CLIENT_01_441: [ Otherwise all options shall be passed as they are to the underlying IO by calling `xio_setoption`. ]*/ + if (xio_setoption(uws_client->underlying_io, option_name, value) != 0) + { + /* Codes_SRS_UWS_CLIENT_01_443: [ If `xio_setoption` fails, `uws_client_set_option` shall fail and return a non-zero value. ]*/ + LogError("xio_setoption failed."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_442: [ On success, `uws_client_set_option` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static void* uws_client_clone_option(const char* name, const void* value) +{ + void* result; + + if ( + (name == NULL) || + (value == NULL) + ) + { + /* Codes_SRS_UWS_CLIENT_01_506: [ If `uws_client_clone_option` is called with NULL `name` or `value` it shall return NULL. ]*/ + LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value); + result = NULL; + } + else + { + if (strcmp(name, UWS_CLIENT_OPTIONS) == 0) + { + /* Codes_SRS_UWS_CLIENT_01_507: [ `uws_client_clone_option` called with `name` being `uWSClientOptions` shall return the same value. ]*/ + result = (void*)value; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_512: [ `uws_client_clone_option` called with any other option name than `uWSClientOptions` shall return NULL. ]*/ + LogError("unknown option: %s", name); + result = NULL; + } + } + + return result; +} + +static void uws_client_destroy_option(const char* name, const void* value) +{ + if ( + (name == NULL) || + (value == NULL) + ) + { + /* Codes_SRS_UWS_CLIENT_01_509: [ If `uws_client_destroy_option` is called with NULL `name` or `value` it shall do nothing. ]*/ + LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value); + } + else + { + if (strcmp(name, UWS_CLIENT_OPTIONS) == 0) + { + /* Codes_SRS_UWS_CLIENT_01_508: [ `uws_client_destroy_option` called with the option `name` being `uWSClientOptions` shall destroy the value by calling `OptionHandler_Destroy`. ]*/ + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_513: [ If `uws_client_destroy_option` is called with any other `name` it shall do nothing. ]*/ + LogError("unknown option: %s", name); + } + } +} + +OPTIONHANDLER_HANDLE uws_client_retrieve_options(UWS_CLIENT_HANDLE uws_client) +{ + OPTIONHANDLER_HANDLE result; + + if (uws_client == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_444: [ If parameter `uws_client` is `NULL` then `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("NULL uws handle."); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_445: [ `uws_client_retrieve_options` shall call `OptionHandler_Create` to produce an `OPTIONHANDLER_HANDLE` and on success return the new `OPTIONHANDLER_HANDLE` handle. ]*/ + result = OptionHandler_Create(uws_client_clone_option, uws_client_destroy_option, (pfSetOption)uws_client_set_option); + if (result == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_446: [ If `OptionHandler_Create` fails then `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("OptionHandler_Create failed"); + } + else + { + /* Codes_SRS_UWS_CLIENT_01_502: [ When calling `xio_retrieveoptions` the underlying IO handle shall be passed to it. ]*/ + OPTIONHANDLER_HANDLE underlying_io_options = xio_retrieveoptions(uws_client->underlying_io); + if (underlying_io_options == NULL) + { + /* Codes_SRS_UWS_CLIENT_01_503: [ If `xio_retrieveoptions` fails, `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("unable to concrete_io_retrieveoptions"); + OptionHandler_Destroy(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_CLIENT_01_501: [ `uws_client_retrieve_options` shall add to the option handler one option, whose name shall be `uWSClientOptions` and the value shall be queried by calling `xio_retrieveoptions`. ]*/ + /* Codes_SRS_UWS_CLIENT_01_504: [ Adding the option shall be done by calling `OptionHandler_AddOption`. ]*/ + if (OptionHandler_AddOption(result, UWS_CLIENT_OPTIONS, underlying_io_options) != OPTIONHANDLER_OK) + { + /* Codes_SRS_UWS_CLIENT_01_505: [ If `OptionHandler_AddOption` fails, `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("OptionHandler_AddOption failed"); + OptionHandler_Destroy(underlying_io_options); + OptionHandler_Destroy(result); + result = NULL; + } + } + } + + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/uws_frame_encoder.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,191 @@ + // Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/gb_rand.h" +#include "azure_c_shared_utility/uws_frame_encoder.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/uniqueid.h" + +BUFFER_HANDLE uws_frame_encoder_encode(WS_FRAME_TYPE opcode, const unsigned char* payload, size_t length, bool is_masked, bool is_final, unsigned char reserved) +{ + BUFFER_HANDLE result; + + if (reserved > 7) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_052: [ If `reserved` has any bits set except the lowest 3 then `uws_frame_encoder_encode` shall fail and return NULL. ]*/ + LogError("Bad reserved value: 0x%02x", reserved); + result = NULL; + } + else if (opcode > 0x0F) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_006: [ If an unknown opcode is received, the receiving endpoint MUST _Fail the WebSocket Connection_. ]*/ + LogError("Invalid opcode: 0x%02x", opcode); + result = NULL; + } + else if ((length > 0) && + (payload == NULL)) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_054: [ If `length` is greater than 0 and payload is NULL, then `uws_frame_encoder_encode` shall fail and return NULL. ]*/ + LogError("Invalid arguments: NULL payload and length=%u", (unsigned int)length); + result = NULL; + } + else + { + size_t needed_bytes = 2; + size_t header_bytes; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_044: [ On success `uws_frame_encoder_encode` shall return a non-NULL handle to the result buffer. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_048: [ The newly created buffer shall be created by calling `BUFFER_new`. ]*/ + result = BUFFER_new(); + if (result == NULL) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_049: [ If `BUFFER_new` fails then `uws_frame_encoder_encode` shall fail and return NULL. ]*/ + LogError("Cannot create new buffer"); + } + else + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_001: [ `uws_frame_encoder_encode` shall encode the information given in `opcode`, `payload`, `length`, `is_masked`, `is_final` and `reserved` according to the RFC6455 into a new buffer.]*/ + if (length > 65535) + { + needed_bytes += 8; + } + else if (length > 125) + { + needed_bytes += 2; + } + + if (is_masked) + { + needed_bytes += 4; + } + + header_bytes = needed_bytes; + needed_bytes += length; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_046: [ The result buffer shall be resized accordingly using `BUFFER_enlarge`. ]*/ + if (BUFFER_enlarge(result, needed_bytes) != 0) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_047: [ If `BUFFER_enlarge` fails then `uws_frame_encoder_encode` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for encoded frame"); + BUFFER_delete(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_050: [ The allocated memory shall be accessed by calling `BUFFER_u_char`. ]*/ + unsigned char* buffer = BUFFER_u_char(result); + if (buffer == NULL) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_051: [ If `BUFFER_u_char` fails then `uws_frame_encoder_encode` shall fail and return a NULL. ]*/ + LogError("Cannot get encoded buffer pointer"); + BUFFER_delete(result); + result = NULL; + } + else + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_007: [ * %x0 denotes a continuation frame ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_008: [ * %x1 denotes a text frame ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_009: [ * %x2 denotes a binary frame ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_010: [ * %x3-7 are reserved for further non-control frames ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_011: [ * %x8 denotes a connection close ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_012: [ * %x9 denotes a ping ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_013: [ * %xA denotes a pong ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_014: [ * %xB-F are reserved for further control frames ]*/ + buffer[0] = (unsigned char)opcode; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_002: [ Indicates that this is the final fragment in a message. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_003: [ The first fragment MAY also be the final fragment. ]*/ + if (is_final) + { + buffer[0] |= 0x80; + } + + /* Codes_SRS_UWS_FRAME_ENCODER_01_004: [ MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. ]*/ + buffer[0] |= reserved << 4; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_022: [ Note that in all cases, the minimal number of bytes MUST be used to encode the length, for example, the length of a 124-byte-long string can't be encoded as the sequence 126, 0, 124. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_018: [ The length of the "Payload data", in bytes: ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_023: [ The payload length is the length of the "Extension data" + the length of the "Application data". ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_042: [ The payload length, indicated in the framing as frame-payload-length, does NOT include the length of the masking key. ]*/ + if (length > 65535) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_020: [ If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length. ]*/ + buffer[1] = 127; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_021: [ Multibyte length quantities are expressed in network byte order. ]*/ + buffer[2] = (unsigned char)((uint64_t)length >> 56) & 0xFF; + buffer[3] = (unsigned char)((uint64_t)length >> 48) & 0xFF; + buffer[4] = (unsigned char)((uint64_t)length >> 40) & 0xFF; + buffer[5] = (unsigned char)((uint64_t)length >> 32) & 0xFF; + buffer[6] = (unsigned char)((uint64_t)length >> 24) & 0xFF; + buffer[7] = (unsigned char)((uint64_t)length >> 16) & 0xFF; + buffer[8] = (unsigned char)((uint64_t)length >> 8) & 0xFF; + buffer[9] = (unsigned char)(length & 0xFF); + } + else if (length > 125) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_019: [ If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. ]*/ + buffer[1] = 126; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_021: [ Multibyte length quantities are expressed in network byte order. ]*/ + buffer[2] = (unsigned char)(length >> 8); + buffer[3] = (unsigned char)(length & 0xFF); + } + else + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_043: [ if 0-125, that is the payload length. ]*/ + buffer[1] = (unsigned char)length; + } + + if (is_masked) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_015: [ Defines whether the "Payload data" is masked. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_033: [ A masked frame MUST have the field frame-masked set to 1, as defined in Section 5.2. ]*/ + buffer[1] |= 0x80; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_053: [ In order to obtain a 32 bit value for masking, `gb_rand` shall be used 4 times (for each byte). ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_016: [ If set to 1, a masking key is present in masking-key, and this is used to unmask the "Payload data" as per Section 5.3. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_026: [ This field is present if the mask bit is set to 1 and is absent if the mask bit is set to 0. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_034: [ The masking key is contained completely within the frame, as defined in Section 5.2 as frame-masking-key. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_036: [ The masking key is a 32-bit value chosen at random by the client. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_037: [ When preparing a masked frame, the client MUST pick a fresh masking key from the set of allowed 32-bit values. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_038: [ The masking key needs to be unpredictable; thus, the masking key MUST be derived from a strong source of entropy, and the masking key for a given frame MUST NOT make it simple for a server/proxy to predict the masking key for a subsequent frame. ]*/ + buffer[header_bytes - 4] = (unsigned char)gb_rand(); + buffer[header_bytes - 3] = (unsigned char)gb_rand(); + buffer[header_bytes - 2] = (unsigned char)gb_rand(); + buffer[header_bytes - 1] = (unsigned char)gb_rand(); + } + + if (length > 0) + { + if (is_masked) + { + size_t i; + + /* Codes_SRS_UWS_FRAME_ENCODER_01_035: [ It is used to mask the "Payload data" defined in the same section as frame-payload-data, which includes "Extension data" and "Application data". ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_039: [ To convert masked data into unmasked data, or vice versa, the following algorithm is applied. ]*/ + /* Codes_SRS_UWS_FRAME_ENCODER_01_040: [ The same algorithm applies regardless of the direction of the translation, e.g., the same steps are applied to mask the data as to unmask the data. ]*/ + for (i = 0; i < length; i++) + { + /* Codes_SRS_UWS_FRAME_ENCODER_01_041: [ Octet i of the transformed data ("transformed-octet-i") is the XOR of octet i of the original data ("original-octet-i") with octet at index i modulo 4 of the masking key ("masking-key-octet-j"): ]*/ + buffer[header_bytes + i] = ((unsigned char*)payload)[i] ^ buffer[header_bytes - 4 + (i % 4)]; + } + } + else + { + (void)memcpy(buffer + header_bytes, payload, length); + } + } + } + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/vector.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,338 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +#include "azure_c_shared_utility/vector_types_internal.h" + +VECTOR_HANDLE VECTOR_create(size_t elementSize) +{ + VECTOR_HANDLE result; + + /* Codes_SRS_VECTOR_10_002: [VECTOR_create shall fail and return NULL if elementsize is 0.] */ + if (elementSize == 0) + { + LogError("invalid elementSize(%zd).", elementSize); + result = NULL; + } + else + { + result = (VECTOR*)malloc(sizeof(VECTOR)); + /* Codes_SRS_VECTOR_10_002 : [VECTOR_create shall fail and return NULL if malloc fails.] */ + if (result == NULL) + { + LogError("malloc failed."); + } + else + { + /* Codes_SRS_VECTOR_10_001: [VECTOR_create shall allocate a VECTOR_HANDLE that will contain an empty vector.The size of each element is given with the parameter elementSize.] */ + result->storage = NULL; + result->count = 0; + result->elementSize = elementSize; + } + } + return result; +} + +void VECTOR_destroy(VECTOR_HANDLE handle) +{ + /* Codes_SRS_VECTOR_10_009: [VECTOR_destroy shall return if the given handle is NULL.] */ + if (handle == NULL) + { + LogError("invalid argument handle(NULL)."); + } + else + { + /* Codes_SRS_VECTOR_10_008: [VECTOR_destroy shall free the given handle and its internal storage.] */ + free(handle->storage); + free(handle); + } +} + +VECTOR_HANDLE VECTOR_move(VECTOR_HANDLE handle) +{ + VECTOR_HANDLE result; + if (handle == NULL) + { + /* Codes_SRS_VECTOR_10_005: [VECTOR_move shall fail and return NULL if the given handle is NULL.] */ + LogError("invalid argument - handle(NULL)."); + result = NULL; + } + else + { + result = (VECTOR*)malloc(sizeof(VECTOR)); + if (result == NULL) + { + /* Codes_SRS_VECTOR_10_006: [VECTOR_move shall fail and return NULL if malloc fails.] */ + LogError("malloc failed."); + } + else + { + /* Codes_SRS_VECTOR_10_004: [VECTOR_move shall allocate a VECTOR_HANDLE and move the data to it from the given handle.] */ + result->count = handle->count; + result->elementSize = handle->elementSize; + result->storage = handle->storage; + + handle->storage = NULL; + handle->count = 0; + } + } + return result; +} + +/* insertion */ + +int VECTOR_push_back(VECTOR_HANDLE handle, const void* elements, size_t numElements) +{ + int result; + if (handle == NULL || elements == NULL || numElements == 0) + { + /* Codes_SRS_VECTOR_10_011: [VECTOR_push_back shall fail and return non-zero if `handle` is NULL.] */ + /* Codes_SRS_VECTOR_10_034: [VECTOR_push_back shall fail and return non-zero if `elements` is NULL.] */ + /* Codes_SRS_VECTOR_10_035: [VECTOR_push_back shall fail and return non-zero if `numElements` is 0.] */ + LogError("invalid argument - handle(%p), elements(%p), numElements(%zd).", handle, elements, numElements); + result = __FAILURE__; + } + else + { + size_t curSize = handle->elementSize * handle->count; + size_t appendSize = handle->elementSize * numElements; + + void* temp = realloc(handle->storage, curSize + appendSize); + if (temp == NULL) + { + /* Codes_SRS_VECTOR_10_012: [VECTOR_push_back shall fail and return non-zero if memory allocation fails.] */ + LogError("realloc failed."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_VECTOR_10_013: [VECTOR_push_back shall append the given elements and return 0 indicating success.] */ + (void)memcpy((unsigned char*)temp + curSize, elements, appendSize); + handle->storage = temp; + handle->count += numElements; + result = 0; + } + } + return result; +} + +/* removal */ + +void VECTOR_erase(VECTOR_HANDLE handle, void* elements, size_t numElements) +{ + if (handle == NULL || elements == NULL || numElements == 0) + { + /* Codes_SRS_VECTOR_10_015: [VECTOR_erase shall return if `handle` is NULL.] */ + /* Codes_SRS_VECTOR_10_038: [VECTOR_erase shall return if `elements` is NULL.] */ + /* Codes_SRS_VECTOR_10_039: [VECTOR_erase shall return if `numElements` is 0.] */ + LogError("invalid argument - handle(%p), elements(%p), numElements(%zd).", handle, elements, numElements); + } + else + { + if (elements < handle->storage) + { + /* Codes_SRS_VECTOR_10_040: [VECTOR_erase shall return if `elements` is out of bound.] */ + LogError("invalid argument elements(%p) is not a member of this object.", elements); + } + else + { + ptrdiff_t diff = ((unsigned char*)elements) - ((unsigned char*)handle->storage); + if ((diff % handle->elementSize) != 0) + { + /* Codes_SRS_VECTOR_10_041: [VECTOR_erase shall return if elements is misaligned.] */ + LogError("invalid argument - elements(%p) is misaligned", elements); + } + else + { + /* Compute the arguments needed for memmove. */ + unsigned char* src = (unsigned char*)elements + (handle->elementSize * numElements); + unsigned char* srcEnd = (unsigned char*)handle->storage + (handle->elementSize * handle->count); + if (src > srcEnd) + { + /* Codes_SRS_VECTOR_10_040: [VECTOR_erase shall return if `elements` is out of bound.] */ + LogError("invalid argument - numElements(%zd) is out of bound.", numElements); + } + else + { + /* Codes_SRS_VECTOR_10_014: [VECTOR_erase shall remove the 'numElements' starting at 'elements' and reduce its internal storage.] */ + handle->count -= numElements; + if (handle->count == 0) + { + free(handle->storage); + handle->storage = NULL; + } + else + { + void* tmp; + (void)memmove(elements, src, srcEnd - src); + tmp = realloc(handle->storage, (handle->elementSize * handle->count)); + if (tmp == NULL) + { + LogInfo("realloc failed. Keeping original internal storage pointer."); + } + else + { + handle->storage = tmp; + } + } + } + } + } + } +} + +void VECTOR_clear(VECTOR_HANDLE handle) +{ + /* Codes_SRS_VECTOR_10_017: [VECTOR_clear shall if the object is NULL or empty.] */ + if (handle == NULL) + { + LogError("invalid argument handle(NULL)."); + } + else + { + /* Codes_SRS_VECTOR_10_016: [VECTOR_clear shall remove all elements from the object and release internal storage.] */ + free(handle->storage); + handle->storage = NULL; + handle->count = 0; + } +} + +/* access */ + +void* VECTOR_element(VECTOR_HANDLE handle, size_t index) +{ + void* result; + if (handle == NULL) + { + /* Codes_SRS_VECTOR_10_019: [VECTOR_element shall fail and return NULL if handle is NULL.] */ + LogError("invalid argument handle(NULL)."); + result = NULL; + } + else + { + if (index >= handle->count) + { + /* Codes_SRS_VECTOR_10_020: [VECTOR_element shall fail and return NULL if the given index is out of range.] */ + LogError("invalid argument - index(%zd); should be >= 0 and < %zd.", index, handle->count); + result = NULL; + } + else + { + /* Codes_SRS_VECTOR_10_018: [VECTOR_element shall return the element at the given index.] */ + result = (unsigned char*)handle->storage + (handle->elementSize * index); + } + } + return result; +} + +void* VECTOR_front(VECTOR_HANDLE handle) +{ + void* result; + if (handle == NULL) + { + /* Codes_SRS_VECTOR_10_022: [VECTOR_front shall fail and return NULL if handle is NULL.] */ + LogError("invalid argument handle (NULL)."); + result = NULL; + } + else + { + if (handle->count == 0) + { + /* Codes_SRS_VECTOR_10_028: [VECTOR_front shall return NULL if the vector is empty.] */ + LogError("vector is empty."); + result = NULL; + } + else + { + /* Codes_SRS_VECTOR_10_021: [VECTOR_front shall return a pointer to the element at index 0.] */ + result = handle->storage; + } + } + return result; +} + +void* VECTOR_back(VECTOR_HANDLE handle) +{ + void* result; + if (handle == NULL) + { + /* Codes_SRS_VECTOR_10_024: [VECTOR_back shall fail and return NULL if handle is NULL.] */ + LogError("invalid argument handle (NULL)."); + result = NULL; + } + else + { + if (handle->count == 0) + { + /* Codes_SRS_VECTOR_10_029: [VECTOR_back shall return NULL if the vector is empty.] */ + LogError("vector is empty."); + result = NULL; + } + else + { + /* Codes_SRS_VECTOR_10_023: [VECTOR_front shall return the last element of the vector.] */ + result = (unsigned char*)handle->storage + (handle->elementSize * (handle->count - 1)); + } + } + return result; +} + +void* VECTOR_find_if(VECTOR_HANDLE handle, PREDICATE_FUNCTION pred, const void* value) +{ + void* result; + if (handle == NULL || pred == NULL) + { + /* Codes_SRS_VECTOR_10_030: [VECTOR_find_if shall fail and return NULL if `handle` is NULL.] */ + /* Codes_SRS_VECTOR_10_036: [VECTOR_find_if shall fail and return NULL if `pred` is NULL.] */ + LogError("invalid argument - handle(%p), pred(%p)", handle, pred); + result = NULL; + } + else + { + size_t i; + for (i = 0; i < handle->count; ++i) + { + if (true == pred((unsigned char*)handle->storage + (handle->elementSize * i), value)) + { + /* Codes_SRS_VECTOR_10_031: [VECTOR_find_if shall return the first element in the vector that matches `pred`.] */ + break; + } + } + + if (i == handle->count) + { + /* Codes_SRS_VECTOR_10_032: [VECTOR_find_if shall return NULL if no matching element is found.] */ + result = NULL; + } + else + { + /* Codes_SRS_VECTOR_10_031: [VECTOR_find_if shall return the first element in the vector that matches `pred`.]*/ + result = (unsigned char*)handle->storage + (handle->elementSize * i); + } + } + return result; +} + +/* capacity */ + +size_t VECTOR_size(VECTOR_HANDLE handle) +{ + size_t result; + if (handle == NULL) + { + /* Codes_SRS_VECTOR_10_026: [**VECTOR_size shall return 0 if the given handle is NULL.] */ + LogError("invalid argument handle(NULL)."); + result = 0; + } + else + { + /* Codes_SRS_VECTOR_10_025: [VECTOR_size shall return the number of elements stored with the given handle.] */ + result = handle->count; + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/wsio.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,826 @@ + // Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/wsio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/uws_client.h" +#include "azure_c_shared_utility/optimize_size.h" + +static const char* WSIO_OPTIONS = "WSIOOptions"; + +typedef enum IO_STATE_TAG +{ + IO_STATE_NOT_OPEN, + IO_STATE_OPENING, + IO_STATE_OPEN, + IO_STATE_CLOSING, + IO_STATE_ERROR +} IO_STATE; + +typedef struct PENDING_IO_TAG +{ + ON_SEND_COMPLETE on_send_complete; + void* callback_context; + void* wsio; +} PENDING_IO; + +typedef struct WSIO_INSTANCE_TAG +{ + ON_BYTES_RECEIVED on_bytes_received; + void* on_bytes_received_context; + ON_IO_OPEN_COMPLETE on_io_open_complete; + void* on_io_open_complete_context; + ON_IO_ERROR on_io_error; + void* on_io_error_context; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + void* on_io_close_complete_context; + IO_STATE io_state; + SINGLYLINKEDLIST_HANDLE pending_io_list; + UWS_CLIENT_HANDLE uws; +} WSIO_INSTANCE; + +static void indicate_error(WSIO_INSTANCE* wsio_instance) +{ + wsio_instance->io_state = IO_STATE_ERROR; + wsio_instance->on_io_error(wsio_instance->on_io_error_context); +} + +static void indicate_open_complete(WSIO_INSTANCE* ws_io_instance, IO_OPEN_RESULT open_result) +{ + ws_io_instance->on_io_open_complete(ws_io_instance->on_io_open_complete_context, open_result); +} + +static void complete_send_item(LIST_ITEM_HANDLE pending_io_list_item, IO_SEND_RESULT io_send_result) +{ + PENDING_IO* pending_io = (PENDING_IO*)singlylinkedlist_item_get_value(pending_io_list_item); + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)pending_io->wsio; + + /* Codes_SRS_WSIO_01_145: [ Removing it from the list shall be done by calling `singlylinkedlist_remove`. ]*/ + if (singlylinkedlist_remove(wsio_instance->pending_io_list, pending_io_list_item) != 0) + { + LogError("Failed removing pending IO from linked list."); + } + + /* Codes_SRS_WSIO_01_105: [ The argument `on_send_complete` shall be optional, if NULL is passed by the caller then no send complete callback shall be triggered. ]*/ + if (pending_io->on_send_complete != NULL) + { + pending_io->on_send_complete(pending_io->callback_context, io_send_result); + } + + /* Codes_SRS_WSIO_01_144: [ Also the pending IO data shall be freed. ]*/ + free(pending_io); +} + +static void on_underlying_ws_send_frame_complete(void* context, WS_SEND_FRAME_RESULT ws_send_frame_result) +{ + if (context == NULL) + { + LogError("NULL context for on_underlying_ws_send_frame_complete"); + } + else + { + IO_SEND_RESULT io_send_result; + LIST_ITEM_HANDLE list_item_handle = (LIST_ITEM_HANDLE)context; + + /* Codes_SRS_WSIO_01_143: [ When `on_underlying_ws_send_frame_complete` is called after sending a WebSocket frame, the pending IO shall be removed from the list. ]*/ + switch (ws_send_frame_result) + { + default: + /* Codes_SRS_WSIO_01_148: [ When `on_underlying_ws_send_frame_complete` is called with any other error code, the callback `on_send_complete` shall be called with `IO_SEND_ERROR`. ]*/ + LogError("Frame send error with result %d", (int)ws_send_frame_result); + io_send_result = IO_SEND_ERROR; + break; + + case WS_SEND_FRAME_OK: + /* Codes_SRS_WSIO_01_146: [ When `on_underlying_ws_send_frame_complete` is called with `WS_SEND_OK`, the callback `on_send_complete` shall be called with `IO_SEND_OK`. ]*/ + io_send_result = IO_SEND_OK; + break; + + case WS_SEND_FRAME_CANCELLED: + /* Codes_SRS_WSIO_01_147: [ When `on_underlying_ws_send_frame_complete` is called with `WS_SEND_CANCELLED`, the callback `on_send_complete` shall be called with `IO_SEND_CANCELLED`. ]*/ + io_send_result = IO_SEND_CANCELLED; + break; + } + + complete_send_item(list_item_handle, io_send_result); + } +} + +static void on_underlying_ws_close_complete(void* context) +{ + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context; + if (wsio_instance == NULL) + { + /* Codes_SRS_WSIO_01_161: [ If the context passed to `on_underlying_ws_close_complete` is NULL, `on_underlying_ws_close_complete` shall do nothing. ]*/ + LogError("NULL context passed to on_underlying_ws_close_complete"); + } + else + { + wsio_instance->io_state = IO_STATE_NOT_OPEN; + + /* Codes_SRS_WSIO_01_160: [ If NULL was passed to `wsio_close` no callback shall be called. ]*/ + if (wsio_instance->on_io_close_complete != NULL) + { + /* Codes_SRS_WSIO_01_159: [ When `on_underlying_ws_close_complete` while the IO is closing (after `wsio_close`), the close shall be indicated up by calling the `on_io_close_complete` callback passed to `wsio_close`. ]*/ + /* Codes_SRS_WSIO_01_163: [ When `on_io_close_complete` is called, the context passed to `wsio_close` shall be passed as argument to `on_io_close_complete`. ]*/ + wsio_instance->on_io_close_complete(wsio_instance->on_io_close_complete_context); + } + } +} + +static int internal_close(WSIO_INSTANCE* wsio_instance, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) +{ + int result; + + /* Codes_SRS_WSIO_01_089: [ `wsio_close` after a `wsio_close` shall fail and return a non-zero value. ]*/ + /* Codes_SRS_WSIO_01_088: [ `wsio_close` when no open action has been issued shall fail and return a non-zero value. ]*/ + if (wsio_instance->io_state == IO_STATE_NOT_OPEN) + { + LogError("wsio_close when not open."); + result = __FAILURE__; + } + else + { + if (wsio_instance->io_state == IO_STATE_OPENING) + { + wsio_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(wsio_instance, IO_OPEN_CANCELLED); + result = 0; + } + else if (wsio_instance->io_state == IO_STATE_CLOSING) + { + LogError("Already closing"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE first_pending_io; + + wsio_instance->io_state = IO_STATE_CLOSING; + + wsio_instance->on_io_close_complete = on_io_close_complete; + wsio_instance->on_io_close_complete_context = on_io_close_complete_context; + + /* Codes_SRS_WSIO_01_087: [ `wsio_close` shall call `uws_client_close_async` while passing as argument the IO handle created in `wsio_create`. ]*/ + if (uws_client_close_async(wsio_instance->uws, on_underlying_ws_close_complete, wsio_instance) != 0) + { + /* Codes_SRS_WSIO_01_164: [ When uws_client_close_async fails, wsio_close shall call the on_io_close_complete callback and continue. ] */ + if (wsio_instance->on_io_close_complete != NULL) + { + wsio_instance->on_io_close_complete(wsio_instance->on_io_close_complete_context); + } + } + + /* Codes_SRS_WSIO_01_085: [ `wsio_close` shall close the websockets IO if an open action is either pending or has completed successfully (if the IO is open). ]*/ + /* Codes_SRS_WSIO_01_091: [ `wsio_close` shall obtain all the pending IO items by repetitively querying for the head of the pending IO list and freeing that head item. ]*/ + /* Codes_SRS_WSIO_01_092: [ Obtaining the head of the pending IO list shall be done by calling `singlylinkedlist_get_head_item`. ]*/ + while ((first_pending_io = singlylinkedlist_get_head_item(wsio_instance->pending_io_list)) != NULL) + { + complete_send_item(first_pending_io, IO_SEND_CANCELLED); + } + + /* Codes_SRS_WSIO_01_133: [ On success `wsio_close` shall return 0. ]*/ + result = 0; + wsio_instance->io_state = IO_STATE_NOT_OPEN; + } + } + return result; +} + +CONCRETE_IO_HANDLE wsio_create(void* io_create_parameters) +{ + /* Codes_SRS_WSIO_01_066: [ `io_create_parameters` shall be used as a `WSIO_CONFIG*` . ]*/ + WSIO_CONFIG* ws_io_config = (WSIO_CONFIG*)io_create_parameters; + WSIO_INSTANCE* result; + + /* Codes_SRS_WSIO_01_065: [ If the argument `io_create_parameters` is NULL then `wsio_create` shall return NULL. ]*/ + if ((ws_io_config == NULL) || + /* Codes_SRS_WSIO_01_067: [ If any of the members `hostname`, `resource_name` or `protocol` is NULL in `WSIO_CONFIG` then `wsio_create` shall return NULL. ]*/ + (ws_io_config->hostname == NULL) || + (ws_io_config->resource_name == NULL) || + (ws_io_config->protocol == NULL)) + { + LogError("NULL io_create_parameters."); + result = NULL; + } + else + { + /* Codes_SRS_WSIO_01_001: [`wsio_create` shall create an instance of wsio and return a non-NULL handle to it.] */ + result = (WSIO_INSTANCE*)malloc(sizeof(WSIO_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_WSIO_01_068: [ If allocating memory for the new wsio instance fails then `wsio_create` shall return NULL. ]*/ + LogError("Cannot allocate memory for the new WSIO instance."); + } + else + { + WS_PROTOCOL protocols; + + protocols.protocol = ws_io_config->protocol; + + result->on_bytes_received = NULL; + result->on_bytes_received_context = NULL; + result->on_io_open_complete = NULL; + result->on_io_open_complete_context = NULL; + result->on_io_error = NULL; + result->on_io_error_context = NULL; + result->on_io_close_complete = NULL; + result->on_io_close_complete_context = NULL; + + /* Codes_SRS_WSIO_01_070: [ The underlying uws instance shall be created by calling `uws_client_create_with_io`. ]*/ + /* Codes_SRS_WSIO_01_071: [ The arguments for `uws_client_create_with_io` shall be: ]*/ + /* Codes_SRS_WSIO_01_185: [ - `underlying_io_interface` shall be set to the `underlying_io_interface` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + /* Codes_SRS_WSIO_01_186: [ - `underlying_io_parameters` shall be set to the `underlying_io_parameters` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + /* Codes_SRS_WSIO_01_072: [ - `hostname` set to the `hostname` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + /* Codes_SRS_WSIO_01_130: [ - `port` set to the `port` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + /* Codes_SRS_WSIO_01_128: [ - `resource_name` set to the `resource_name` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + /* Codes_SRS_WSIO_01_129: [ - `protocols` shall be filled with only one structure, that shall have the `protocol` set to the value of the `protocol` field in the `io_create_parameters` passed to `wsio_create`. ]*/ + result->uws = uws_client_create_with_io(ws_io_config->underlying_io_interface, ws_io_config->underlying_io_parameters, ws_io_config->hostname, ws_io_config->port, ws_io_config->resource_name, &protocols, 1); + if (result->uws == NULL) + { + /* Codes_SRS_WSIO_01_075: [ If `uws_client_create_with_io` fails, then `wsio_create` shall fail and return NULL. ]*/ + LogError("Cannot create uws instance."); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_WSIO_01_076: [ `wsio_create` shall create a pending send IO list that is to be used to queue send packets by calling `singlylinkedlist_create`. ]*/ + result->pending_io_list = singlylinkedlist_create(); + if (result->pending_io_list == NULL) + { + /* Codes_SRS_WSIO_01_077: [ If `singlylinkedlist_create` fails then `wsio_create` shall fail and return NULL. ]*/ + LogError("Cannot create singly linked list."); + uws_client_destroy(result->uws); + free(result); + result = NULL; + } + else + { + result->io_state = IO_STATE_NOT_OPEN; + } + } + } + } + + return result; +} + +void wsio_destroy(CONCRETE_IO_HANDLE ws_io) +{ + /* Codes_SRS_WSIO_01_079: [ If `ws_io` is NULL, `wsio_destroy` shall do nothing. ]*/ + if (ws_io == NULL) + { + LogError("NULL handle"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + if (wsio_instance->io_state != IO_STATE_NOT_OPEN) + { + internal_close(wsio_instance, NULL, NULL); + } + + /* Codes_SRS_WSIO_01_078: [ `wsio_destroy` shall free all resources associated with the wsio instance. ]*/ + /* Codes_SRS_WSIO_01_080: [ `wsio_destroy` shall destroy the uws instance created in `wsio_create` by calling `uws_client_destroy`. ]*/ + uws_client_destroy(wsio_instance->uws); + /* Codes_SRS_WSIO_01_081: [ `wsio_destroy` shall free the list used to track the pending send IOs by calling `singlylinkedlist_destroy`. ]*/ + singlylinkedlist_destroy(wsio_instance->pending_io_list); + free(ws_io); + } +} + +static void on_underlying_ws_open_complete(void* context, WS_OPEN_RESULT open_result) +{ + if (context == NULL) + { + /* Codes_SRS_WSIO_01_138: [ When `on_underlying_ws_open_complete` is called with a NULL context, it shall do nothing. ]*/ + LogError("NULL context in on_underlying_ws_open_complete"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context; + + switch (wsio_instance->io_state) + { + default: + /* Codes_SRS_WSIO_01_142: [ When `on_underlying_ws_open_complete` is called while in the CLOSING state an error shall be indicated by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + case IO_STATE_CLOSING: + /* Codes_SRS_WSIO_01_141: [ When `on_underlying_ws_open_complete` is called while in the ERROR state it shall indicate an error by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + case IO_STATE_ERROR: + /* Codes_SRS_WSIO_01_139: [ When `on_underlying_ws_open_complete` is called while in OPEN state it shall indicate an error by calling the `on_io_error` callback passed to `wsio_open` and switch to the ERROR state. ]*/ + case IO_STATE_OPEN: + /* Codes_SRS_WSIO_01_140: [ When calling `on_io_error`, the `on_io_error_context` argument given in `wsio_open` shall be passed to the callback `on_io_error`. ]*/ + indicate_error(wsio_instance); + break; + + case IO_STATE_OPENING: + wsio_instance->io_state = IO_STATE_OPEN; + + switch (open_result) + { + default: + /* Codes_SRS_WSIO_01_137: [ When `on_underlying_ws_open_complete` is called with any other error code while the IO is opening, the callback `on_io_open_complete` shall be called with `IO_OPEN_ERROR`. ]*/ + wsio_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(wsio_instance, IO_OPEN_ERROR); + break; + + case WS_OPEN_CANCELLED: + /* Codes_SRS_WSIO_01_149: [ When `on_underlying_ws_open_complete` is called with `WS_OPEN_CANCELLED` while the IO is opening, the callback `on_io_open_complete` shall be called with `IO_OPEN_CANCELLED`. ]*/ + wsio_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(wsio_instance, IO_OPEN_CANCELLED); + break; + + case WS_OPEN_OK: + /* Codes_SRS_WSIO_01_136: [ When `on_underlying_ws_open_complete` is called with `WS_OPEN_OK` while the IO is opening, the callback `on_io_open_complete` shall be called with `IO_OPEN_OK`. ]*/ + wsio_instance->io_state = IO_STATE_OPEN; + indicate_open_complete(wsio_instance, IO_OPEN_OK); + break; + } + + break; + } + } +} + +static void on_underlying_ws_frame_received(void* context, unsigned char frame_type, const unsigned char* buffer, size_t size) +{ + if (context == NULL) + { + /* Codes_SRS_WSIO_01_150: [ If `on_underlying_ws_frame_received` is called with NULL context it shall do nothing. ]*/ + LogError("NULL context for on_underlying_ws_frame_received"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context; + + if (wsio_instance->io_state != IO_STATE_OPEN) + { + /* Codes_SRS_WSIO_01_126: [ If `on_underlying_ws_frame_received` is called while the IO is in any state other than OPEN, it shall do nothing. ]*/ + LogError("on_underlying_ws_frame_received called in a bad state."); + } + else + { + if (frame_type != WS_FRAME_TYPE_BINARY) + { + /* Codes_SRS_WSIO_01_151: [ If the WebSocket frame type is not binary then an error shall be indicated by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + LogError("Invalid non binary WebSocket frame received."); + /* Codes_SRS_WSIO_01_152: [ When calling `on_io_error`, the `on_io_error_context` argument given in `wsio_open` shall be passed to the callback `on_io_error`. ]*/ + indicate_error(wsio_instance); + } + else + { + /* Codes_SRS_WSIO_01_153: [ When `on_underlying_ws_frame_received` is called with zero `size`, no bytes shall be indicated up as received. ]*/ + if (size > 0) + { + if (buffer == NULL) + { + /* Codes_SRS_WSIO_01_154: [ When `on_underlying_ws_frame_received` is called with a positive `size` and a NULL `buffer`, an error shall be indicated by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + LogError("NULL buffer received for Websocket frame with positive payload length."); + indicate_error(wsio_instance); + } + else + { + /* Codes_SRS_WSIO_01_124: [ When `on_underlying_ws_frame_received` is called the bytes in the frame shall be indicated by calling the `on_bytes_received` callback passed to `wsio_open`. ]*/ + /* Codes_SRS_WSIO_01_125: [ When calling `on_bytes_received`, the `on_bytes_received_context` argument given in `wsio_open` shall be passed to the callback `on_bytes_received`. ]*/ + wsio_instance->on_bytes_received(wsio_instance->on_bytes_received_context, buffer, size); + } + } + } + } + } +} + +static void on_underlying_ws_peer_closed(void* context, uint16_t* close_code, const unsigned char* extra_data, size_t extra_data_length) +{ + /* Codes_SRS_WSIO_01_168: [ The `close_code`, `extra_data` and `extra_data_length` arguments shall be ignored. ]*/ + (void)close_code; + (void)extra_data; + (void)extra_data_length; + + if (context == NULL) + { + /* Codes_SRS_WSIO_01_167: [ If `on_underlying_ws_peer_closed` is called with a NULL context it shall do nothing. ]*/ + LogError("NULL context for on_underlying_ws_peer_closed"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context; + + switch (wsio_instance->io_state) + { + default: + /* Codes_SRS_WSIO_01_166: [ When `on_underlying_ws_peer_closed` and the state of the IO is OPEN an error shall be indicated by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + case IO_STATE_OPEN: + /* Codes_SRS_WSIO_01_169: [ When `on_underlying_ws_peer_closed` and the state of the IO is CLOSING an error shall be indicated by calling the `on_io_error` callback passed to `wsio_open`. ]*/ + case IO_STATE_CLOSING: + indicate_error(wsio_instance); + break; + + case IO_STATE_NOT_OPEN: + // Codes_SRS_WSIO_07_001: [When `on_underlying_ws_peer_closed` and the state of the IO is NOT_OPEN an error will be raised and the io_state will remain as NOT_OPEN] + indicate_error(wsio_instance); + wsio_instance->io_state = IO_STATE_NOT_OPEN; + break; + + case IO_STATE_OPENING: + /* Codes_SRS_WSIO_01_170: [ When `on_underlying_ws_peer_closed` and the state of the IO is OPENING an error shall be indicated by calling the `on_io_open_complete` callback passed to `wsio_open` with the error code `IO_OPEN_ERROR`. ]*/ + wsio_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(wsio_instance, IO_OPEN_ERROR); + break; + } + } +} + +static void on_underlying_ws_error(void* context, WS_ERROR ws_error) +{ + (void)ws_error; + /* Don't have much to do with the error here */ + LogError("on_underlying_ws_error called with error code %d", (int)ws_error); + + if (context == NULL) + { + /* Codes_SRS_WSIO_01_135: [ When `on_underlying_ws_error` is called with a NULL context, it shall do nothing. ]*/ + LogError("NULL context in on_underlying_ws_error"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context; + + if (wsio_instance->io_state == IO_STATE_OPENING) + { + /* Codes_SRS_WSIO_01_123: [ When calling `on_io_error`, the `on_io_error_context` argument given in `wsio_open` shall be passed to the callback `on_io_error`. ]*/ + wsio_instance->on_io_open_complete(wsio_instance->on_io_open_complete_context, IO_OPEN_ERROR); + wsio_instance->io_state = IO_STATE_NOT_OPEN; + } + else + { + /* Codes_SRS_WSIO_01_123: [ When calling `on_io_error`, the `on_io_error_context` argument given in `wsio_open` shall be passed to the callback `on_io_error`. ]*/ + wsio_instance->on_io_error(wsio_instance->on_io_error_context); + } + } +} + +int wsio_open(CONCRETE_IO_HANDLE ws_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result = 0; + + if ((ws_io == NULL) || + (on_io_open_complete == NULL) || + (on_bytes_received == NULL) || + (on_io_error == NULL)) + { + /* Codes_SRS_WSIO_01_132: [ If any of the arguments `ws_io`, `on_io_open_complete`, `on_bytes_received`, `on_io_error` is NULL, `wsio_open` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: ws_io=%p, on_io_open_complete=%p, on_bytes_received=%p, on_io_error=%p", + ws_io, on_io_open_complete, on_bytes_received, on_io_error); + result = __FAILURE__; + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + if (wsio_instance->io_state != IO_STATE_NOT_OPEN) + { + /* Codes_SRS_WSIO_01_165: [ `wsio_open` when CLOSING shall fail and return a non-zero value. ]*/ + /* Codes_SRS_WSIO_01_131: [ `wsio_open` when already OPEN or OPENING shall fail and return a non-zero value. ]*/ + LogError("wsio has already been opened current state: %d", wsio_instance->io_state); + result = __FAILURE__; + } + else + { + wsio_instance->on_bytes_received = on_bytes_received; + wsio_instance->on_bytes_received_context = on_bytes_received_context; + wsio_instance->on_io_open_complete = on_io_open_complete; + wsio_instance->on_io_open_complete_context = on_io_open_complete_context; + wsio_instance->on_io_error = on_io_error; + wsio_instance->on_io_error_context = on_io_error_context; + + wsio_instance->io_state = IO_STATE_OPENING; + + /* Codes_SRS_WSIO_01_082: [ `wsio_open` shall open the underlying uws instance by calling `uws_client_open_async` and providing the uws handle created in `wsio_create` as argument. ] */ + if (uws_client_open_async(wsio_instance->uws, on_underlying_ws_open_complete, wsio_instance, on_underlying_ws_frame_received, wsio_instance, on_underlying_ws_peer_closed, wsio_instance, on_underlying_ws_error, wsio_instance) != 0) + { + /* Codes_SRS_WSIO_01_084: [ If opening the underlying uws instance fails then `wsio_open` shall fail and return a non-zero value. ]*/ + LogError("Opening the uws instance failed."); + wsio_instance->io_state = IO_STATE_NOT_OPEN; + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_083: [ On success, `wsio_open` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int wsio_close(CONCRETE_IO_HANDLE ws_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) +{ + int result = 0; + + if (ws_io == NULL) + { + /* Codes_SRS_WSIO_01_086: [ if `ws_io` is NULL, `wsio_close` shall return a non-zero value. ]*/ + LogError("NULL handle"); + result = __FAILURE__; + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + if (internal_close(wsio_instance, on_io_close_complete, on_io_close_complete_context) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +int wsio_send(CONCRETE_IO_HANDLE ws_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_SRS_WSIO_01_100: [ If any of the arguments `ws_io` or `buffer` are NULL, `wsio_send` shall fail and return a non-zero value. ]*/ + if ((ws_io == NULL) || + (buffer == NULL) || + /* Codes_SRS_WSIO_01_101: [ If `size` is zero then `wsio_send` shall fail and return a non-zero value. ]*/ + (size == 0)) + { + LogError("Bad arguments: ws_io=%p, buffer=%p, size=%u", + ws_io, buffer, (unsigned int)size); + result = __FAILURE__; + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + if (wsio_instance->io_state != IO_STATE_OPEN) + { + /* Codes_SRS_WSIO_01_099: [ If the wsio is not OPEN (open has not been called or is still in progress) then `wsio_send` shall fail and return a non-zero value. ]*/ + LogError("Attempting to send when not open"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE new_item; + PENDING_IO* pending_socket_io = (PENDING_IO*)malloc(sizeof(PENDING_IO)); + if (pending_socket_io == NULL) + { + /* Codes_SRS_WSIO_01_134: [ If allocating memory for the pending IO data fails, `wsio_send` shall fail and return a non-zero value. ]*/ + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_103: [ The entry shall contain the `on_send_complete` callback and its context. ]*/ + pending_socket_io->on_send_complete = on_send_complete; + pending_socket_io->callback_context = callback_context; + pending_socket_io->wsio = wsio_instance; + + /* Codes_SRS_WSIO_01_102: [ An entry shall be queued in the singly linked list by calling `singlylinkedlist_add`. ]*/ + if ((new_item = singlylinkedlist_add(wsio_instance->pending_io_list, pending_socket_io)) == NULL) + { + /* Codes_SRS_WSIO_01_104: [ If `singlylinkedlist_add` fails, `wsio_send` shall fail and return a non-zero value. ]*/ + free(pending_socket_io); + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_095: [ `wsio_send` shall call `uws_client_send_frame_async`, passing the `buffer` and `size` arguments as they are: ]*/ + /* Codes_SRS_WSIO_01_097: [ The `is_final` argument shall be set to true. ]*/ + /* Codes_SRS_WSIO_01_096: [ The frame type used shall be `WS_FRAME_TYPE_BINARY`. ]*/ + if (uws_client_send_frame_async(wsio_instance->uws, WS_FRAME_TYPE_BINARY, (const unsigned char*)buffer, size, true, on_underlying_ws_send_frame_complete, new_item) != 0) + { + if (singlylinkedlist_remove(wsio_instance->pending_io_list, new_item) != 0) + { + LogError("Failed removing pending IO from linked list."); + } + + free(pending_socket_io); + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_098: [ On success, `wsio_send` shall return 0. ]*/ + result = 0; + } + } + } + } + } + + return result; +} + +void wsio_dowork(CONCRETE_IO_HANDLE ws_io) +{ + if (ws_io == NULL) + { + /* Codes_SRS_WSIO_01_107: [ If the `ws_io` argument is NULL, `wsio_dowork` shall do nothing. ]*/ + LogError("NULL handle"); + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + /* Codes_SRS_WSIO_01_108: [ If the IO is not yet open, `wsio_dowork` shall do nothing. ]*/ + if (wsio_instance->io_state != IO_STATE_NOT_OPEN) + { + /* Codes_SRS_WSIO_01_106: [ `wsio_dowork` shall call `uws_client_dowork` with the uws handle created in `wsio_create`. ]*/ + uws_client_dowork(wsio_instance->uws); + } + } +} + +int wsio_setoption(CONCRETE_IO_HANDLE ws_io, const char* optionName, const void* value) +{ + int result; + if ( + /* Codes_SRS_WSIO_01_109: [ If any of the arguments `ws_io` or `option_name` is NULL `wsio_setoption` shall return a non-zero value. ]*/ + (ws_io == NULL) || + (optionName == NULL) + ) + { + LogError("Bad parameters: ws_io=%p, optionName=%p", + ws_io, optionName); + result = __FAILURE__; + } + else + { + WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io; + + if (strcmp(WSIO_OPTIONS, optionName) == 0) + { + /* Codes_SRS_WSIO_01_183: [ If the option name is `WSIOOptions` then `wsio_setoption` shall call `OptionHandler_FeedOptions` and pass to it the underlying IO handle and the `value` argument. ]*/ + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, wsio_instance->uws) != OPTIONHANDLER_OK) + { + /* Codes_SRS_WSIO_01_184: [ If `OptionHandler_FeedOptions` fails, `wsio_setoption` shall fail and return a non-zero value. ]*/ + LogError("unable to OptionHandler_FeedOptions"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_158: [ On success, `wsio_setoption` shall return 0. ]*/ + result = 0; + } + } + else + { + /* Codes_SRS_WSIO_01_156: [ Otherwise all options shall be passed as they are to uws by calling `uws_client_set_option`. ]*/ + if (uws_client_set_option(wsio_instance->uws, optionName, value) != 0) + { + /* Codes_SRS_WSIO_01_157: [ If `uws_client_set_option` fails, `wsio_setoption` shall fail and return a non-zero value. ]*/ + LogError("Setting the option %s failed", optionName); + result = __FAILURE__; + } + else + { + /* Codes_SRS_WSIO_01_158: [ On success, `wsio_setoption` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static void* wsio_clone_option(const char* name, const void* value) +{ + void *result; + + if ( + (name == NULL) || + (value == NULL) + ) + { + /* Codes_SRS_WSIO_01_174: [ If `wsio_clone_option` is called with NULL `name` or `value` it shall return NULL. ]*/ + LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value); + result = NULL; + } + else + { + if (strcmp(name, WSIO_OPTIONS) == 0) + { + /* Codes_SRS_WSIO_01_171: [** `wsio_clone_option` called with `name` being `WSIOOptions` shall return the same value. ]*/ + result = (void*)value; + } + else + { + /* Codes_SRS_WSIO_01_173: [ `wsio_clone_option` called with any other option name than `WSIOOptions` shall return NULL. ]*/ + LogError("unknown option: %s", name); + result = NULL; + } + } + + return result; +} + +static void wsio_destroy_option(const char* name, const void* value) +{ + if ( + (name == NULL) || + (value == NULL) + ) + { + /* Codes_SRS_WSIO_01_177: [ If `wsio_destroy_option` is called with NULL `name` or `value` it shall do nothing. ]*/ + LogError("Bad arguments: const char* name=%p, const void* value=%p", name, value); + } + else + { + if (strcmp(name, WSIO_OPTIONS) == 0) + { + /* Codes_SRS_WSIO_01_175: [ `wsio_destroy_option` called with the option `name` being `WSIOOptions` shall destroy the value by calling `OptionHandler_Destroy`. ]*/ + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + /* Codes_SRS_WSIO_01_176: [ If `wsio_destroy_option` is called with any other `name` it shall do nothing. ]*/ + LogError("unknown option: %s", name); + } + } +} + +OPTIONHANDLER_HANDLE wsio_retrieveoptions(CONCRETE_IO_HANDLE handle) +{ + OPTIONHANDLER_HANDLE result; + if (handle == NULL) + { + /* Codes_SRS_WSIO_01_118: [ If parameter `handle` is `NULL` then `wsio_retrieveoptions` shall fail and return NULL. ]*/ + LogError("parameter handle is NULL"); + result = NULL; + } + else + { + WSIO_INSTANCE* wsio = (WSIO_INSTANCE*)handle; + + /* Codes_SRS_WSIO_01_119: [ `wsio_retrieveoptions` shall call `OptionHandler_Create` to produce an `OPTIONHANDLER_HANDLE` and on success return the new `OPTIONHANDLER_HANDLE` handle. ]*/ + result = OptionHandler_Create(wsio_clone_option, wsio_destroy_option, wsio_setoption); + if (result == NULL) + { + /* Codes_SRS_WSIO_01_120: [ If `OptionHandler_Create` fails then `wsio_retrieveoptions` shall fail and return NULL. ]*/ + LogError("OptionHandler_Create failed"); + } + else + { + /* Codes_SRS_WSIO_01_179: [ When calling `uws_client_retrieve_options` the uws client handle shall be passed to it. ]*/ + /* Codes_SRS_WSIO_01_178: [ `uws_client_retrieve_options` shall add to the option handler one option, whose name shall be `uWSCLientOptions` and the value shall be queried by calling `uws_client_retrieve_options`. ]*/ + OPTIONHANDLER_HANDLE concreteOptions = uws_client_retrieve_options(wsio->uws); + if (concreteOptions == NULL) + { + /* Codes_SRS_WSIO_01_180: [ If `uws_client_retrieve_options` fails, `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("unable to concrete_io_retrieveoptions"); + OptionHandler_Destroy(result); + result = NULL; + } + else + { + /* Codes_SRS_WSIO_01_181: [ Adding the option shall be done by calling `OptionHandler_AddOption`. ]*/ + if (OptionHandler_AddOption(result, WSIO_OPTIONS, concreteOptions) != OPTIONHANDLER_OK) + { + /* Codes_SRS_WSIO_01_182: [ If `OptionHandler_AddOption` fails, `uws_client_retrieve_options` shall fail and return NULL. ]*/ + LogError("unable to OptionHandler_AddOption"); + OptionHandler_Destroy(concreteOptions); + OptionHandler_Destroy(result); + result = NULL; + } + } + } + } + + return result; +} + +static const IO_INTERFACE_DESCRIPTION ws_io_interface_description = +{ + wsio_retrieveoptions, + wsio_create, + wsio_destroy, + wsio_open, + wsio_close, + wsio_send, + wsio_dowork, + wsio_setoption +}; + +const IO_INTERFACE_DESCRIPTION* wsio_get_interface_description(void) +{ + return &ws_io_interface_description; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/xio.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" + +static const char* CONCRETE_OPTIONS = "concreteOptions"; + +typedef struct XIO_INSTANCE_TAG +{ + const IO_INTERFACE_DESCRIPTION* io_interface_description; + CONCRETE_IO_HANDLE concrete_xio_handle; +} XIO_INSTANCE; + +XIO_HANDLE xio_create(const IO_INTERFACE_DESCRIPTION* io_interface_description, const void* xio_create_parameters) +{ + XIO_INSTANCE* xio_instance; + /* Codes_SRS_XIO_01_003: [If the argument io_interface_description is NULL, xio_create shall return NULL.] */ + if ((io_interface_description == NULL) || + /* Codes_SRS_XIO_01_004: [If any io_interface_description member is NULL, xio_create shall return NULL.] */ + (io_interface_description->concrete_io_retrieveoptions == NULL) || + (io_interface_description->concrete_io_create == NULL) || + (io_interface_description->concrete_io_destroy == NULL) || + (io_interface_description->concrete_io_open == NULL) || + (io_interface_description->concrete_io_close == NULL) || + (io_interface_description->concrete_io_send == NULL) || + (io_interface_description->concrete_io_dowork == NULL) || + (io_interface_description->concrete_io_setoption == NULL)) + { + xio_instance = NULL; + } + else + { + xio_instance = (XIO_INSTANCE*)malloc(sizeof(XIO_INSTANCE)); + + /* Codes_SRS_XIO_01_017: [If allocating the memory needed for the IO interface fails then xio_create shall return NULL.] */ + if (xio_instance != NULL) + { + /* Codes_SRS_XIO_01_001: [xio_create shall return on success a non-NULL handle to a new IO interface.] */ + xio_instance->io_interface_description = io_interface_description; + + /* Codes_SRS_XIO_01_002: [In order to instantiate the concrete IO implementation the function concrete_io_create from the io_interface_description shall be called, passing the xio_create_parameters argument.] */ + xio_instance->concrete_xio_handle = xio_instance->io_interface_description->concrete_io_create((void*)xio_create_parameters); + + /* Codes_SRS_XIO_01_016: [If the underlying concrete_io_create call fails, xio_create shall return NULL.] */ + if (xio_instance->concrete_xio_handle == NULL) + { + free(xio_instance); + xio_instance = NULL; + } + } + } + return (XIO_HANDLE)xio_instance; +} + +void xio_destroy(XIO_HANDLE xio) +{ + /* Codes_SRS_XIO_01_007: [If the argument io is NULL, xio_destroy shall do nothing.] */ + if (xio != NULL) + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_006: [xio_destroy shall also call the concrete_io_destroy function that is member of the io_interface_description argument passed to xio_create, while passing as argument to concrete_io_destroy the result of the underlying concrete_io_create handle that was called as part of the xio_create call.] */ + xio_instance->io_interface_description->concrete_io_destroy(xio_instance->concrete_xio_handle); + + /* Codes_SRS_XIO_01_005: [xio_destroy shall free all resources associated with the IO handle.] */ + free(xio_instance); + } +} + +int xio_open(XIO_HANDLE xio, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result; + + if (xio == NULL) + { + /* Codes_SRS_XIO_01_021: [If handle is NULL, xio_open shall return a non-zero value.] */ + result = __FAILURE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_019: [xio_open shall call the specific concrete_xio_open function specified in xio_create, passing callback function and context arguments for three events: open completed, bytes received, and IO error.] */ + if (xio_instance->io_interface_description->concrete_io_open(xio_instance->concrete_xio_handle, on_io_open_complete, on_io_open_complete_context, on_bytes_received, on_bytes_received_context, on_io_error, on_io_error_context) != 0) + { + /* Codes_SRS_XIO_01_022: [If the underlying concrete_io_open fails, xio_open shall return a non-zero value.] */ + result = __FAILURE__; + } + else + { + /* Codes_SRS_XIO_01_020: [On success, xio_open shall return 0.] */ + result = 0; + } + } + + return result; +} + +int xio_close(XIO_HANDLE xio, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result; + + if (xio == NULL) + { + /* Codes_SRS_XIO_01_025: [If handle is NULL, xio_close shall return a non-zero value.] */ + result = __FAILURE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_023: [xio_close shall call the specific concrete_io_close function specified in xio_create.] */ + if (xio_instance->io_interface_description->concrete_io_close(xio_instance->concrete_xio_handle, on_io_close_complete, callback_context) != 0) + { + /* Codes_SRS_XIO_01_026: [If the underlying concrete_io_close fails, xio_close shall return a non-zero value.] */ + result = __FAILURE__; + } + else + { + /* Codes_SRS_XIO_01_024: [On success, xio_close shall return 0.] */ + result = 0; + } + } + + return result; +} + +int xio_send(XIO_HANDLE xio, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_SRS_XIO_01_011: [No error check shall be performed on buffer and size.] */ + /* Codes_SRS_XIO_01_010: [If handle is NULL, xio_send shall return a non-zero value.] */ + if (xio == NULL) + { + result = __FAILURE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_008: [xio_send shall pass the sequence of bytes pointed to by buffer to the concrete IO implementation specified in xio_create, by calling the concrete_io_send function while passing down the buffer and size arguments to it.] */ + /* Codes_SRS_XIO_01_009: [On success, xio_send shall return 0.] */ + /* Codes_SRS_XIO_01_015: [If the underlying concrete_io_send fails, xio_send shall return a non-zero value.] */ + /* Codes_SRS_XIO_01_027: [xio_send shall pass to the concrete_io_send function the on_send_complete and callback_context arguments.] */ + result = xio_instance->io_interface_description->concrete_io_send(xio_instance->concrete_xio_handle, buffer, size, on_send_complete, callback_context); + } + + return result; +} + +void xio_dowork(XIO_HANDLE xio) +{ + /* Codes_SRS_XIO_01_018: [When the handle argument is NULL, xio_dowork shall do nothing.] */ + if (xio != NULL) + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_012: [xio_dowork shall call the concrete XIO implementation specified in xio_create, by calling the concrete_io_dowork function.] */ + xio_instance->io_interface_description->concrete_io_dowork(xio_instance->concrete_xio_handle); + } +} + +int xio_setoption(XIO_HANDLE xio, const char* optionName, const void* value) +{ + int result; + + /* Codes_SRS_XIO_03_030: [If the xio argument or the optionName argument is NULL, xio_setoption shall return a non-zero value.] */ + if (xio == NULL || optionName == NULL) + { + result = __FAILURE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + if (strcmp(CONCRETE_OPTIONS, optionName) == 0) + { + /*then value is a pointer to OPTIONHANDLER_HANDLE*/ + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, xio_instance->concrete_xio_handle) != OPTIONHANDLER_OK) + { + LogError("unable to OptionHandler_FeedOptions"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else /*passthrough*/ + { + /* Codes_SRS_XIO_003_028: [xio_setoption shall pass the optionName and value to the concrete IO implementation specified in xio_create by invoking the concrete_xio_setoption function.] */ + /* Codes_SRS_XIO_03_029: [xio_setoption shall return 0 upon success.] */ + /* Codes_SRS_XIO_03_031: [If the underlying concrete_xio_setoption fails, xio_setOption shall return a non-zero value.] */ + result = xio_instance->io_interface_description->concrete_io_setoption(xio_instance->concrete_xio_handle, optionName, value); + } + } + + return result; +} + +static void* xio_CloneOption(const char* name, const void* value) +{ + void *result; + if ( + (name == NULL) || + (value == NULL) + ) + { + LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value); + result = NULL; + } + else + { + if (strcmp(name, CONCRETE_OPTIONS) == 0) + { + result = (void*)value; + } + else + { + LogError("unknown option: %s", name); + result = NULL; + } + } + return result; +} + + +static void xio_DestroyOption(const char* name, const void* value) +{ + if ( + (name == NULL) || + (value == NULL) + ) + { + LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value); + } + else + { + if (strcmp(name, CONCRETE_OPTIONS) == 0) + { + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + LogError("unknown option: %s", name); + } + } +} + +OPTIONHANDLER_HANDLE xio_retrieveoptions(XIO_HANDLE xio) +{ + OPTIONHANDLER_HANDLE result; + + if (xio == NULL) + { + LogError("invalid argument detected: XIO_HANDLE xio=%p", xio); + result = NULL; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + /*xio_retrieveoptions shall return a OPTIONHANDLER_HANDLE that has 1 option called "underlyingOptions" which is of type OPTIONHANDLER_HANDLE*/ + result = OptionHandler_Create(xio_CloneOption, xio_DestroyOption, (pfSetOption)xio_setoption); + if (result == NULL) + { + LogError("unable to OptionHandler_Create"); + /*return as is*/ + } + else + { + OPTIONHANDLER_HANDLE concreteOptions = xio_instance->io_interface_description->concrete_io_retrieveoptions(xio_instance->concrete_xio_handle); + if (concreteOptions == NULL) + { + LogError("unable to concrete_io_retrieveoptions"); + OptionHandler_Destroy(result); + result = NULL; + } + else + { + if (OptionHandler_AddOption(result, CONCRETE_OPTIONS, concreteOptions) != OPTIONHANDLER_OK) + { + LogError("unable to OptionHandler_AddOption"); + OptionHandler_Destroy(concreteOptions); + OptionHandler_Destroy(result); + result = NULL; + } + else + { + /*all is fine*/ + } + } + } + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c-utility/src/xlogging.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/consolelogger.h" + +#ifndef NO_LOGGING + + +#ifdef WINCE +#include <stdarg.h> + +void consolelogger_log(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...) +{ + va_list args; + va_start(args, format); + + time_t t = time(NULL); + + switch (log_category) + { + case AZ_LOG_INFO: + (void)printf("Info: "); + break; + case AZ_LOG_ERROR: + (void)printf("Error: Time:%.24s File:%s Func:%s Line:%d ", ctime(&t), file, func, line); + break; + default: + break; + } + + (void)vprintf(format, args); + va_end(args); + + (void)log_category; + if (options & LOG_LINE) + { + (void)printf("\r\n"); + } +} +#endif + +LOGGER_LOG global_log_function = consolelogger_log; + +void xlogging_set_log_function(LOGGER_LOG log_function) +{ + global_log_function = log_function; +} + +LOGGER_LOG xlogging_get_log_function(void) +{ + return global_log_function; +} + +#if (defined(_MSC_VER)) && (!(defined WINCE)) + +LOGGER_LOG_GETLASTERROR global_log_function_GetLastError = consolelogger_log_with_GetLastError; + +void xlogging_set_log_function_GetLastError(LOGGER_LOG_GETLASTERROR log_function_GetLastError) +{ + global_log_function_GetLastError = log_function_GetLastError; +} + +LOGGER_LOG_GETLASTERROR xlogging_get_log_function_GetLastError(void) +{ + return global_log_function_GetLastError; +} +#endif + +/* Print up to 16 bytes per line. */ +#define LINE_SIZE 16 + +/* Return the printable char for the provided value. */ +#define PRINTABLE(c) ((c >= ' ') && (c <= '~')) ? (char)c : '.' + +/* Convert the lower nibble of the provided byte to a hexadecimal printable char. */ +#define HEX_STR(c) (((c) & 0xF) < 0xA) ? (char)(((c) & 0xF) + '0') : (char)(((c) & 0xF) - 0xA + 'A') + +void LogBinary(const char* comment, const void* data, size_t size) +{ + char charBuf[LINE_SIZE + 1]; + char hexBuf[LINE_SIZE * 3 + 1]; + size_t countbuf = 0; + size_t i = 0; + const unsigned char* bufAsChar = (const unsigned char*)data; + const unsigned char* startPos = bufAsChar; + + LOG(AZ_LOG_TRACE, LOG_LINE, "%s %lu bytes", comment, size); + + /* Print the whole buffer. */ + for (i = 0; i < size; i++) + { + /* Store the printable value of the char in the charBuf to print. */ + charBuf[countbuf] = PRINTABLE(*bufAsChar); + + /* Convert the high nibble to a printable hexadecimal value. */ + hexBuf[countbuf * 3] = HEX_STR(*bufAsChar >> 4); + + /* Convert the low nibble to a printable hexadecimal value. */ + hexBuf[countbuf * 3 + 1] = HEX_STR(*bufAsChar); + + hexBuf[countbuf * 3 + 2] = ' '; + + countbuf++; + bufAsChar++; + /* If the line is full, print it to start another one. */ + if (countbuf == LINE_SIZE) + { + charBuf[countbuf] = '\0'; + hexBuf[countbuf * 3] = '\0'; + LOG(AZ_LOG_TRACE, LOG_LINE, "%p: %s %s", startPos, hexBuf, charBuf); + countbuf = 0; + startPos = bufAsChar; + } + } + + /* If the last line does not fit the line size. */ + if (countbuf > 0) + { + /* Close the charBuf string. */ + charBuf[countbuf] = '\0'; + + /* Fill the hexBuf with spaces to keep the charBuf alignment. */ + while ((countbuf++) < LINE_SIZE - 1) + { + hexBuf[countbuf * 3] = ' '; + hexBuf[countbuf * 3 + 1] = ' '; + hexBuf[countbuf * 3 + 2] = ' '; + } + hexBuf[countbuf * 3] = '\0'; + + /* Print the last line. */ + LOG(AZ_LOG_TRACE, LOG_LINE, "%p: %s %s", startPos, hexBuf, charBuf); + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/certs.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* This file contains certs needed to communicate with Azure (IoT) */ + +#include "certs.h" + +const char certificates[] = +/* DigiCert Baltimore Root */ +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n" +"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" +"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n" +"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n" +"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n" +"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n" +"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n" +"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n" +"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n" +"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n" +"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n" +"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n" +"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n" +"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n" +"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n" +"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n" +"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n" +"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n" +"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n" +"-----END CERTIFICATE-----\r\n" +/*DigiCert Global Root CA*/ +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\r\n" +"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n" +"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\r\n" +"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\r\n" +"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\r\n" +"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\r\n" +"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\r\n" +"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\r\n" +"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\r\n" +"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\r\n" +"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\r\n" +"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\r\n" +"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\r\n" +"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\r\n" +"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\r\n" +"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\r\n" +"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\r\n" +"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\r\n" +"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\r\n" +"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\r\n" +"-----END CERTIFICATE-----\r\n" +/*D-TRUST Root Class 3 CA 2 2009*/ +"-----BEGIN CERTIFICATE-----\r\n" +"MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF\r\n" +"MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD\r\n" +"bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha\r\n" +"ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM\r\n" +"HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB\r\n" +"BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03\r\n" +"UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42\r\n" +"tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R\r\n" +"ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM\r\n" +"lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp\r\n" +"/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G\r\n" +"A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G\r\n" +"A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj\r\n" +"dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy\r\n" +"MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl\r\n" +"cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js\r\n" +"L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL\r\n" +"BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni\r\n" +"acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0\r\n" +"o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K\r\n" +"zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8\r\n" +"PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y\r\n" +"Johw1+qRzT65ysCQblrGXnRl11z+o+I=\r\n" +"-----END CERTIFICATE-----\r\n" +/*WoSign*/ +"-----BEGIN CERTIFICATE-----\r\n" +"MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV\r\n" +"MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV\r\n" +"BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw\r\n" +"MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX\r\n" +"b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp\r\n" +"dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN\r\n" +"rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U\r\n" +"fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc\r\n" +"f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2\r\n" +"ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M\r\n" +"x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR\r\n" +"aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch\r\n" +"zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar\r\n" +"uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K\r\n" +"mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA\r\n" +"Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv\r\n" +"HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/\r\n" +"BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H\r\n" +"EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1\r\n" +"LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ\r\n" +"MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e\r\n" +"JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN\r\n" +"g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp\r\n" +"dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab\r\n" +"R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ\r\n" +"PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce\r\n" +"xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+\r\n" +"J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl\r\n" +"OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT\r\n" +"ee5Ehr7XHuQe+w==\r\n" +"-----END CERTIFICATE-----\r\n" + +;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/certs.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CERTS_H +#define CERTS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const char certificates[]; + +#ifdef __cplusplus +} +#endif + +#endif /* CERTS_H */
Binary file certs/ms.der has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/parson/package.json Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,12 @@ +{ + "name": "parson", + "version": "0.0.0", + "repo": "kgabis/parson", + "description": "Small json parser and reader", + "keywords": [ "json", "parser" ], + "license": "MIT", + "src": [ + "parson.c", + "parson.h" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/parson/parson.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1998 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2017 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif /* _CRT_SECURE_NO_WARNINGS */ +#endif /* _MSC_VER */ + +#include "parson.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <errno.h> + +/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you + * don't have to. */ +#define sscanf THINK_TWICE_ABOUT_USING_SSCANF + +#define STARTING_CAPACITY 16 +#define MAX_NESTING 2048 +#define FLOAT_FORMAT "%1.17g" + +#define SIZEOF_TOKEN(a) (sizeof(a) - 1) +#define SKIP_CHAR(str) ((*str)++) +#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#undef malloc +#undef free + +static JSON_Malloc_Function parson_malloc = malloc; +static JSON_Free_Function parson_free = free; + +#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */ + +/* Type definitions */ +typedef union json_value_value { + char *string; + double number; + JSON_Object *object; + JSON_Array *array; + int boolean; + int null; +} JSON_Value_Value; + +struct json_value_t { + JSON_Value *parent; + JSON_Value_Type type; + JSON_Value_Value value; +}; + +struct json_object_t { + JSON_Value *wrapping_value; + char **names; + JSON_Value **values; + size_t count; + size_t capacity; +}; + +struct json_array_t { + JSON_Value *wrapping_value; + JSON_Value **items; + size_t count; + size_t capacity; +}; + +/* Various */ +static char * read_file(const char *filename); +static void remove_comments(char *string, const char *start_token, const char *end_token); +static char * parson_strndup(const char *string, size_t n); +static char * parson_strdup(const char *string); +static int hex_char_to_int(char c); +static int parse_utf16_hex(const char *string, unsigned int *result); +static int num_bytes_in_utf8_sequence(unsigned char c); +static int verify_utf8_sequence(const unsigned char *string, int *len); +static int is_valid_utf8(const char *string, size_t string_len); +static int is_decimal(const char *string, size_t length); + +/* JSON Object */ +static JSON_Object * json_object_init(JSON_Value *wrapping_value); +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value); +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity); +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); +static void json_object_free(JSON_Object *object); + +/* JSON Array */ +static JSON_Array * json_array_init(JSON_Value *wrapping_value); +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value); +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); +static void json_array_free(JSON_Array *array); + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string); + +/* Parser */ +static JSON_Status skip_quotes(const char **string); +static int parse_utf16(const char **unprocessed, char **processed); +static char * process_string(const char *input, size_t len); +static char * get_quoted_string(const char **string); +static JSON_Value * parse_object_value(const char **string, size_t nesting); +static JSON_Value * parse_array_value(const char **string, size_t nesting); +static JSON_Value * parse_string_value(const char **string); +static JSON_Value * parse_boolean_value(const char **string); +static JSON_Value * parse_number_value(const char **string); +static JSON_Value * parse_null_value(const char **string); +static JSON_Value * parse_value(const char **string, size_t nesting); + +/* Serialization */ +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf); +static int json_serialize_string(const char *string, char *buf); +static int append_indent(char *buf, int level); +static int append_string(char *buf, const char *string); + +/* Various */ +static char * parson_strndup(const char *string, size_t n) { + char *output_string = (char*)parson_malloc(n + 1); + if (!output_string) { + return NULL; + } + output_string[n] = '\0'; + strncpy(output_string, string, n); + return output_string; +} + +static char * parson_strdup(const char *string) { + return parson_strndup(string, strlen(string)); +} + +static int hex_char_to_int(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static int parse_utf16_hex(const char *s, unsigned int *result) { + int x1, x2, x3, x4; + if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') { + return 0; + } + x1 = hex_char_to_int(s[0]); + x2 = hex_char_to_int(s[1]); + x3 = hex_char_to_int(s[2]); + x4 = hex_char_to_int(s[3]); + if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) { + return 0; + } + *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4); + return 1; +} + +static int num_bytes_in_utf8_sequence(unsigned char c) { + if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) { + return 0; + } else if ((c & 0x80) == 0) { /* 0xxxxxxx */ + return 1; + } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */ + return 2; + } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */ + return 3; + } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */ + return 4; + } + return 0; /* won't happen */ +} + +static int verify_utf8_sequence(const unsigned char *string, int *len) { + unsigned int cp = 0; + *len = num_bytes_in_utf8_sequence(string[0]); + + if (*len == 1) { + cp = string[0]; + } else if (*len == 2 && IS_CONT(string[1])) { + cp = string[0] & 0x1F; + cp = (cp << 6) | (string[1] & 0x3F); + } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) { + cp = ((unsigned char)string[0]) & 0xF; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) { + cp = string[0] & 0x7; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + cp = (cp << 6) | (string[3] & 0x3F); + } else { + return 0; + } + + /* overlong encodings */ + if ((cp < 0x80 && *len > 1) || + (cp < 0x800 && *len > 2) || + (cp < 0x10000 && *len > 3)) { + return 0; + } + + /* invalid unicode */ + if (cp > 0x10FFFF) { + return 0; + } + + /* surrogate halves */ + if (cp >= 0xD800 && cp <= 0xDFFF) { + return 0; + } + + return 1; +} + +static int is_valid_utf8(const char *string, size_t string_len) { + int len = 0; + const char *string_end = string + string_len; + while (string < string_end) { + if (!verify_utf8_sequence((const unsigned char*)string, &len)) { + return 0; + } + string += len; + } + return 1; +} + +static int is_decimal(const char *string, size_t length) { + if (length > 1 && string[0] == '0' && string[1] != '.') { + return 0; + } + if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { + return 0; + } + while (length--) { + if (strchr("xX", string[length])) { + return 0; + } + } + return 1; +} + +static char * read_file(const char * filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + long pos; + char *file_contents; + if (!fp) { + return NULL; + } + fseek(fp, 0L, SEEK_END); + pos = ftell(fp); + if (pos < 0) { + fclose(fp); + return NULL; + } + file_size = pos; + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { + fclose(fp); + return NULL; + } + if (fread(file_contents, file_size, 1, fp) < 1) { + if (ferror(fp)) { + fclose(fp); + parson_free(file_contents); + return NULL; + } + } + fclose(fp); + file_contents[file_size] = '\0'; + return file_contents; +} + +static void remove_comments(char *string, const char *start_token, const char *end_token) { + int in_string = 0, escaped = 0; + size_t i; + char *ptr = NULL, current_char; + size_t start_token_len = strlen(start_token); + size_t end_token_len = strlen(end_token); + if (start_token_len == 0 || end_token_len == 0) { + return; + } + while ((current_char = *string) != '\0') { + if (current_char == '\\' && !escaped) { + escaped = 1; + string++; + continue; + } else if (current_char == '\"' && !escaped) { + in_string = !in_string; + } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { + for(i = 0; i < start_token_len; i++) { + string[i] = ' '; + } + string = string + start_token_len; + ptr = strstr(string, end_token); + if (!ptr) { + return; + } + for (i = 0; i < (ptr - string) + end_token_len; i++) { + string[i] = ' '; + } + string = ptr + end_token_len - 1; + } + escaped = 0; + string++; + } +} + +/* JSON Object */ +static JSON_Object * json_object_init(JSON_Value *wrapping_value) { + JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); + if (new_obj == NULL) { + return NULL; + } + new_obj->wrapping_value = wrapping_value; + new_obj->names = (char**)NULL; + new_obj->values = (JSON_Value**)NULL; + new_obj->capacity = 0; + new_obj->count = 0; + return new_obj; +} + +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { + size_t index = 0; + if (object == NULL || name == NULL || value == NULL) { + return JSONFailure; + } + if (json_object_get_value(object, name) != NULL) { + return JSONFailure; + } + if (object->count >= object->capacity) { + size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); + if (json_object_resize(object, new_capacity) == JSONFailure) { + return JSONFailure; + } + } + index = object->count; + object->names[index] = parson_strdup(name); + if (object->names[index] == NULL) { + return JSONFailure; + } + value->parent = json_object_get_wrapping_value(object); + object->values[index] = value; + object->count++; + return JSONSuccess; +} + +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity) { + char **temp_names = NULL; + JSON_Value **temp_values = NULL; + + if ((object->names == NULL && object->values != NULL) || + (object->names != NULL && object->values == NULL) || + new_capacity == 0) { + return JSONFailure; /* Shouldn't happen */ + } + temp_names = (char**)parson_malloc(new_capacity * sizeof(char*)); + if (temp_names == NULL) { + return JSONFailure; + } + temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (temp_values == NULL) { + parson_free(temp_names); + return JSONFailure; + } + if (object->names != NULL && object->values != NULL && object->count > 0) { + memcpy(temp_names, object->names, object->count * sizeof(char*)); + memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*)); + } + parson_free(object->names); + parson_free(object->values); + object->names = temp_names; + object->values = temp_values; + object->capacity = new_capacity; + return JSONSuccess; +} + +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { + size_t i, name_length; + for (i = 0; i < json_object_get_count(object); i++) { + name_length = strlen(object->names[i]); + if (name_length != n) { + continue; + } + if (strncmp(object->names[i], name, n) == 0) { + return object->values[i]; + } + } + return NULL; +} + +static void json_object_free(JSON_Object *object) { + size_t i; + for (i = 0; i < object->count; i++) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + } + parson_free(object->names); + parson_free(object->values); + parson_free(object); +} + +/* JSON Array */ +static JSON_Array * json_array_init(JSON_Value *wrapping_value) { + JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); + if (new_array == NULL) { + return NULL; + } + new_array->wrapping_value = wrapping_value; + new_array->items = (JSON_Value**)NULL; + new_array->capacity = 0; + new_array->count = 0; + return new_array; +} + +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) { + if (array->count >= array->capacity) { + size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); + if (json_array_resize(array, new_capacity) == JSONFailure) { + return JSONFailure; + } + } + value->parent = json_array_get_wrapping_value(array); + array->items[array->count] = value; + array->count++; + return JSONSuccess; +} + +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) { + JSON_Value **new_items = NULL; + if (new_capacity == 0) { + return JSONFailure; + } + new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (new_items == NULL) { + return JSONFailure; + } + if (array->items != NULL && array->count > 0) { + memcpy(new_items, array->items, array->count * sizeof(JSON_Value*)); + } + parson_free(array->items); + array->items = new_items; + array->capacity = new_capacity; + return JSONSuccess; +} + +static void json_array_free(JSON_Array *array) { + size_t i; + for (i = 0; i < array->count; i++) { + json_value_free(array->items[i]); + } + parson_free(array->items); + parson_free(array); +} + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONString; + new_value->value.string = string; + return new_value; +} + +/* Parser */ +static JSON_Status skip_quotes(const char **string) { + if (**string != '\"') { + return JSONFailure; + } + SKIP_CHAR(string); + while (**string != '\"') { + if (**string == '\0') { + return JSONFailure; + } else if (**string == '\\') { + SKIP_CHAR(string); + if (**string == '\0') { + return JSONFailure; + } + } + SKIP_CHAR(string); + } + SKIP_CHAR(string); + return JSONSuccess; +} + +static int parse_utf16(const char **unprocessed, char **processed) { + unsigned int cp, lead, trail; + int parse_succeeded = 0; + char *processed_ptr = *processed; + const char *unprocessed_ptr = *unprocessed; + unprocessed_ptr++; /* skips u */ + parse_succeeded = parse_utf16_hex(unprocessed_ptr, &cp); + if (!parse_succeeded) { + return JSONFailure; + } + if (cp < 0x80) { + processed_ptr[0] = (char)cp; /* 0xxxxxxx */ + } else if (cp < 0x800) { + processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */ + processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr += 1; + } else if (cp < 0xD800 || cp > 0xDFFF) { + processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */ + processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr += 2; + } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */ + lead = cp; + unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */ + if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') { + return JSONFailure; + } + parse_succeeded = parse_utf16_hex(unprocessed_ptr, &trail); + if (!parse_succeeded || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */ + return JSONFailure; + } + cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000; + processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */ + processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr += 3; + } else { /* trail surrogate before lead surrogate */ + return JSONFailure; + } + unprocessed_ptr += 3; + *processed = processed_ptr; + *unprocessed = unprocessed_ptr; + return JSONSuccess; +} + + +/* Copies and processes passed string up to supplied length. +Example: "\u006Corem ipsum" -> lorem ipsum */ +static char* process_string(const char *input, size_t len) { + const char *input_ptr = input; + size_t initial_size = (len + 1) * sizeof(char); + size_t final_size = 0; + char *output = NULL, *output_ptr = NULL, *resized_output = NULL; + output = (char*)parson_malloc(initial_size); + if (output == NULL) { + goto error; + } + output_ptr = output; + while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < len) { + if (*input_ptr == '\\') { + input_ptr++; + switch (*input_ptr) { + case '\"': *output_ptr = '\"'; break; + case '\\': *output_ptr = '\\'; break; + case '/': *output_ptr = '/'; break; + case 'b': *output_ptr = '\b'; break; + case 'f': *output_ptr = '\f'; break; + case 'n': *output_ptr = '\n'; break; + case 'r': *output_ptr = '\r'; break; + case 't': *output_ptr = '\t'; break; + case 'u': + if (parse_utf16(&input_ptr, &output_ptr) == JSONFailure) { + goto error; + } + break; + default: + goto error; + } + } else if ((unsigned char)*input_ptr < 0x20) { + goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ + } else { + *output_ptr = *input_ptr; + } + output_ptr++; + input_ptr++; + } + *output_ptr = '\0'; + /* resize to new length */ + final_size = (size_t)(output_ptr-output) + 1; + /* todo: don't resize if final_size == initial_size */ + resized_output = (char*)parson_malloc(final_size); + if (resized_output == NULL) { + goto error; + } + memcpy(resized_output, output, final_size); + parson_free(output); + return resized_output; +error: + parson_free(output); + return NULL; +} + +/* Return processed contents of a string between quotes and + skips passed argument to a matching quote. */ +static char * get_quoted_string(const char **string) { + const char *string_start = *string; + size_t string_len = 0; + JSON_Status status = skip_quotes(string); + if (status != JSONSuccess) { + return NULL; + } + string_len = *string - string_start - 2; /* length without quotes */ + return process_string(string_start + 1, string_len); +} + +static JSON_Value * parse_value(const char **string, size_t nesting) { + if (nesting > MAX_NESTING) { + return NULL; + } + SKIP_WHITESPACES(string); + switch (**string) { + case '{': + return parse_object_value(string, nesting + 1); + case '[': + return parse_array_value(string, nesting + 1); + case '\"': + return parse_string_value(string); + case 'f': case 't': + return parse_boolean_value(string); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return parse_number_value(string); + case 'n': + return parse_null_value(string); + default: + return NULL; + } +} + +static JSON_Value * parse_object_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_object(), *new_value = NULL; + JSON_Object *output_object = json_value_get_object(output_value); + char *new_key = NULL; + if (output_value == NULL || **string != '{') { + return NULL; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == '}') { /* empty object */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_key = get_quoted_string(string); + if (new_key == NULL) { + json_value_free(output_value); + return NULL; + } + SKIP_WHITESPACES(string); + if (**string != ':') { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + new_value = parse_value(string, nesting); + if (new_value == NULL) { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + if (json_object_add(output_object, new_key, new_value) == JSONFailure) { + parson_free(new_key); + json_value_free(new_value); + json_value_free(output_value); + return NULL; + } + parson_free(new_key); + SKIP_WHITESPACES(string); + if (**string != ',') { + break; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != '}' || /* Trim object after parsing is over */ + json_object_resize(output_object, json_object_get_count(output_object)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_array_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL; + JSON_Array *output_array = json_value_get_array(output_value); + if (!output_value || **string != '[') { + return NULL; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == ']') { /* empty array */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_array_value = parse_value(string, nesting); + if (new_array_value == NULL) { + json_value_free(output_value); + return NULL; + } + if (json_array_add(output_array, new_array_value) == JSONFailure) { + json_value_free(new_array_value); + json_value_free(output_value); + return NULL; + } + SKIP_WHITESPACES(string); + if (**string != ',') { + break; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != ']' || /* Trim array after parsing is over */ + json_array_resize(output_array, json_array_get_count(output_array)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_string_value(const char **string) { + JSON_Value *value = NULL; + char *new_string = get_quoted_string(string); + if (new_string == NULL) { + return NULL; + } + value = json_value_init_string_no_copy(new_string); + if (value == NULL) { + parson_free(new_string); + return NULL; + } + return value; +} + +static JSON_Value * parse_boolean_value(const char **string) { + size_t true_token_size = SIZEOF_TOKEN("true"); + size_t false_token_size = SIZEOF_TOKEN("false"); + if (strncmp("true", *string, true_token_size) == 0) { + *string += true_token_size; + return json_value_init_boolean(1); + } else if (strncmp("false", *string, false_token_size) == 0) { + *string += false_token_size; + return json_value_init_boolean(0); + } + return NULL; +} + +static JSON_Value * parse_number_value(const char **string) { + char *end; + double number = 0; + errno = 0; + number = strtod(*string, &end); + if (errno || !is_decimal(*string, end - *string)) { + return NULL; + } + *string = end; + return json_value_init_number(number); +} + +static JSON_Value * parse_null_value(const char **string) { + size_t token_size = SIZEOF_TOKEN("null"); + if (strncmp("null", *string, token_size) == 0) { + *string += token_size; + return json_value_init_null(); + } + return NULL; +} + +/* Serialization */ +#define APPEND_STRING(str) do { written = append_string(buf, (str));\ + if (written < 0) { return -1; }\ + if (buf != NULL) { buf += written; }\ + written_total += written; } while(0) + +#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\ + if (written < 0) { return -1; }\ + if (buf != NULL) { buf += written; }\ + written_total += written; } while(0) + +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf) +{ + const char *key = NULL, *string = NULL; + JSON_Value *temp_value = NULL; + JSON_Array *array = NULL; + JSON_Object *object = NULL; + size_t i = 0, count = 0; + double num = 0.0; + int written = -1, written_total = 0; + + switch (json_value_get_type(value)) { + case JSONArray: + array = json_value_get_array(value); + count = json_array_get_count(array); + APPEND_STRING("["); + if (count > 0 && is_pretty) { + APPEND_STRING("\n"); + } + for (i = 0; i < count; i++) { + if (is_pretty) { + APPEND_INDENT(level+1); + } + temp_value = json_array_get_value(array, i); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + if (i < (count - 1)) { + APPEND_STRING(","); + } + if (is_pretty) { + APPEND_STRING("\n"); + } + } + if (count > 0 && is_pretty) { + APPEND_INDENT(level); + } + APPEND_STRING("]"); + return written_total; + case JSONObject: + object = json_value_get_object(value); + count = json_object_get_count(object); + APPEND_STRING("{"); + if (count > 0 && is_pretty) { + APPEND_STRING("\n"); + } + for (i = 0; i < count; i++) { + key = json_object_get_name(object, i); + if (key == NULL) { + return -1; + } + if (is_pretty) { + APPEND_INDENT(level+1); + } + written = json_serialize_string(key, buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + APPEND_STRING(":"); + if (is_pretty) { + APPEND_STRING(" "); + } + temp_value = json_object_get_value(object, key); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + if (i < (count - 1)) { + APPEND_STRING(","); + } + if (is_pretty) { + APPEND_STRING("\n"); + } + } + if (count > 0 && is_pretty) { + APPEND_INDENT(level); + } + APPEND_STRING("}"); + return written_total; + case JSONString: + string = json_value_get_string(value); + if (string == NULL) { + return -1; + } + written = json_serialize_string(string, buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + return written_total; + case JSONBoolean: + if (json_value_get_boolean(value)) { + APPEND_STRING("true"); + } else { + APPEND_STRING("false"); + } + return written_total; + case JSONNumber: + num = json_value_get_number(value); + if (buf != NULL) { + num_buf = buf; + } + written = sprintf(num_buf, FLOAT_FORMAT, num); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + return written_total; + case JSONNull: + APPEND_STRING("null"); + return written_total; + case JSONError: + return -1; + default: + return -1; + } +} + +static int json_serialize_string(const char *string, char *buf) { + size_t i = 0, len = strlen(string); + char c = '\0'; + int written = -1, written_total = 0; + APPEND_STRING("\""); + for (i = 0; i < len; i++) { + c = string[i]; + switch (c) { + case '\"': APPEND_STRING("\\\""); break; + case '\\': APPEND_STRING("\\\\"); break; + case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */ + case '\b': APPEND_STRING("\\b"); break; + case '\f': APPEND_STRING("\\f"); break; + case '\n': APPEND_STRING("\\n"); break; + case '\r': APPEND_STRING("\\r"); break; + case '\t': APPEND_STRING("\\t"); break; + case '\x00': APPEND_STRING("\\u0000"); break; + case '\x01': APPEND_STRING("\\u0001"); break; + case '\x02': APPEND_STRING("\\u0002"); break; + case '\x03': APPEND_STRING("\\u0003"); break; + case '\x04': APPEND_STRING("\\u0004"); break; + case '\x05': APPEND_STRING("\\u0005"); break; + case '\x06': APPEND_STRING("\\u0006"); break; + case '\x07': APPEND_STRING("\\u0007"); break; + /* '\x08' duplicate: '\b' */ + /* '\x09' duplicate: '\t' */ + /* '\x0a' duplicate: '\n' */ + case '\x0b': APPEND_STRING("\\u000b"); break; + /* '\x0c' duplicate: '\f' */ + /* '\x0d' duplicate: '\r' */ + case '\x0e': APPEND_STRING("\\u000e"); break; + case '\x0f': APPEND_STRING("\\u000f"); break; + case '\x10': APPEND_STRING("\\u0010"); break; + case '\x11': APPEND_STRING("\\u0011"); break; + case '\x12': APPEND_STRING("\\u0012"); break; + case '\x13': APPEND_STRING("\\u0013"); break; + case '\x14': APPEND_STRING("\\u0014"); break; + case '\x15': APPEND_STRING("\\u0015"); break; + case '\x16': APPEND_STRING("\\u0016"); break; + case '\x17': APPEND_STRING("\\u0017"); break; + case '\x18': APPEND_STRING("\\u0018"); break; + case '\x19': APPEND_STRING("\\u0019"); break; + case '\x1a': APPEND_STRING("\\u001a"); break; + case '\x1b': APPEND_STRING("\\u001b"); break; + case '\x1c': APPEND_STRING("\\u001c"); break; + case '\x1d': APPEND_STRING("\\u001d"); break; + case '\x1e': APPEND_STRING("\\u001e"); break; + case '\x1f': APPEND_STRING("\\u001f"); break; + default: + if (buf != NULL) { + buf[0] = c; + buf += 1; + } + written_total += 1; + break; + } + } + APPEND_STRING("\""); + return written_total; +} + +static int append_indent(char *buf, int level) { + int i; + int written = -1, written_total = 0; + for (i = 0; i < level; i++) { + APPEND_STRING(" "); + } + return written_total; +} + +static int append_string(char *buf, const char *string) { + if (buf == NULL) { + return (int)strlen(string); + } + return sprintf(buf, "%s", string); +} + +#undef APPEND_STRING +#undef APPEND_INDENT + +/* Parser API */ +JSON_Value * json_parse_file(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) { + return NULL; + } + output_value = json_parse_string(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_file_with_comments(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) { + return NULL; + } + output_value = json_parse_string_with_comments(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_string(const char *string) { + if (string == NULL) { + return NULL; + } + if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') { + string = string + 3; /* Support for UTF-8 BOM */ + } + return parse_value((const char**)&string, 0); +} + +JSON_Value * json_parse_string_with_comments(const char *string) { + JSON_Value *result = NULL; + char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL; + string_mutable_copy = parson_strdup(string); + if (string_mutable_copy == NULL) { + return NULL; + } + remove_comments(string_mutable_copy, "/*", "*/"); + remove_comments(string_mutable_copy, "//", "\n"); + string_mutable_copy_ptr = string_mutable_copy; + result = parse_value((const char**)&string_mutable_copy_ptr, 0); + parson_free(string_mutable_copy); + return result; +} + +/* JSON Object API */ + +JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { + if (object == NULL || name == NULL) { + return NULL; + } + return json_object_nget_value(object, name, strlen(name)); +} + +const char * json_object_get_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_get_value(object, name)); +} + +double json_object_get_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_get_value(object, name)); +} + +JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_get_value(object, name)); +} + +JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_get_value(object, name)); +} + +int json_object_get_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_get_value(object, name)); +} + +JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) { + const char *dot_position = strchr(name, '.'); + if (!dot_position) { + return json_object_get_value(object, name); + } + object = json_value_get_object(json_object_nget_value(object, name, dot_position - name)); + return json_object_dotget_value(object, dot_position + 1); +} + +const char * json_object_dotget_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_dotget_value(object, name)); +} + +double json_object_dotget_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_dotget_value(object, name)); +} + +JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_dotget_value(object, name)); +} + +JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_dotget_value(object, name)); +} + +int json_object_dotget_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_dotget_value(object, name)); +} + +size_t json_object_get_count(const JSON_Object *object) { + return object ? object->count : 0; +} + +const char * json_object_get_name(const JSON_Object *object, size_t index) { + if (object == NULL || index >= json_object_get_count(object)) { + return NULL; + } + return object->names[index]; +} + +JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) { + if (object == NULL || index >= json_object_get_count(object)) { + return NULL; + } + return object->values[index]; +} + +JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) { + return object->wrapping_value; +} + +int json_object_has_value (const JSON_Object *object, const char *name) { + return json_object_get_value(object, name) != NULL; +} + +int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { + JSON_Value *val = json_object_get_value(object, name); + return val != NULL && json_value_get_type(val) == type; +} + +int json_object_dothas_value (const JSON_Object *object, const char *name) { + return json_object_dotget_value(object, name) != NULL; +} + +int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { + JSON_Value *val = json_object_dotget_value(object, name); + return val != NULL && json_value_get_type(val) == type; +} + +/* JSON Array API */ +JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) { + if (array == NULL || index >= json_array_get_count(array)) { + return NULL; + } + return array->items[index]; +} + +const char * json_array_get_string(const JSON_Array *array, size_t index) { + return json_value_get_string(json_array_get_value(array, index)); +} + +double json_array_get_number(const JSON_Array *array, size_t index) { + return json_value_get_number(json_array_get_value(array, index)); +} + +JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) { + return json_value_get_object(json_array_get_value(array, index)); +} + +JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) { + return json_value_get_array(json_array_get_value(array, index)); +} + +int json_array_get_boolean(const JSON_Array *array, size_t index) { + return json_value_get_boolean(json_array_get_value(array, index)); +} + +size_t json_array_get_count(const JSON_Array *array) { + return array ? array->count : 0; +} + +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array) { + return array->wrapping_value; +} + +/* JSON Value API */ +JSON_Value_Type json_value_get_type(const JSON_Value *value) { + return value ? value->type : JSONError; +} + +JSON_Object * json_value_get_object(const JSON_Value *value) { + return json_value_get_type(value) == JSONObject ? value->value.object : NULL; +} + +JSON_Array * json_value_get_array(const JSON_Value *value) { + return json_value_get_type(value) == JSONArray ? value->value.array : NULL; +} + +const char * json_value_get_string(const JSON_Value *value) { + return json_value_get_type(value) == JSONString ? value->value.string : NULL; +} + +double json_value_get_number(const JSON_Value *value) { + return json_value_get_type(value) == JSONNumber ? value->value.number : 0; +} + +int json_value_get_boolean(const JSON_Value *value) { + return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; +} + +JSON_Value * json_value_get_parent (const JSON_Value *value) { + return value ? value->parent : NULL; +} + +void json_value_free(JSON_Value *value) { + switch (json_value_get_type(value)) { + case JSONObject: + json_object_free(value->value.object); + break; + case JSONString: + parson_free(value->value.string); + break; + case JSONArray: + json_array_free(value->value.array); + break; + default: + break; + } + parson_free(value); +} + +JSON_Value * json_value_init_object(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONObject; + new_value->value.object = json_object_init(new_value); + if (!new_value->value.object) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_array(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONArray; + new_value->value.array = json_array_init(new_value); + if (!new_value->value.array) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_string(const char *string) { + char *copy = NULL; + JSON_Value *value; + size_t string_len = 0; + if (string == NULL) { + return NULL; + } + string_len = strlen(string); + if (!is_valid_utf8(string, string_len)) { + return NULL; + } + copy = parson_strndup(string, string_len); + if (copy == NULL) { + return NULL; + } + value = json_value_init_string_no_copy(copy); + if (value == NULL) { + parson_free(copy); + } + return value; +} + +JSON_Value * json_value_init_number(double number) { + JSON_Value *new_value = NULL; + if ((number * 0.0) != 0.0) { /* nan and inf test */ + return NULL; + } + new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (new_value == NULL) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONNumber; + new_value->value.number = number; + return new_value; +} + +JSON_Value * json_value_init_boolean(int boolean) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONBoolean; + new_value->value.boolean = boolean ? 1 : 0; + return new_value; +} + +JSON_Value * json_value_init_null(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONNull; + return new_value; +} + +JSON_Value * json_value_deep_copy(const JSON_Value *value) { + size_t i = 0; + JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL; + const char *temp_string = NULL, *temp_key = NULL; + char *temp_string_copy = NULL; + JSON_Array *temp_array = NULL, *temp_array_copy = NULL; + JSON_Object *temp_object = NULL, *temp_object_copy = NULL; + + switch (json_value_get_type(value)) { + case JSONArray: + temp_array = json_value_get_array(value); + return_value = json_value_init_array(); + if (return_value == NULL) { + return NULL; + } + temp_array_copy = json_value_get_array(return_value); + for (i = 0; i < json_array_get_count(temp_array); i++) { + temp_value = json_array_get_value(temp_array, i); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_array_add(temp_array_copy, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONObject: + temp_object = json_value_get_object(value); + return_value = json_value_init_object(); + if (return_value == NULL) { + return NULL; + } + temp_object_copy = json_value_get_object(return_value); + for (i = 0; i < json_object_get_count(temp_object); i++) { + temp_key = json_object_get_name(temp_object, i); + temp_value = json_object_get_value(temp_object, temp_key); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONBoolean: + return json_value_init_boolean(json_value_get_boolean(value)); + case JSONNumber: + return json_value_init_number(json_value_get_number(value)); + case JSONString: + temp_string = json_value_get_string(value); + if (temp_string == NULL) { + return NULL; + } + temp_string_copy = parson_strdup(temp_string); + if (temp_string_copy == NULL) { + return NULL; + } + return_value = json_value_init_string_no_copy(temp_string_copy); + if (return_value == NULL) { + parson_free(temp_string_copy); + } + return return_value; + case JSONNull: + return json_value_init_null(); + case JSONError: + return NULL; + default: + return NULL; + } +} + +size_t json_serialization_size(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { + return JSONFailure; + } + written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL); + if (written < 0) { + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp == NULL) { + json_free_serialized_string(serialized_string); + return JSONFailure; + } + if (fputs(serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose(fp) == EOF) { + return_code = JSONFailure; + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) { + return NULL; + } + serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +size_t json_serialization_size_pretty(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size_pretty(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { + return JSONFailure; + } + written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL); + if (written < 0) { + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string_pretty(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp == NULL) { + json_free_serialized_string(serialized_string); + return JSONFailure; + } + if (fputs(serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose(fp) == EOF) { + return_code = JSONFailure; + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string_pretty(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size_pretty(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) { + return NULL; + } + serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +void json_free_serialized_string(char *string) { + parson_free(string); +} + +JSON_Status json_array_remove(JSON_Array *array, size_t ix) { + size_t to_move_bytes = 0; + if (array == NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + json_value_free(json_array_get_value(array, ix)); + to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value*); + memmove(array->items + ix, array->items + ix + 1, to_move_bytes); + array->count -= 1; + return JSONSuccess; +} + +JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) { + if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + json_value_free(json_array_get_value(array, ix)); + value->parent = json_array_get_wrapping_value(array); + array->items[ix] = value; + return JSONSuccess; +} + +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_null(JSON_Array *array, size_t i) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_clear(JSON_Array *array) { + size_t i = 0; + if (array == NULL) { + return JSONFailure; + } + for (i = 0; i < json_array_get_count(array); i++) { + json_value_free(json_array_get_value(array, i)); + } + array->count = 0; + return JSONSuccess; +} + +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) { + if (array == NULL || value == NULL || value->parent != NULL) { + return JSONFailure; + } + return json_array_add(array, value); +} + +JSON_Status json_array_append_string(JSON_Array *array, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_number(JSON_Array *array, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_null(JSON_Array *array) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) { + size_t i = 0; + JSON_Value *old_value; + if (object == NULL || name == NULL || value == NULL || value->parent != NULL) { + return JSONFailure; + } + old_value = json_object_get_value(object, name); + if (old_value != NULL) { /* free and overwrite old value */ + json_value_free(old_value); + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + value->parent = json_object_get_wrapping_value(object); + object->values[i] = value; + return JSONSuccess; + } + } + } + /* add new key value pair */ + return json_object_add(object, name, value); +} + +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) { + return json_object_set_value(object, name, json_value_init_string(string)); +} + +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) { + return json_object_set_value(object, name, json_value_init_number(number)); +} + +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) { + return json_object_set_value(object, name, json_value_init_boolean(boolean)); +} + +JSON_Status json_object_set_null(JSON_Object *object, const char *name) { + return json_object_set_value(object, name, json_value_init_null()); +} + +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) { + const char *dot_pos = NULL; + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + JSON_Value *new_value = NULL; + if (object == NULL || name == NULL || value == NULL) { + return JSONFailure; + } + dot_pos = strchr(name, '.'); + if (dot_pos == NULL) { + return json_object_set_value(object, name, value); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + if (temp_obj == NULL) { + new_value = json_value_init_object(); + if (new_value == NULL) { + parson_free(current_name); + return JSONFailure; + } + if (json_object_add(object, current_name, new_value) == JSONFailure) { + json_value_free(new_value); + parson_free(current_name); + return JSONFailure; + } + temp_obj = json_object_get_object(object, current_name); + } + parson_free(current_name); + return json_object_dotset_value(temp_obj, dot_pos + 1, value); + } +} + +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_remove(JSON_Object *object, const char *name) { + size_t i = 0, last_item_index = 0; + if (object == NULL || json_object_get_value(object, name) == NULL) { + return JSONFailure; + } + last_item_index = json_object_get_count(object) - 1; + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + if (i != last_item_index) { /* Replace key value pair with one from the end */ + object->names[i] = object->names[last_item_index]; + object->values[i] = object->values[last_item_index]; + } + object->count -= 1; + return JSONSuccess; + } + } + return JSONFailure; /* No execution path should end here */ +} + +JSON_Status json_object_dotremove(JSON_Object *object, const char *name) { + const char *dot_pos = strchr(name, '.'); + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + if (dot_pos == NULL) { + return json_object_remove(object, name); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + parson_free(current_name); + if (temp_obj == NULL) { + return JSONFailure; + } + return json_object_dotremove(temp_obj, dot_pos + 1); + } +} + +JSON_Status json_object_clear(JSON_Object *object) { + size_t i = 0; + if (object == NULL) { + return JSONFailure; + } + for (i = 0; i < json_object_get_count(object); i++) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + } + object->count = 0; + return JSONSuccess; +} + +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) { + JSON_Value *temp_schema_value = NULL, *temp_value = NULL; + JSON_Array *schema_array = NULL, *value_array = NULL; + JSON_Object *schema_object = NULL, *value_object = NULL; + JSON_Value_Type schema_type = JSONError, value_type = JSONError; + const char *key = NULL; + size_t i = 0, count = 0; + if (schema == NULL || value == NULL) { + return JSONFailure; + } + schema_type = json_value_get_type(schema); + value_type = json_value_get_type(value); + if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */ + return JSONFailure; + } + switch (schema_type) { + case JSONArray: + schema_array = json_value_get_array(schema); + value_array = json_value_get_array(value); + count = json_array_get_count(schema_array); + if (count == 0) { + return JSONSuccess; /* Empty array allows all types */ + } + /* Get first value from array, rest is ignored */ + temp_schema_value = json_array_get_value(schema_array, 0); + for (i = 0; i < json_array_get_count(value_array); i++) { + temp_value = json_array_get_value(value_array, i); + if (json_validate(temp_schema_value, temp_value) == JSONFailure) { + return JSONFailure; + } + } + return JSONSuccess; + case JSONObject: + schema_object = json_value_get_object(schema); + value_object = json_value_get_object(value); + count = json_object_get_count(schema_object); + if (count == 0) { + return JSONSuccess; /* Empty object allows all objects */ + } else if (json_object_get_count(value_object) < count) { + return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */ + } + for (i = 0; i < count; i++) { + key = json_object_get_name(schema_object, i); + temp_schema_value = json_object_get_value(schema_object, key); + temp_value = json_object_get_value(value_object, key); + if (temp_value == NULL) { + return JSONFailure; + } + if (json_validate(temp_schema_value, temp_value) == JSONFailure) { + return JSONFailure; + } + } + return JSONSuccess; + case JSONString: case JSONNumber: case JSONBoolean: case JSONNull: + return JSONSuccess; /* equality already tested before switch */ + case JSONError: default: + return JSONFailure; + } +} + +int json_value_equals(const JSON_Value *a, const JSON_Value *b) { + JSON_Object *a_object = NULL, *b_object = NULL; + JSON_Array *a_array = NULL, *b_array = NULL; + const char *a_string = NULL, *b_string = NULL; + const char *key = NULL; + size_t a_count = 0, b_count = 0, i = 0; + JSON_Value_Type a_type, b_type; + a_type = json_value_get_type(a); + b_type = json_value_get_type(b); + if (a_type != b_type) { + return 0; + } + switch (a_type) { + case JSONArray: + a_array = json_value_get_array(a); + b_array = json_value_get_array(b); + a_count = json_array_get_count(a_array); + b_count = json_array_get_count(b_array); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + if (!json_value_equals(json_array_get_value(a_array, i), + json_array_get_value(b_array, i))) { + return 0; + } + } + return 1; + case JSONObject: + a_object = json_value_get_object(a); + b_object = json_value_get_object(b); + a_count = json_object_get_count(a_object); + b_count = json_object_get_count(b_object); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + key = json_object_get_name(a_object, i); + if (!json_value_equals(json_object_get_value(a_object, key), + json_object_get_value(b_object, key))) { + return 0; + } + } + return 1; + case JSONString: + a_string = json_value_get_string(a); + b_string = json_value_get_string(b); + if (a_string == NULL || b_string == NULL) { + return 0; /* shouldn't happen */ + } + return strcmp(a_string, b_string) == 0; + case JSONBoolean: + return json_value_get_boolean(a) == json_value_get_boolean(b); + case JSONNumber: + return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */ + case JSONError: + return 1; + case JSONNull: + return 1; + default: + return 1; + } +} + +JSON_Value_Type json_type(const JSON_Value *value) { + return json_value_get_type(value); +} + +JSON_Object * json_object (const JSON_Value *value) { + return json_value_get_object(value); +} + +JSON_Array * json_array (const JSON_Value *value) { + return json_value_get_array(value); +} + +const char * json_string (const JSON_Value *value) { + return json_value_get_string(value); +} + +double json_number (const JSON_Value *value) { + return json_value_get_number(value); +} + +int json_boolean(const JSON_Value *value) { + return json_value_get_boolean(value); +} + +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) { + parson_malloc = malloc_fun; + parson_free = free_fun; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/parson/parson.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,234 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2017 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef parson_parson_h +#define parson_parson_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stddef.h> /* size_t */ + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +}; +typedef int JSON_Value_Type; + +enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 +}; +typedef int JSON_Status; + +typedef void * (*JSON_Malloc_Function)(size_t); +typedef void (*JSON_Free_Function)(void *); + +/* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + +/* Serialization */ +size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); +char * json_serialize_to_string(const JSON_Value *value); + +/* Pretty serialization */ +size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); +char * json_serialize_to_string_pretty(const JSON_Value *value); + +void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + +/* Comparing */ +int json_value_equals(const JSON_Value *a, const JSON_Value *b); + +/* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + +/* + * JSON Object + */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* Functions to get available names */ +size_t json_object_get_count (const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); +JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index); +JSON_Value * json_object_get_wrapping_value(const JSON_Object *object); + +/* Functions to check if object has a value with a specific name. Returned value is 1 if object has + * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */ +int json_object_has_value (const JSON_Object *object, const char *name); +int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); + +int json_object_dothas_value (const JSON_Object *object, const char *name); +int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); + +/* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_set_null(JSON_Object *object, const char *name); + +/* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + +/* Frees and removes name-value pair */ +JSON_Status json_object_remove(JSON_Object *object, const char *name); + +/* Works like dotget function, but removes name-value pair only on exact match. */ +JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + +/* Removes all name-value pairs in object */ +JSON_Status json_object_clear(JSON_Object *object); + +/* + *JSON Array + */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ +int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ +size_t json_array_get_count (const JSON_Array *array); +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array); + +/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ +JSON_Status json_array_remove(JSON_Array *array, size_t i); + +/* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); +JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + +/* Frees and removes all values from array */ +JSON_Status json_array_clear(JSON_Array *array); + +/* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); +JSON_Status json_array_append_string(JSON_Array *array, const char *string); +JSON_Status json_array_append_number(JSON_Array *array, double number); +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); +JSON_Status json_array_append_null(JSON_Array *array); + +/* + *JSON Value + */ +JSON_Value * json_value_init_object (void); +JSON_Value * json_value_init_array (void); +JSON_Value * json_value_init_string (const char *string); /* copies passed string */ +JSON_Value * json_value_init_number (double number); +JSON_Value * json_value_init_boolean(int boolean); +JSON_Value * json_value_init_null (void); +JSON_Value * json_value_deep_copy (const JSON_Value *value); +void json_value_free (JSON_Value *value); + +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); +JSON_Value * json_value_get_parent (const JSON_Value *value); + +/* Same as above, but shorter */ +JSON_Value_Type json_type (const JSON_Value *value); +JSON_Object * json_object (const JSON_Value *value); +JSON_Array * json_array (const JSON_Value *value); +const char * json_string (const JSON_Value *value); +double json_number (const JSON_Value *value); +int json_boolean(const JSON_Value *value); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/uhttp/inc/azure_uhttp_c/uhttp.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UHTTP_H +#define UHTTP_H + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct HTTP_CLIENT_HANDLE_DATA_TAG* HTTP_CLIENT_HANDLE; + +#define HTTP_CLIENT_RESULT_VALUES \ + HTTP_CLIENT_OK, \ + HTTP_CLIENT_INVALID_ARG, \ + HTTP_CLIENT_ERROR, \ + HTTP_CLIENT_OPEN_FAILED, \ + HTTP_CLIENT_SEND_FAILED, \ + HTTP_CLIENT_ALREADY_INIT, \ + HTTP_CLIENT_HTTP_HEADERS_FAILED, \ + HTTP_CLIENT_INVALID_STATE \ + + +/** @brief Enumeration specifying the possible return values for the APIs in +* this module. +*/ +DEFINE_ENUM(HTTP_CLIENT_RESULT, HTTP_CLIENT_RESULT_VALUES); + +#define HTTP_CLIENT_REQUEST_TYPE_VALUES \ + HTTP_CLIENT_REQUEST_OPTIONS, \ + HTTP_CLIENT_REQUEST_GET, \ + HTTP_CLIENT_REQUEST_POST, \ + HTTP_CLIENT_REQUEST_PUT, \ + HTTP_CLIENT_REQUEST_DELETE, \ + HTTP_CLIENT_REQUEST_PATCH + +DEFINE_ENUM(HTTP_CLIENT_REQUEST_TYPE, HTTP_CLIENT_REQUEST_TYPE_VALUES); + +#define HTTP_CALLBACK_REASON_VALUES \ + HTTP_CALLBACK_REASON_OK, \ + HTTP_CALLBACK_REASON_OPEN_FAILED, \ + HTTP_CALLBACK_REASON_SEND_FAILED, \ + HTTP_CALLBACK_REASON_ERROR, \ + HTTP_CALLBACK_REASON_PARSING_ERROR, \ + HTTP_CALLBACK_REASON_DESTROY, \ + HTTP_CALLBACK_REASON_DISCONNECTED + +DEFINE_ENUM(HTTP_CALLBACK_REASON, HTTP_CALLBACK_REASON_VALUES); + +typedef void(*ON_HTTP_OPEN_COMPLETE_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON open_result); +typedef void(*ON_HTTP_ERROR_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON error_result); +typedef void(*ON_HTTP_REQUEST_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_length, unsigned int status_code, + HTTP_HEADERS_HANDLE response_headers); +typedef void(*ON_HTTP_CLOSED_CALLBACK)(void* callback_ctx); + +MOCKABLE_FUNCTION(, HTTP_CLIENT_HANDLE, uhttp_client_create, const IO_INTERFACE_DESCRIPTION*, io_interface_desc, const void*, xio_param, ON_HTTP_ERROR_CALLBACK, on_http_error, void*, callback_ctx); +MOCKABLE_FUNCTION(, void, uhttp_client_destroy, HTTP_CLIENT_HANDLE, handle); +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_open, HTTP_CLIENT_HANDLE, handle, const char*, host, int, port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK, on_connect, void*, callback_ctx); +MOCKABLE_FUNCTION(, void, uhttp_client_close, HTTP_CLIENT_HANDLE, handle, ON_HTTP_CLOSED_CALLBACK, on_close_callback, void*, callback_ctx); +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_execute_request, HTTP_CLIENT_HANDLE, handle, HTTP_CLIENT_REQUEST_TYPE, request_type, const char*, relative_path, + HTTP_HEADERS_HANDLE, http_header_handle, const unsigned char*, content, size_t, content_length, ON_HTTP_REQUEST_CALLBACK, on_request_callback, void*, callback_ctx); + +MOCKABLE_FUNCTION(, void, uhttp_client_dowork, HTTP_CLIENT_HANDLE, handle); + +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trace, HTTP_CLIENT_HANDLE, handle, bool, trace_on, bool, trace_data); +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_X509_cert, HTTP_CLIENT_HANDLE, handle, bool, ecc_type, const char*, certificate, const char*, private_key); +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trusted_cert, HTTP_CLIENT_HANDLE, handle, const char*, certificate); +MOCKABLE_FUNCTION(, const char*, uhttp_client_get_trusted_cert, HTTP_CLIENT_HANDLE, handle); +MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_option, HTTP_CLIENT_HANDLE, handle, const char*, optionName, const void*, value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UHTTP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/uhttp/src/uhttp.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1466 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdbool.h> +#include <ctype.h> + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/gballoc.h" + +#include <string.h> +#include "azure_uhttp_c/uhttp.h" +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/optimize_size.h" + +#define MAX_HOSTNAME 64 +#define TIME_MAX_BUFFER 16 +#define HTTP_CRLF_LEN 2 +#define HTTP_END_TOKEN_LEN 4 + +static const char* HTTP_REQUEST_LINE_FMT = "%s %s HTTP/1.1\r\n"; +static const char* HTTP_HOST = "Host"; +static const char* HTTP_CONTENT_LEN = "content-length"; +static const char* HTTP_TRANSFER_ENCODING = "transfer-encoding"; +//static const char* HTTP_CHUNKED_ENCODING_HDR = "Transfer-Encoding: chunked\r\n"; +static const char* HTTP_CRLF_VALUE = "\r\n"; +//static const char* FORMAT_HEX_CHAR = "0x%02x "; + +typedef enum RESPONSE_MESSAGE_STATE_TAG +{ + state_initial, + state_opening, + state_open, + state_process_status_line, + state_process_headers, + state_process_body, + state_process_chunked_body, + + state_send_user_callback, + state_parse_complete, + + state_closing, + state_closed, + state_error +} RESPONSE_MESSAGE_STATE; + +typedef struct HTTP_RECV_DATA_TAG +{ + ON_HTTP_REQUEST_CALLBACK on_request_callback; + void* user_ctx; + int status_code; + RESPONSE_MESSAGE_STATE recv_state; + HTTP_HEADERS_HANDLE resp_header; + BUFFER_HANDLE msg_body; + size_t total_body_len; + BUFFER_HANDLE accrual_buff; + bool chunked_reply; +} HTTP_RECV_DATA; + +typedef struct HTTP_CLIENT_HANDLE_DATA_TAG +{ + XIO_HANDLE xio_handle; + ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect; + void* connect_user_ctx; + ON_HTTP_ERROR_CALLBACK on_error; + void* error_user_ctx; + ON_HTTP_CLOSED_CALLBACK on_close_callback; + void* close_user_ctx; + HTTP_RECV_DATA recv_msg; + bool chunk_request; + bool trace_on; + bool trace_body; + char* host_name; + int port_num; + SINGLYLINKEDLIST_HANDLE data_list; + bool cert_type_ecc; + char* x509_cert; + char* x509_pk; + char* certificate; + int connected; +} HTTP_CLIENT_HANDLE_DATA; + +typedef struct HTTP_SEND_DATA_TAG +{ + HTTP_CLIENT_REQUEST_TYPE request_type; + STRING_HANDLE relative_path; + STRING_HANDLE header_line; + BUFFER_HANDLE content; +} HTTP_SEND_DATA; + +static void send_complete_callback(void* context, IO_SEND_RESULT send_result) +{ + (void)context;(void)send_result; +} + +static int initialize_received_data(HTTP_CLIENT_HANDLE_DATA* http_data) +{ + int result = 0; + + // Initialize data if neccessary + if (http_data->recv_msg.resp_header == NULL) + { + http_data->recv_msg.resp_header = HTTPHeaders_Alloc(); + if (http_data->recv_msg.resp_header == NULL) + { + /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ + LogError("Failure creating Http header."); + result = __FAILURE__; + } + } + if (result == 0 && http_data->recv_msg.accrual_buff == NULL) + { + http_data->recv_msg.accrual_buff = BUFFER_new(); + if (http_data->recv_msg.accrual_buff == NULL) + { + /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ + LogError("Failure creating accrual buffer."); + result = __FAILURE__; + } + } + http_data->recv_msg.chunked_reply = false; + return result; +} + +static int process_status_code_line(const unsigned char* buffer, size_t len, size_t* position, int* statusLen) +{ + int result = __FAILURE__; + size_t index; + int spaceFound = 0; + const char* initSpace = NULL; + char status_code[4]; + + for (index = 0; index < len; index++) + { + if (buffer[index] == ' ') + { + if (spaceFound == 1) + { + strncpy(status_code, initSpace, 3); + status_code[3] = '\0'; + } + else + { + initSpace = (const char*)buffer+index+1; + } + spaceFound++; + } + else if (buffer[index] == '\n') + { + *statusLen = (int)atol(status_code); + if (index < len) + { + *position = index+1; + } + else + { + *position = index; + } + result = 0; + break; + } + } + return result; +} + +static int process_header_line(const unsigned char* buffer, size_t len, size_t* position, HTTP_HEADERS_HANDLE resp_header, size_t* contentLen, bool* isChunked) +{ + int result = __FAILURE__; + size_t index; + const unsigned char* targetPos = buffer; + bool crlfEncounted = false; + bool colonEncountered = false; + char* headerKey = NULL; + bool continueProcessing = true; + + for (index = 0; index < len && continueProcessing; index++) + { + if (buffer[index] == ':' && !colonEncountered) + { + colonEncountered = true; + size_t keyLen = (&buffer[index])-targetPos; + headerKey = (char*)malloc(keyLen+1); + if (headerKey == NULL) + { + result = __FAILURE__; + continueProcessing = false; + } + else + { + memcpy(headerKey, targetPos, keyLen); + headerKey[keyLen] = '\0'; + + // Convert to lower case + for (size_t inner = 0; inner < keyLen; inner++) + { + headerKey[inner] = (char)tolower(headerKey[inner]); + } + + targetPos = buffer+index+1; + crlfEncounted = false; + } + } + else if (buffer[index] == '\r') + { + if (headerKey != NULL) + { + // Remove leading spaces + while (*targetPos == 32) { targetPos++; } + + size_t valueLen = (&buffer[index])-targetPos; + char* headerValue = (char*)malloc(valueLen+1); + if (headerValue == NULL) + { + result = __FAILURE__; + continueProcessing = false; + } + else + { + memcpy(headerValue, targetPos, valueLen); + headerValue[valueLen] = '\0'; + + if (HTTPHeaders_AddHeaderNameValuePair(resp_header, headerKey, headerValue) != HTTP_HEADERS_OK) + { + result = __FAILURE__; + continueProcessing = false; + } + else + { + if (strcmp(headerKey, HTTP_CONTENT_LEN) == 0) + { + *isChunked = false; + *contentLen = atol(headerValue); + } + else if (strcmp(headerKey, HTTP_TRANSFER_ENCODING) == 0) + { + *isChunked = true; + *contentLen = 0; + } + if (index < len) + { + *position = index; + } + else + { + *position = index-1; + } + } + } + free(headerKey); + headerKey = NULL; + free(headerValue); + } + } + else if (buffer[index] == '\n') + { + if (crlfEncounted) + { + if (index < len) + { + *position = index+1; + } + else + { + *position = index; + } + result = 0; + break; + } + else + { + colonEncountered = false; + crlfEncounted = true; + targetPos = buffer+index+1; + } + } + else + { + crlfEncounted = false; + } + } + if (headerKey != NULL) + { + free(headerKey); + } + return result; +} + +static int write_text_line(HTTP_CLIENT_HANDLE_DATA* http_data, const char* text_line) +{ + int result; + if (xio_send(http_data->xio_handle, text_line, strlen(text_line), send_complete_callback, NULL) != 0) + { + LogError("Failure calling xio_send."); + result = __FAILURE__; + } + else + { + result = 0; + if (http_data->trace_on) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "%s", text_line); + } + } + return result; +} + +static int write_data_line(HTTP_CLIENT_HANDLE_DATA* http_data, const unsigned char* data_line, size_t length) +{ + int result; + if (xio_send(http_data->xio_handle, data_line, length, send_complete_callback, NULL) != 0) + { + LogError("Failure calling xio_send."); + result = __FAILURE__; + } + else + { + result = 0; + if (http_data->trace_on) + { + if (length > 0) + { + if (http_data->trace_body) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n%.*s\r\n", (int)length, (int)length, data_line); + } + else + { + LOG(AZ_LOG_TRACE, LOG_LINE, "<data> len: %d\r\n", (int)length); + } + } + else + { + LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n", (int)length); + } + } + } + return result; +} + +static int convert_char_to_hex(const unsigned char* hexText, size_t len) +{ + int result = 0; + for (size_t index = 0; index < len; index++) + { + if (hexText[index] == ';') + { + break; + } + else + { + int accumulator = 0; + if (hexText[index] >= 48 && hexText[index] <= 57) + { + accumulator = hexText[index] - 48; + } + else if (hexText[index] >= 65 && hexText[index] <= 70) + { + accumulator = hexText[index] - 55; + } + else if (hexText[index] >= 97 && hexText[index] <= 102) + { + accumulator = hexText[index] - 87; + } + if (index > 0) + { + result = result << 4; + } + result += accumulator; + } + } + return result; +} + +static void on_bytes_received(void* context, const unsigned char* buffer, size_t len) +{ + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; + + if (http_data != NULL && buffer != NULL && len > 0 && http_data->recv_msg.recv_state != state_error) + { + if (http_data->recv_msg.recv_state == state_initial || http_data->recv_msg.recv_state == state_open) + { + if (initialize_received_data(http_data) != 0) + { + http_data->recv_msg.recv_state = state_error; + } + else + { + http_data->recv_msg.recv_state = state_process_status_line; + } + } + + // Put the data in the buffer + if (BUFFER_append_build(http_data->recv_msg.accrual_buff, buffer, len) != 0) + { + /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ + LogError("Failure appending bytes to buffer."); + http_data->recv_msg.recv_state = state_error; + } + + if (http_data->recv_msg.recv_state == state_process_status_line) + { + size_t index = 0; + const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff); + size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); + + int lineComplete = process_status_code_line(stored_bytes, stored_len, &index, &http_data->recv_msg.status_code); + if (lineComplete == 0 && http_data->recv_msg.status_code > 0) + { + if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0) + { + LogError("Failure appending bytes to buffer."); + http_data->recv_msg.recv_state = state_error; + } + else + { + http_data->recv_msg.recv_state = state_process_headers; + } + } + } + + if (http_data->recv_msg.recv_state == state_process_headers) + { + size_t index = 0; + const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff); + size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); + + int headerComplete = process_header_line(stored_bytes, stored_len, &index, http_data->recv_msg.resp_header, &http_data->recv_msg.total_body_len, &http_data->recv_msg.chunked_reply); + if (headerComplete == 0) + { + if (http_data->recv_msg.total_body_len == 0) + { + if (http_data->recv_msg.chunked_reply) + { + + /* Codes_SRS_UHTTP_07_054: [ If the http header does not include a content length then it indicates a chunk response. ] */ + http_data->recv_msg.recv_state = state_process_chunked_body; + } + else + { + // Content len is 0 so we are finished with the body + http_data->recv_msg.recv_state = state_send_user_callback; + } + } + else + { + http_data->recv_msg.recv_state = state_process_body; + } + } + if (index > 0) + { + if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0) + { + LogError("Failure appending bytes to buffer."); + http_data->recv_msg.recv_state = state_error; + } + } + } + + if (http_data->recv_msg.recv_state == state_process_body) + { + if (http_data->recv_msg.total_body_len != 0) + { + size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); + + if ((http_data->recv_msg.total_body_len == stored_len) || (http_data->recv_msg.total_body_len == (stored_len - HTTP_END_TOKEN_LEN))) + { + if (http_data->recv_msg.msg_body != NULL) + { + BUFFER_delete(http_data->recv_msg.msg_body); + } + if ((http_data->recv_msg.msg_body = BUFFER_clone(http_data->recv_msg.accrual_buff)) == NULL) + { + LogError("Failure cloning BUFFER."); + http_data->recv_msg.recv_state = state_error; + } + else + { + http_data->recv_msg.recv_state = state_send_user_callback; + } + } + else if (stored_len > http_data->recv_msg.total_body_len) + { + LogError("Failure bytes encountered is greater then body length."); + http_data->recv_msg.recv_state = state_error; + } + } + } + + if (http_data->recv_msg.recv_state == state_process_chunked_body) + { + const unsigned char* iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff); + const unsigned char* initial_pos = iterator; + const unsigned char* begin = iterator; + const unsigned char* end = iterator; + size_t accural_len = BUFFER_length(http_data->recv_msg.accrual_buff); + + /* Codes_SRS_UHTTP_07_059: [ on_bytes_received shall loop throught the stored data to find the /r/n separator. ] */ + while (iterator < (initial_pos + accural_len)) + { + if (*iterator == '\r') + { + // Don't need anything + end = iterator; + iterator++; + } + else if (*iterator == '\n') + { + size_t data_length = 0; + + /* Codes_SRS_UHTTP_07_055: [ on_bytes_received shall convert the hexs length supplied in the response to the data length of the chunked data. ] */ + size_t hex_len = end - begin; + data_length = convert_char_to_hex(begin, hex_len); + if (data_length == 0) + { + if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN) + { + http_data->recv_msg.recv_state = state_send_user_callback; + } + else + { + // Need to continue parsing + http_data->recv_msg.recv_state = state_process_headers; + } + break; + } + else if ((data_length + HTTP_CRLF_LEN) < accural_len - (iterator - initial_pos)) + { + /* Codes_SRS_UHTTP_07_056: [ After the response chunk is parsed it shall be placed in a BUFFER_HANDLE. ] */ + iterator += 1; + if (BUFFER_append_build(http_data->recv_msg.msg_body, iterator, data_length) != 0) + { + /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the stop processing the request. ] */ + LogError("Failure building buffer for chunked data."); + http_data->recv_msg.recv_state = state_error; + } + else + { + /* Codes_SRS_UHTTP_07_060: [ if the data_length specified in the chunk is beyond the amount of data recieved, the parsing shall end and wait for more data. ] */ + if (iterator + (data_length + HTTP_CRLF_LEN) > initial_pos + accural_len) + { + LogError("Invalid length specified."); + http_data->recv_msg.recv_state = state_error; + break; + } + else if (iterator + (data_length + HTTP_CRLF_LEN) == initial_pos + accural_len) + { + if (BUFFER_shrink(http_data->recv_msg.accrual_buff, accural_len, false) != 0) + { + LogError("Failure shrinking accural buffer."); + http_data->recv_msg.recv_state = state_error; + } + break; + } + else + { + // Move the iterator beyond the data we read and the /r/n + iterator += (data_length + HTTP_CRLF_LEN); + } + + /* Codes_SRS_UHTTP_07_058: [ Once a chunk size value of 0 is encountered on_bytes_received shall call the on_request_callback with the http message ] */ + if (*iterator == '0' && (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)) + { + if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN) + { + http_data->recv_msg.recv_state = state_send_user_callback; + } + else + { + // Need to continue parsing + http_data->recv_msg.recv_state = state_process_headers; + } + break; + } + else + { + size_t shrink_len = iterator - initial_pos; + if (shrink_len > 0) + { + if (BUFFER_shrink(http_data->recv_msg.accrual_buff, shrink_len, false) != 0) + { + LogError("Failure shrinking accrual buffer."); + http_data->recv_msg.recv_state = state_error; + } + else + { + accural_len = BUFFER_length(http_data->recv_msg.accrual_buff); + initial_pos = iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff); + } + } + } + } + begin = end = iterator; + } + else + { + break; + } + } + else + { + end = iterator; + iterator++; + } + } + } + + if (http_data->recv_msg.recv_state == state_send_user_callback || http_data->recv_msg.recv_state == state_error) + { + const unsigned char* reply_data = NULL; + size_t reply_len = 0; + HTTP_CALLBACK_REASON http_reason = HTTP_CALLBACK_REASON_OK; + if (http_data->recv_msg.msg_body != NULL) + { + reply_data = BUFFER_u_char(http_data->recv_msg.msg_body); + reply_len = BUFFER_length(http_data->recv_msg.msg_body); + } + if (http_data->recv_msg.recv_state == state_error) + { + http_reason = HTTP_CALLBACK_REASON_PARSING_ERROR; + } + if (http_data->trace_on) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "\r\nHTTP Status: %d\r\n", http_data->recv_msg.status_code); + + // Loop through headers + size_t count; + HTTPHeaders_GetHeaderCount(http_data->recv_msg.resp_header, &count); + for (size_t index = 0; index < count; index++) + { + char* header; + if (HTTPHeaders_GetHeader(http_data->recv_msg.resp_header, index, &header) == HTTP_HEADERS_OK) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "%s", header); + free(header); + } + } + if (http_data->trace_body && reply_len > 0) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "\r\n%.*s\r\n", (int)reply_len, reply_data); + } + } + http_data->recv_msg.on_request_callback(http_data->recv_msg.user_ctx, http_reason, reply_data, reply_len, http_data->recv_msg.status_code, http_data->recv_msg.resp_header); + http_data->recv_msg.recv_state = state_parse_complete; + } + + if (http_data->recv_msg.recv_state == state_parse_complete) + { + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + BUFFER_delete(http_data->recv_msg.accrual_buff); + http_data->recv_msg.accrual_buff = NULL; + } + } +} + +static void on_xio_close_complete(void* context) +{ + if (context != NULL) + { + /* Codes_SRS_UHTTP_07_045: [ If on_close_callback is not NULL, on_close_callback shall be called once the underlying xio is closed. ] */ + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; + if (http_data->on_close_callback) + { + http_data->on_close_callback(http_data->close_user_ctx); + } + http_data->recv_msg.recv_state = state_closed; + http_data->connected = 0; + } +} + +static void on_xio_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + /* Codes_SRS_UHTTP_07_049: [ If not NULL uhttp_client_open shall call the on_connect callback with the callback_ctx, once the underlying xio's open is complete. ] */ + if (context != NULL) + { + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; + if (open_result == IO_OPEN_OK) + { + /* Codes_SRS_UHTTP_07_042: [ If the underlying socket opens successfully the on_connect callback shall be call with HTTP_CALLBACK_REASON_OK... ] */ + if (http_data->on_connect != NULL) + { + http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OK); + } + http_data->recv_msg.recv_state = state_open; + http_data->connected = 1; + } + else + { + /* Codes_SRS_UHTTP_07_043: [ Otherwise on_connect callback shall be call with HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ + if (http_data->on_connect != NULL) + { + http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OPEN_FAILED); + } + } + } + else + { + LogError("Context on_xio_open_complete is NULL"); + } +} + +static void on_io_error(void* context) +{ + /* Codes_SRS_UHTTP_07_050: [ if context is NULL on_io_error shall do nothing. ] */ + if (context != NULL) + { + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; + /* Codes_SRS_UHTTP_07_051: [ if on_error callback is not NULL, on_io_error shall call on_error callback. ] */ + if (http_data->on_error) + { + http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_ERROR); + } + http_data->connected = 0; + } + else + { + LogError("Context on_io_error is NULL"); + } +} + +static int construct_http_headers(HTTP_HEADERS_HANDLE http_header, size_t content_len, STRING_HANDLE buffData, bool chunk_data, const char* hostname, int port_num) +{ + (void)chunk_data; + int result = 0; + size_t headerCnt = 0; + if ( (http_header != NULL) && HTTPHeaders_GetHeaderCount(http_header, &headerCnt) != HTTP_HEADERS_OK) + { + LogError("Failed in HTTPHeaders_GetHeaderCount"); + result = __FAILURE__; + } + else + { + bool hostname_found = false; + for (size_t index = 0; index < headerCnt && result == 0; index++) + { + char* header; + if (HTTPHeaders_GetHeader(http_header, index, &header) != HTTP_HEADERS_OK) + { + result = __FAILURE__; + LogError("Failed in HTTPHeaders_GetHeader"); + } + else + { + size_t dataLen = strlen(header)+2; + char* sendData = malloc(dataLen+1); + if (sendData == NULL) + { + result = __FAILURE__; + LogError("Failed in allocating header data"); + } + else + { + if (strcmp(header, HTTP_HOST) == 0) + { + hostname_found = true; + } + + if (snprintf(sendData, dataLen+1, "%s\r\n", header) <= 0) + { + result = __FAILURE__; + LogError("Failed in constructing header data"); + } + else + { + if (STRING_concat(buffData, sendData) != 0) + { + result = __FAILURE__; + LogError("Failed in building header data"); + } + } + free(sendData); + } + free(header); + } + } + if (!hostname_found) + { + size_t host_len = strlen(HTTP_HOST)+strlen(hostname)+8+2; + char* host_header = malloc(host_len+1); + if (host_header == NULL) + { + LogError("Failed allocating host header"); + result = __FAILURE__; + } + else + { + if (snprintf(host_header, host_len+1, "%s: %s:%d\r\n", HTTP_HOST, hostname, port_num) <= 0) + { + LogError("Failed constructing host header"); + result = __FAILURE__; + } + else if (STRING_concat(buffData, host_header) != 0) + { + LogError("Failed adding the host header to the http item"); + result = __FAILURE__; + } + free(host_header); + } + } + + if (result == 0) + { + /* Codes_SRS_UHTTP_07_015: [uhttp_client_execute_request shall add the Content-Length to the request if the contentLength is > 0] */ + size_t fmtLen = strlen(HTTP_CONTENT_LEN)+strlen(HTTP_CRLF_VALUE)+8; + char* content = malloc(fmtLen+1); + if (content == NULL) + { + LogError("Failed allocating chunk header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_UHTTP_07_015: [on_bytes_received shall add the Content-Length http header item to the request.] */ + if (sprintf(content, "%s: %lu%s", HTTP_CONTENT_LEN, content_len, HTTP_CRLF_VALUE) <= 0) + { + result = __FAILURE__; + LogError("Failed allocating content len header data"); + } + else + { + if (STRING_concat(buffData, content) != 0) + { + result = __FAILURE__; + LogError("Failed building content len header data"); + } + } + free(content); + } + + if (STRING_concat(buffData, "\r\n") != 0) + { + result = __FAILURE__; + LogError("Failed sending header finalization data"); + } + } + } + return result; +} + +static STRING_HANDLE construct_http_data(HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, STRING_HANDLE http_line) +{ + STRING_HANDLE result; + + const char* method = (request_type == HTTP_CLIENT_REQUEST_GET) ? "GET" + : (request_type == HTTP_CLIENT_REQUEST_OPTIONS) ? "OPTIONS" + : (request_type == HTTP_CLIENT_REQUEST_POST) ? "POST" + : (request_type == HTTP_CLIENT_REQUEST_PUT) ? "PUT" + : (request_type == HTTP_CLIENT_REQUEST_DELETE) ? "DELETE" + : (request_type == HTTP_CLIENT_REQUEST_PATCH) ? "PATCH" + : NULL; + /* Codes_SRS_UHTTP_07_014: [If the request_type is not a valid request http_client_execute_request shall return HTTP_CLIENT_ERROR] */ + if (method == NULL) + { + LogError("Invalid request method %s specified", method); + result = NULL; + } + else + { + size_t buffLen = strlen(HTTP_REQUEST_LINE_FMT)+strlen(method)+strlen(relative_path); + char* request = malloc(buffLen+1); + if (request == NULL) + { + result = NULL; + LogError("Failure allocating Request data"); + } + else + { + if (snprintf(request, buffLen+1, HTTP_REQUEST_LINE_FMT, method, relative_path) <= 0) + { + result = NULL; + LogError("Failure writing request buffer"); + } + else + { + result = STRING_construct(request); + if (result == NULL) + { + LogError("Failure creating buffer object"); + } + else if (STRING_concat_with_STRING(result, http_line) != 0) + { + STRING_delete(result); + result = NULL; + LogError("Failure writing request buffers"); + } + } + free(request); + } + } + return result; +} + +static int send_http_data(HTTP_CLIENT_HANDLE_DATA* http_data, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, + STRING_HANDLE http_line) +{ + int result; + STRING_HANDLE transmit_data = construct_http_data(request_type, relative_path, http_line); + if (transmit_data == NULL) + { + LogError("Failure constructing http data"); + result = __FAILURE__; + } + else + { + /* Tests_SRS_UHTTP_07_016: [http_client_execute_request shall transmit the http headers data through a call to xio_send;]*/ + if (write_text_line(http_data, STRING_c_str(transmit_data) ) != 0) + { + result = __FAILURE__; + LogError("Failure writing request buffer"); + } + else + { + result = 0; + } + STRING_delete(transmit_data); + } + return result; +} + +HTTP_CLIENT_HANDLE uhttp_client_create(const IO_INTERFACE_DESCRIPTION* io_interface_desc, const void* xio_param, ON_HTTP_ERROR_CALLBACK on_http_error, void* callback_ctx) +{ + HTTP_CLIENT_HANDLE_DATA* result; + /* Codes_SRS_UHTTP_07_002: [If io_interface_desc is NULL, uhttp_client_create shall return NULL.] */ + if (io_interface_desc == NULL) + { + LogError("Invalid Parameter io_interface_desc is NULL"); + result = NULL; + } + else + { + result = malloc(sizeof(HTTP_CLIENT_HANDLE_DATA)); + if (result == NULL) + { + /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */ + LogError("Failure allocating http_client_handle"); + } + else + { + memset(result, 0, sizeof(HTTP_CLIENT_HANDLE_DATA) ); + if ((result->data_list = singlylinkedlist_create() ) == NULL) + { + /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */ + LogError("Failure allocating data list"); + free(result); + result = NULL; + } + else if ((result->xio_handle = xio_create(io_interface_desc, xio_param) ) == NULL) + { + /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ + LogError("xio create failed"); + singlylinkedlist_destroy(result->data_list); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_UHTTP_07_001: [uhttp_client_create shall return an initialize the http client handle.] */ + result->on_error = on_http_error; + result->error_user_ctx = callback_ctx; + result->recv_msg.recv_state = state_initial; + result->chunk_request = false; + result->trace_on = false; + bool ignore_check = true; + (void)xio_setoption(result->xio_handle, "ignore_server_name_check", &ignore_check); + } + } + } + return (HTTP_CLIENT_HANDLE)result; +} + +void uhttp_client_destroy(HTTP_CLIENT_HANDLE handle) +{ + /* Codes_SRS_UHTTP_07_004: [ If handle is NULL then uhttp_client_destroy shall do nothing ] */ + if (handle != NULL) + { + /* Codes_SRS_UHTTP_07_005: [uhttp_client_destroy shall free any resource that is allocated in this translation unit] */ + singlylinkedlist_destroy(handle->data_list); + xio_destroy(handle->xio_handle); + free(handle->certificate); + free(handle->x509_pk); + free(handle->x509_cert); + free(handle); + } +} + +HTTP_CLIENT_RESULT uhttp_client_open(HTTP_CLIENT_HANDLE handle, const char* host, int port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect, void* callback_ctx) +{ + HTTP_CLIENT_RESULT result; + if (handle == NULL || host == NULL) + { + /* Codes_SRS_UHTTP_07_006: [If handle, io_interface_desc or host is NULL then `uhttp_client_open` shall return HTTP_CLIENT_INVALID_ARG] */ + LogError("Invalid handle value"); + result = HTTP_CLIENT_INVALID_ARG; + } + else + { + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; + + if (http_data->recv_msg.recv_state != state_initial && http_data->recv_msg.recv_state != state_error && http_data->recv_msg.recv_state != state_closed) + { + LogError("Unable to open previously open client."); + result = HTTP_CLIENT_INVALID_STATE; + } + else if (mallocAndStrcpy_s(&http_data->host_name, host) != 0) + { + LogError("copying hostname has failed"); + result = HTTP_CLIENT_ERROR; + } + /* Codes_SRS_UHTTP_07_007: [http_client_connect shall attempt to open the xio_handle. ] */ + else + { + result = HTTP_CLIENT_OK; + http_data->recv_msg.recv_state = state_opening; + http_data->on_connect = on_connect; + http_data->connect_user_ctx = callback_ctx; + http_data->port_num = port_num; + + if (http_data->x509_cert != NULL && http_data->x509_pk != NULL) + { + if (xio_setoption(http_data->xio_handle, SU_OPTION_X509_CERT, http_data->x509_cert) != 0 || xio_setoption(http_data->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_data->x509_pk) != 0) + { + LogError("Failed setting x509 certificate"); + result = HTTP_CLIENT_ERROR; + free(http_data->host_name); + http_data->host_name = NULL; + http_data->on_connect = NULL; + http_data->connect_user_ctx = NULL; + http_data->port_num = 0; + } + } + if (result == HTTP_CLIENT_OK && http_data->certificate != NULL) + { + if (xio_setoption(http_data->xio_handle, OPTION_TRUSTED_CERT, http_data->certificate) != 0) + { + LogError("Failed setting Trusted certificate"); + result = HTTP_CLIENT_ERROR; + free(http_data->host_name); + http_data->host_name = NULL; + http_data->on_connect = NULL; + http_data->connect_user_ctx = NULL; + http_data->port_num = 0; + } + } + + if (result == HTTP_CLIENT_OK) + { +#ifdef USE_OPENSSL + // Default to tls 1.2 + int tls_version = 12; + xio_setoption(http_data->xio_handle, OPTION_TLS_VERSION, &tls_version); +#endif + if (xio_open(http_data->xio_handle, on_xio_open_complete, http_data, on_bytes_received, http_data, on_io_error, http_data) != 0) + { + /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ + LogError("opening xio failed"); + free(http_data->host_name); + http_data->host_name = NULL; + http_data->on_connect = NULL; + http_data->connect_user_ctx = NULL; + http_data->port_num = 0; + + result = HTTP_CLIENT_OPEN_FAILED; + } + else + { + /* Codes_SRS_UHTTP_07_008: [If http_client_connect succeeds then it shall return HTTP_CLIENT_OK] */ + result = HTTP_CLIENT_OK; + } + } + } + } + return result; +} + +void uhttp_client_close(HTTP_CLIENT_HANDLE handle, ON_HTTP_CLOSED_CALLBACK on_close_callback, void* callback_ctx) +{ + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; + /* Codes_SRS_UHTTP_07_009: [If handle is NULL then http_client_close shall do nothing] */ + /* Codes_SRS_UHTTP_07_049: [ If the state has been previously set to state_closed, uhttp_client_close shall do nothing. ] */ + if (http_data != NULL && http_data->recv_msg.recv_state != state_closed && http_data->recv_msg.recv_state != state_closing) + { + http_data->on_close_callback = on_close_callback; + http_data->close_user_ctx = callback_ctx; + /* Codes_SRS_UHTTP_07_010: [If the xio_handle is NOT NULL http_client_close shall call xio_close to close the handle] */ + (void)xio_close(http_data->xio_handle, on_xio_close_complete, http_data); + + LIST_ITEM_HANDLE pending_list_item; + while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL) + { + HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item); + if (send_data != NULL) + { + STRING_delete(send_data->relative_path); + BUFFER_delete(send_data->content); + STRING_delete(send_data->header_line); + free(send_data); + } + singlylinkedlist_remove(http_data->data_list, pending_list_item); + } + + http_data->recv_msg.status_code = 0; + http_data->recv_msg.recv_state = state_closing; + http_data->recv_msg.total_body_len = 0; + free(http_data->host_name); + http_data->host_name = NULL; + + /* Codes_SRS_UHTTP_07_011: [http_client_close shall free any HTTPHeader object that has not been freed] */ + if (http_data->recv_msg.resp_header != NULL) + { + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + } + if (http_data->recv_msg.msg_body != NULL) + { + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + } + } +} + +HTTP_CLIENT_RESULT uhttp_client_execute_request(HTTP_CLIENT_HANDLE handle, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, + HTTP_HEADERS_HANDLE http_header_handle, const unsigned char* content, size_t content_len, ON_HTTP_REQUEST_CALLBACK on_request_callback, void* callback_ctx) +{ + HTTP_CLIENT_RESULT result; + LIST_ITEM_HANDLE list_item; + + /* Codes_SRS_UHTTP_07_012: [If handle, relativePath, or httpHeadersHandle is NULL then http_client_execute_request shall return HTTP_CLIENT_INVALID_ARG] */ + if (handle == NULL || on_request_callback == NULL || + (content != NULL && content_len == 0) || (content == NULL && content_len != 0) ) + { + result = HTTP_CLIENT_INVALID_ARG; + LogError("Invalid parameter sent to execute_request"); + } + else + { + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; + + http_data->recv_msg.status_code = 0; + http_data->recv_msg.recv_state = state_initial; + http_data->recv_msg.total_body_len = 0; + http_data->recv_msg.on_request_callback = on_request_callback; + http_data->recv_msg.user_ctx = callback_ctx; + if (http_data->recv_msg.resp_header != NULL) + { + HTTPHeaders_Free(http_data->recv_msg.resp_header); + } + if (http_data->recv_msg.msg_body != NULL) + { + BUFFER_delete(http_data->recv_msg.msg_body); + } + if ((http_data->recv_msg.resp_header = HTTPHeaders_Alloc()) == NULL) + { + /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */ + LogError("Failure allocating http http_data items"); + result = HTTP_CLIENT_ERROR; + } + else if ( (http_data->recv_msg.msg_body = BUFFER_new() ) == NULL) + { + /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */ + LogError("Failure allocating http data items"); + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + result = HTTP_CLIENT_ERROR; + } + else + { + HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)malloc(sizeof(HTTP_SEND_DATA)); + if (send_data == NULL) + { + LogError("Failure allocating http data items"); + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + result = HTTP_CLIENT_ERROR; + } + else + { + memset(send_data, 0, sizeof(HTTP_SEND_DATA)); + /* Codes_SRS_UHTTP_07_041: [HTTP_CLIENT_REQUEST_TYPE shall support all request types specified under section 9.1.2 in the spec.] */ + send_data->request_type = request_type; + if ( (content_len > 0) && (send_data->content = BUFFER_create(content, content_len)) == NULL) + { + LogError("Failure allocating content buffer"); + result = HTTP_CLIENT_ERROR; + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + } + else if ((send_data->header_line = STRING_new()) == NULL) + { + LogError("Failure allocating content buffer"); + result = HTTP_CLIENT_ERROR; + BUFFER_delete(send_data->content); + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + } + else if (construct_http_headers(http_header_handle, content_len, send_data->header_line, false, http_data->host_name, http_data->port_num)) + { + LogError("Failure allocating content buffer"); + result = HTTP_CLIENT_ERROR; + BUFFER_delete(send_data->content); + STRING_delete(send_data->header_line); + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + } + else if ((list_item = singlylinkedlist_add(http_data->data_list, send_data)) == NULL) + { + STRING_delete(send_data->header_line); + BUFFER_delete(send_data->content); + LogError("Failure adding send data to list"); + result = HTTP_CLIENT_ERROR; + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + } + else + { + if (relative_path != NULL) + { + if ((send_data->relative_path = STRING_construct(relative_path)) == NULL) + { + (void)singlylinkedlist_remove(http_data->data_list, list_item); + STRING_delete(send_data->header_line); + BUFFER_delete(send_data->content); + LogError("Failure allocating relative path buffer"); + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + result = HTTP_CLIENT_ERROR; + } + else + { + /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */ + result = HTTP_CLIENT_OK; + } + } + else + { + if ((send_data->relative_path = STRING_construct("/")) == NULL) + { + (void)singlylinkedlist_remove(http_data->data_list, list_item); + STRING_delete(send_data->header_line); + BUFFER_delete(send_data->content); + LogError("Failure allocating relative path buffer"); + BUFFER_delete(http_data->recv_msg.msg_body); + http_data->recv_msg.msg_body = NULL; + HTTPHeaders_Free(http_data->recv_msg.resp_header); + http_data->recv_msg.resp_header = NULL; + free(send_data); + result = HTTP_CLIENT_ERROR; + } + else + { + /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */ + result = HTTP_CLIENT_OK; + } + } + } + } + } + } + return result; +} + +void uhttp_client_dowork(HTTP_CLIENT_HANDLE handle) +{ + if (handle != NULL) + { + /* Codes_SRS_UHTTP_07_037: [http_client_dowork shall call the underlying xio_dowork function. ] */ + HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; + xio_dowork(http_data->xio_handle); + + // Wait till I'm connected + if (handle->connected == 1) + { + LIST_ITEM_HANDLE pending_list_item; + /* Codes_SRS_UHTTP_07_016: [http_client_dowork shall iterate through the queued Data using the xio interface to send the http request in the following ways...] */ + while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL) + { + HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item); + if (send_data != NULL) + { + size_t content_len = BUFFER_length(send_data->content); + /* Codes_SRS_UHTTP_07_052: [uhttp_client_dowork shall call xio_send to transmits the header information... ] */ + if (send_http_data(http_data, send_data->request_type, STRING_c_str(send_data->relative_path), send_data->header_line) != 0) + { + LogError("Failure writing content buffer"); + if (http_data->on_error) + { + http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED); + } + } + else if (content_len > 0) + { + /* Codes_SRS_UHTTP_07_053: [ Then uhttp_client_dowork shall use xio_send to transmit the content of the http request. ] */ + if (write_data_line(http_data, BUFFER_u_char(send_data->content), content_len) != 0) + { + LogError("Failure writing content buffer"); + if (http_data->on_error) + { + http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED); + } + } + } + + /* Codes_SRS_UHTTP_07_046: [ http_client_dowork shall free resouces queued to send to the http endpoint. ] */ + STRING_delete(send_data->relative_path); + BUFFER_delete(send_data->content); + STRING_delete(send_data->header_line); + free(send_data); + } + (void)singlylinkedlist_remove(http_data->data_list, pending_list_item); + } + } + } +} + +HTTP_CLIENT_RESULT uhttp_client_set_trace(HTTP_CLIENT_HANDLE handle, bool trace_on, bool trace_data) +{ + HTTP_CLIENT_RESULT result; + if (handle == NULL) + { + /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ + result = HTTP_CLIENT_INVALID_ARG; + LogError("invalid parameter (NULL) passed to http_client_set_trace"); + } + else + { + /* Codes_SRS_UHTTP_07_039: [http_client_set_trace shall set the HTTP tracing to the trace_on variable.] */ + handle->trace_on = trace_on; + handle->trace_body = trace_data; + /* Codes_SRS_UHTTP_07_040: [if http_client_set_trace finishes successfully then it shall return HTTP_CLIENT_OK.] */ + result = HTTP_CLIENT_OK; + } + return result; +} + +HTTP_CLIENT_RESULT uhttp_client_set_X509_cert(HTTP_CLIENT_HANDLE handle, bool ecc_type, const char* certificate, const char* private_key) +{ + HTTP_CLIENT_RESULT result; + if (handle == NULL || certificate == NULL || private_key == NULL) + { + /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ + result = HTTP_CLIENT_INVALID_ARG; + LogError("invalid parameter handle: %p certificate: %p private_key: %p", handle, certificate, private_key); + } + else if (handle->recv_msg.recv_state != state_initial) + { + result = HTTP_CLIENT_INVALID_STATE; + LogError("You must set the X509 certificates before opening the connection"); + } + else + { + handle->cert_type_ecc = ecc_type; + if (mallocAndStrcpy_s(&handle->x509_cert, certificate) != 0) + { + result = HTTP_CLIENT_ERROR; + LogError("failure allocating certificate"); + } + else if (mallocAndStrcpy_s(&handle->x509_pk, private_key) != 0) + { + free(handle->x509_cert); + handle->x509_cert = NULL; + result = HTTP_CLIENT_ERROR; + LogError("failure allocating private key"); + } + else + { + result = HTTP_CLIENT_OK; + } + } + return result; +} + +HTTP_CLIENT_RESULT uhttp_client_set_trusted_cert(HTTP_CLIENT_HANDLE handle, const char* certificate) +{ + HTTP_CLIENT_RESULT result; + if (handle == NULL || certificate == NULL) + { + /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ + result = HTTP_CLIENT_INVALID_ARG; + LogError("invalid parameter handle: %p certificate: %p", handle, certificate); + } + else if (handle->recv_msg.recv_state != state_initial) + { + result = HTTP_CLIENT_INVALID_STATE; + LogError("You must set the certificates before opening the connection"); + } + else + { + if (mallocAndStrcpy_s(&handle->certificate, certificate) != 0) + { + result = HTTP_CLIENT_ERROR; + LogError("failure allocating certificate"); + } + else + { + result = HTTP_CLIENT_OK; + } + } + return result; +} + +const char* uhttp_client_get_trusted_cert(HTTP_CLIENT_HANDLE handle) +{ + const char* result; + if (handle == NULL) + { + result = NULL; + LogError("invalid parameter NULL handle"); + } + else + { + result = handle->certificate; + } + return result; +} + +HTTP_CLIENT_RESULT uhttp_client_set_option(HTTP_CLIENT_HANDLE handle, const char* optionName, const void* value) +{ + HTTP_CLIENT_RESULT result; + if (handle == NULL) + { + /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ + result = HTTP_CLIENT_INVALID_ARG; + LogError("invalid parameter handle: %p", handle); + } + else + { + int setoption_result = xio_setoption(handle->xio_handle, optionName, value); + if (setoption_result != 0) + { + LogError("xio_setoption fails, returns %d", setoption_result); + result = HTTP_CLIENT_ERROR; + } + else + { + result = HTTP_CLIENT_OK; + } + + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/blob.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file blob.h +* @brief Contains blob APIs needed for File Upload feature of IoTHub client. +* +* @details IoTHub client needs to upload a byte array by using blob storage API +* IoTHub service provides the complete SAS URI to execute a PUT request +* that will upload the data. +* +*/ + +#ifndef BLOB_H +#define BLOB_H + +#include "../../c-utility/inc/azure_c_shared_utility/macro_utils.h" +#include "../../c-utility/inc/azure_c_shared_utility/buffer_.h" +#include "../../c-utility/inc/azure_c_shared_utility/strings_types.h" +#include "../../c-utility/inc/azure_c_shared_utility/httpapiex.h" +#include "iothub_client_core_ll.h" +#include "../../c-utility/inc/azure_c_shared_utility/shared_util_options.h" +#include "iothub_client_ll.h" + +#ifdef __cplusplus + +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#include "../../c-utility/inc/azure_c_shared_utility/umock_c_prod.h" + +/* Allow unit tests to override MAX_BLOCK_COUNT to something much smaller */ +#ifndef MAX_BLOCK_COUNT +/* Maximum count of blocks uploaded is 50000, per server*/ +#define MAX_BLOCK_COUNT 50000 +#endif + +#define BLOB_RESULT_VALUES \ + BLOB_OK, \ + BLOB_ERROR, \ + BLOB_NOT_IMPLEMENTED, \ + BLOB_HTTP_ERROR, \ + BLOB_INVALID_ARG, \ + BLOB_ABORTED + +DEFINE_ENUM(BLOB_RESULT, BLOB_RESULT_VALUES) + +/** +* @brief Synchronously uploads a byte array to blob storage +* +* @param SASURI The URI to use to upload data +* @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. +* @param context Any data provided by the user to serve as context on getDataCallback. +* @param httpStatus A pointer to an out argument receiving the HTTP status (available only when the return value is BLOB_OK) +* @param httpResponse A BUFFER_HANDLE that receives the HTTP response from the server (available only when the return value is BLOB_OK) +* @param certificates A null terminated string containing CA certificates to be used +* @param proxyOptions A structure that contains optional web proxy information +* +* @return A @c BLOB_RESULT. BLOB_OK means the blob has been uploaded successfully. Any other value indicates an error +*/ +MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadMultipleBlocksFromSasUri, const char*, SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse, const char*, certificates, HTTP_PROXY_OPTIONS*, proxyOptions) + +/** +* @brief Synchronously uploads a byte array as a new block to blob storage +* +* @param requestContent The data to upload +* @param blockId The block id (from 00000 to 49999) +* @param xml The XML file containing the blockId list +* @param relativePath The destination path within the storage +* @param httpApiExHandle The connection handle +* @param httpStatus A pointer to an out argument receiving the HTTP status (available only when the return value is BLOB_OK) +* @param httpResponse A BUFFER_HANDLE that receives the HTTP response from the server (available only when the return value is BLOB_OK) +*/ +//MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadNextBlock, BUFFER_HANDLE, requestContent, unsigned int, blockID, STRING_HANDLE, xml, const char*, relativePath, HTTPAPIEX_HANDLE, httpApiExHandle, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse) +MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadBlock, HTTPAPIEX_HANDLE, httpApiExHandle, const char*, relativePath, BUFFER_HANDLE, requestContent, unsigned int, blockID, STRING_HANDLE, blockIDList, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse) +#ifdef __cplusplus +} +#endif + +#endif /* BLOB_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/blob.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file blob.h +* @brief Contains blob APIs needed for File Upload feature of IoTHub client. +* +* @details IoTHub client needs to upload a byte array by using blob storage API +* IoTHub service provides the complete SAS URI to execute a PUT request +* that will upload the data. +* +*/ + +#ifndef BLOB_H +#define BLOB_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/strings_types.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "iothub_client_core_ll.h" +#include "azure_c_shared_utility/shared_util_options.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +/* Allow unit tests to override MAX_BLOCK_COUNT to something much smaller */ +#ifndef MAX_BLOCK_COUNT +/* Maximum count of blocks uploaded is 50000, per server*/ +#define MAX_BLOCK_COUNT 50000 +#endif + +#define BLOB_RESULT_VALUES \ + BLOB_OK, \ + BLOB_ERROR, \ + BLOB_NOT_IMPLEMENTED, \ + BLOB_HTTP_ERROR, \ + BLOB_INVALID_ARG, \ + BLOB_ABORTED + +DEFINE_ENUM(BLOB_RESULT, BLOB_RESULT_VALUES) + +/** +* @brief Synchronously uploads a byte array to blob storage +* +* @param SASURI The URI to use to upload data +* @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. +* @param context Any data provided by the user to serve as context on getDataCallback. +* @param httpStatus A pointer to an out argument receiving the HTTP status (available only when the return value is BLOB_OK) +* @param httpResponse A BUFFER_HANDLE that receives the HTTP response from the server (available only when the return value is BLOB_OK) +* @param certificates A null terminated string containing CA certificates to be used +* @param proxyOptions A structure that contains optional web proxy information +* +* @return A @c BLOB_RESULT. BLOB_OK means the blob has been uploaded successfully. Any other value indicates an error +*/ +MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadMultipleBlocksFromSasUri, const char*, SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse, const char*, certificates, HTTP_PROXY_OPTIONS*, proxyOptions) + +/** +* @brief Synchronously uploads a byte array as a new block to blob storage +* +* @param requestContent The data to upload +* @param blockId The block id (from 00000 to 49999) +* @param xml The XML file containing the blockId list +* @param relativePath The destination path within the storage +* @param httpApiExHandle The connection handle +* @param httpStatus A pointer to an out argument receiving the HTTP status (available only when the return value is BLOB_OK) +* @param httpResponse A BUFFER_HANDLE that receives the HTTP response from the server (available only when the return value is BLOB_OK) +*/ +//MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadNextBlock, BUFFER_HANDLE, requestContent, unsigned int, blockID, STRING_HANDLE, xml, const char*, relativePath, HTTPAPIEX_HANDLE, httpApiExHandle, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse) +MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadBlock, HTTPAPIEX_HANDLE, httpApiExHandle, const char*, relativePath, BUFFER_HANDLE, requestContent, unsigned int, blockID, STRING_HANDLE, blockIDList, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse) +#ifdef __cplusplus +} +#endif + +#endif /* BLOB_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_authorization.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_CLIENT_AUTHORIZATION_H +#define IOTHUB_CLIENT_AUTHORIZATION_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/xio.h" + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif /* __cplusplus */ + +typedef struct IOTHUB_AUTHORIZATION_DATA_TAG* IOTHUB_AUTHORIZATION_HANDLE; + +#define IOTHUB_CREDENTIAL_TYPE_VALUES \ + IOTHUB_CREDENTIAL_TYPE_UNKNOWN, \ + IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY, \ + IOTHUB_CREDENTIAL_TYPE_X509, \ + IOTHUB_CREDENTIAL_TYPE_X509_ECC, \ + IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN, \ + IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH + + +DEFINE_ENUM(IOTHUB_CREDENTIAL_TYPE, IOTHUB_CREDENTIAL_TYPE_VALUES); + +#define SAS_TOKEN_STATUS_VALUES \ + SAS_TOKEN_STATUS_FAILED, \ + SAS_TOKEN_STATUS_VALID, \ + SAS_TOKEN_STATUS_INVALID + +DEFINE_ENUM(SAS_TOKEN_STATUS, SAS_TOKEN_STATUS_VALUES); + +MOCKABLE_FUNCTION(, IOTHUB_AUTHORIZATION_HANDLE, IoTHubClient_Auth_Create, const char*, device_key, const char*, device_id, const char*, device_sas_token, const char *, module_id); +MOCKABLE_FUNCTION(, IOTHUB_AUTHORIZATION_HANDLE, IoTHubClient_Auth_CreateFromDeviceAuth, const char*, device_id, const char*, module_id); +MOCKABLE_FUNCTION(, void, IoTHubClient_Auth_Destroy, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Set_x509_Type, IOTHUB_AUTHORIZATION_HANDLE, handle, bool, enable_x509); +MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Get_Credential_Type, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, char*, IoTHubClient_Auth_Get_SasToken, IOTHUB_AUTHORIZATION_HANDLE, handle, const char*, scope, size_t, expiry_time_relative_seconds, const char*, key_name); +MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Set_xio_Certificate, IOTHUB_AUTHORIZATION_HANDLE, handle, XIO_HANDLE, xio); +MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_DeviceId, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_ModuleId, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_DeviceKey, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, SAS_TOKEN_STATUS, IoTHubClient_Auth_Is_SasToken_Valid, IOTHUB_AUTHORIZATION_HANDLE, handle); + +#ifdef USE_EDGE_MODULES +MOCKABLE_FUNCTION(, char*, IoTHubClient_Auth_Get_TrustBundle, IOTHUB_AUTHORIZATION_HANDLE, handle); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_AUTHORIZATION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_diagnostic.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_diagnostic.h +* @brief The @c diagnostic is a component that helps to add predefined diagnostic + properties to message for end to end diagnostic purpose +*/ + +#ifndef IOTHUB_CLIENT_DIAGNOSTIC_H +#define IOTHUB_CLIENT_DIAGNOSTIC_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_message.h" +#include <stdint.h> + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +/** @brief diagnostic related setting */ +typedef struct IOTHUB_DIAGNOSTIC_SETTING_DATA_TAG +{ + uint32_t diagSamplingPercentage; + uint32_t currentMessageNumber; +} IOTHUB_DIAGNOSTIC_SETTING_DATA; + +/** + * @brief Adds diagnostic information to message if: + * a. diagSetting->diagSamplingPercentage > 0 and + * b. the number of current message matches sample rule specified by diagSetting->diagSamplingPercentage + * + * @param diagSetting Pointer to an @c IOTHUB_DIAGNOSTIC_SETTING_DATA structure + * + * @param messageHandle message handle + * + * @return 0 upon success + */ +MOCKABLE_FUNCTION(, int, IoTHubClient_Diagnostic_AddIfNecessary, IOTHUB_DIAGNOSTIC_SETTING_DATA *, diagSetting, IOTHUB_MESSAGE_HANDLE, messageHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_DIAGNOSTIC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_edge.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,29 @@ +#ifndef IOTHUB_CLIENT_EDGE_H +#define IOTHUB_CLIENT_EDGE_H + +#include <stddef.h> + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_client_authorization.h" +#include "iothub_client_core_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct IOTHUB_CLIENT_EDGE_HANDLE_DATA_TAG* IOTHUB_CLIENT_EDGE_HANDLE; + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_EDGE_HANDLE, IoTHubClient_EdgeHandle_Create, const IOTHUB_CLIENT_CONFIG*, config, IOTHUB_AUTHORIZATION_HANDLE, authorizationHandle, const char*, module_id); + MOCKABLE_FUNCTION(, void, IoTHubClient_EdgeHandle_Destroy, IOTHUB_CLIENT_EDGE_HANDLE, methodHandle); + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_Edge_DeviceMethodInvoke, IOTHUB_CLIENT_EDGE_HANDLE, moduleMethodHandle, const char*, deviceId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_Edge_ModuleMethodInvoke, IOTHUB_CLIENT_EDGE_HANDLE, moduleMethodHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_EDGE_H */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_hsm_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_version.h +* @brief Functions for managing the client SDK version. +*/ + +#ifndef IOTHUB_CLIENT_PROVISIONING_H +#define IOTHUB_CLIENT_PROVISIONING_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef USE_PROV_MODULE +#include "azure_prov_client/iothub_auth_client.h" +#endif + +#include "iothub_client_ll.h" + +/*This header is DEPREPCATED and mainatined only for backward compatibility*/ + +#endif // IOTHUB_CLIENT_PROVISIONING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_ll_uploadtoblob.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a device) to communicate +* with an Azure IoTHub. +* +* @details IoTHubClient_LL is a module that allows a user (usually a +* device) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef DONT_USE_UPLOADTOBLOB + +#ifndef IOTHUB_CLIENT_LL_UPLOADTOBLOB_H +#define IOTHUB_CLIENT_LL_UPLOADTOBLOB_H + +#include "iothub_client_core_ll.h" + +#include "azure_c_shared_utility/umock_c_prod.h" +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + + #define BLOCK_SIZE (4*1024*1024) + + typedef struct IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE; + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, IoTHubClient_LL_UploadToBlob_Create, const IOTHUB_CLIENT_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, const unsigned char*, source, size_t, size); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_SetOption, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, optionName, const void*, value); + MOCKABLE_FUNCTION(, void, IoTHubClient_LL_UploadToBlob_Destroy, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle); +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_LL_UPLOADTOBLOB_H */ + +#else +#error "trying to #include iothub_client_ll_uploadtoblob.h in the presence of #define DONT_USE_UPLOADTOBLOB" +#endif /*DONT_USE_UPLOADTOBLOB*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_private.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_CLIENT_PRIVATE_H +#define IOTHUB_CLIENT_PRIVATE_H + +#include <stdbool.h> +#include <signal.h> + +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_message.h" +#include "iothub_client_core_ll.h" +#include "internal/iothub_transport_ll_private.h" +#include "internal/iothubtransport.h" + +#ifdef USE_EDGE_MODULES +#include "internal/iothub_client_edge.h" +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define EVENT_ENDPOINT "/messages/events" +#define MESSAGE_ENDPOINT "/messages/devicebound" +#define MESSAGE_ENDPOINT_HTTP "/messages/devicebound" +#define MESSAGE_ENDPOINT_HTTP_ETAG "/messages/devicebound/" +#define CLIENT_DEVICE_TYPE_PREFIX "iothubclient" +#define CLIENT_DEVICE_BACKSLASH "/" +#define CBS_REPLY_TO "cbs" +#define CBS_ENDPOINT "/$" CBS_REPLY_TO +#define API_VERSION "?api-version=2016-11-14" +#define REJECT_QUERY_PARAMETER "&reject" + +typedef bool(*IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX)(MESSAGE_CALLBACK_INFO* messageData, void* userContextCallback); + +MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_SendComplete, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, PDLIST_ENTRY, completed, IOTHUB_CLIENT_CONFIRMATION_RESULT, result); +MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_ReportedStateComplete, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, uint32_t, item_id, int, status_code); +MOCKABLE_FUNCTION(, bool, IoTHubClientCore_LL_MessageCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, MESSAGE_CALLBACK_INFO*, message_data); +MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_RetrievePropertyComplete, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, DEVICE_TWIN_UPDATE_STATE, update_state, const unsigned char*, payLoad, size_t, size); +MOCKABLE_FUNCTION(, int, IoTHubClientCore_LL_DeviceMethodComplete, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, const char*, method_name, const unsigned char*, payLoad, size_t, size, METHOD_HANDLE, response_id); +MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_ConnectionStatusCallBack, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, IOTHUB_CLIENT_CONNECTION_STATUS, status, IOTHUB_CLIENT_CONNECTION_STATUS_REASON, reason); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetMessageCallback_Ex, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX, messageCallback, void*, userContextCallback); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SendMessageDisposition, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, MESSAGE_CALLBACK_INFO*, messageData, IOTHUBMESSAGE_DISPOSITION_RESULT, disposition); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_GetOption, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, optionName, void**, value); +MOCKABLE_FUNCTION(, bool, IoTHubClientCore_LL_MessageCallbackFromInput, IOTHUB_CLIENT_CORE_LL_HANDLE, handle, MESSAGE_CALLBACK_INFO*, message_data); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetInputMessageCallbackEx, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX, eventHandlerCallbackEx, void *, userContextCallback, size_t, userContextCallbackLength); + +#ifdef USE_EDGE_MODULES +/* (Should be replaced after iothub_client refactor)*/ +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_EDGE_HANDLE, IoTHubClientCore_LL_GetEdgeHandle, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_GenericMethodInvoke, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); +#endif + +typedef struct IOTHUB_MESSAGE_LIST_TAG +{ + IOTHUB_MESSAGE_HANDLE messageHandle; + IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK callback; + void* context; + DLIST_ENTRY entry; + tickcounter_ms_t ms_timesOutAfter; /* a value of "0" means "no timeout", if the IOTHUBCLIENT_LL's handle tickcounter > msTimesOutAfer then the message shall timeout*/ +}IOTHUB_MESSAGE_LIST; + +typedef struct IOTHUB_DEVICE_TWIN_TAG +{ + uint32_t item_id; + tickcounter_ms_t ms_timesOutAfter; /* a value of "0" means "no timeout", if the IOTHUBCLIENT_LL's handle tickcounter > msTimesOutAfer then the message shall timeout*/ + IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reported_state_callback; + CONSTBUFFER_HANDLE report_data_handle; + void* context; + DLIST_ENTRY entry; + IOTHUB_CLIENT_CORE_LL_HANDLE client_handle; + IOTHUB_DEVICE_HANDLE device_handle; +} IOTHUB_DEVICE_TWIN; + +union IOTHUB_IDENTITY_INFO_TAG +{ + IOTHUB_DEVICE_TWIN* device_twin; + IOTHUB_MESSAGE_LIST* iothub_message; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_PRIVATE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_client_retry_control.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_CLIENT_RETRY_CONTROL +#define IOTHUB_CLIENT_RETRY_CONTROL + +#include <stdlib.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_client_core_ll.h" +#include "internal/iothubtransport.h" +#include "azure_c_shared_utility/const_defines.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS = "initial_wait_time_in_secs"; +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT = "max_jitter_percent"; +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_SAVED_OPTIONS = "retry_control_saved_options"; + +typedef enum RETRY_ACTION_TAG +{ + RETRY_ACTION_RETRY_NOW, + RETRY_ACTION_RETRY_LATER, + RETRY_ACTION_STOP_RETRYING +} RETRY_ACTION; + +struct RETRY_CONTROL_INSTANCE_TAG; +typedef struct RETRY_CONTROL_INSTANCE_TAG* RETRY_CONTROL_HANDLE; + +MOCKABLE_FUNCTION(, RETRY_CONTROL_HANDLE, retry_control_create, IOTHUB_CLIENT_RETRY_POLICY, policy, unsigned int, max_retry_time_in_secs); +MOCKABLE_FUNCTION(, int, retry_control_should_retry, RETRY_CONTROL_HANDLE, retry_control_handle, RETRY_ACTION*, retry_action); +MOCKABLE_FUNCTION(, void, retry_control_reset, RETRY_CONTROL_HANDLE, retry_control_handle); +MOCKABLE_FUNCTION(, int, retry_control_set_option, RETRY_CONTROL_HANDLE, retry_control_handle, const char*, name, const void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, retry_control_retrieve_options, RETRY_CONTROL_HANDLE, retry_control_handle); +MOCKABLE_FUNCTION(, void, retry_control_destroy, RETRY_CONTROL_HANDLE, retry_control_handle); + +MOCKABLE_FUNCTION(, int, is_timeout_reached, time_t, start_time, unsigned int, timeout_in_secs, bool*, is_timed_out); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_CLIENT_RETRY_CONTROL \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothub_transport_ll_private.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_TRANSPORT_LL_PRIVATE_H +#define IOTHUB_TRANSPORT_LL_PRIVATE_H + +#include "iothub_transport_ll.h" +#include "internal/iothub_client_authorization.h" + + +union IOTHUB_IDENTITY_INFO_TAG; +typedef union IOTHUB_IDENTITY_INFO_TAG IOTHUB_IDENTITY_INFO; + +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/strings.h" +#include "iothub_message.h" +#include "internal/iothub_client_authorization.h" + +struct MESSAGE_DISPOSITION_CONTEXT_TAG; +typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG* MESSAGE_DISPOSITION_CONTEXT_HANDLE; +typedef struct MESSAGE_CALLBACK_INFO_TAG +{ + IOTHUB_MESSAGE_HANDLE messageHandle; + MESSAGE_DISPOSITION_CONTEXT_HANDLE transportContext; +}MESSAGE_CALLBACK_INFO; + +#include "iothub_client_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** @brief This struct captures device configuration. */ + typedef struct IOTHUB_DEVICE_CONFIG_TAG + { + /** @brief A string that identifies the device. */ + const char* deviceId; + + /** @brief The device key used to authenticate the device. */ + const char* deviceKey; + + /** @brief The device SAS used to authenticate the device in place of using the device key. */ + const char* deviceSasToken; + + IOTHUB_AUTHORIZATION_HANDLE authorization_module; // with either SAS Token, x509 Certs, and Device SAS Token + + /** @brief A string that identifies the module. Optional. */ + const char* moduleId; + } IOTHUB_DEVICE_CONFIG; + + typedef STRING_HANDLE (*pfIoTHubTransport_GetHostname)(TRANSPORT_LL_HANDLE handle); + typedef IOTHUB_CLIENT_RESULT(*pfIoTHubTransport_SetOption)(TRANSPORT_LL_HANDLE handle, const char *optionName, const void* value); + typedef TRANSPORT_LL_HANDLE(*pfIoTHubTransport_Create)(const IOTHUBTRANSPORT_CONFIG* config); + typedef void (*pfIoTHubTransport_Destroy)(TRANSPORT_LL_HANDLE handle); + typedef IOTHUB_DEVICE_HANDLE(*pfIotHubTransport_Register)(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend); + typedef void(*pfIotHubTransport_Unregister)(IOTHUB_DEVICE_HANDLE deviceHandle); + typedef int (*pfIoTHubTransport_Subscribe)(IOTHUB_DEVICE_HANDLE handle); + typedef void (*pfIoTHubTransport_Unsubscribe)(IOTHUB_DEVICE_HANDLE handle); + typedef void (*pfIoTHubTransport_DoWork)(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle); + typedef int(*pfIoTHubTransport_SetRetryPolicy)(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds); + typedef IOTHUB_CLIENT_RESULT(*pfIoTHubTransport_GetSendStatus)(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); + typedef int (*pfIoTHubTransport_Subscribe_DeviceTwin)(IOTHUB_DEVICE_HANDLE handle); + typedef void (*pfIoTHubTransport_Unsubscribe_DeviceTwin)(IOTHUB_DEVICE_HANDLE handle); + typedef IOTHUB_CLIENT_RESULT(*pfIotHubTransport_SendMessageDisposition)(MESSAGE_CALLBACK_INFO* messageData, IOTHUBMESSAGE_DISPOSITION_RESULT disposition); + typedef IOTHUB_PROCESS_ITEM_RESULT(*pfIoTHubTransport_ProcessItem)(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item); + typedef int(*pfIoTHubTransport_Subscribe_DeviceMethod)(IOTHUB_DEVICE_HANDLE handle); + typedef void(*pfIoTHubTransport_Unsubscribe_DeviceMethod)(IOTHUB_DEVICE_HANDLE handle); + typedef int(*pfIoTHubTransport_DeviceMethod_Response)(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response); + typedef int(*pfIoTHubTransport_Subscribe_InputQueue)(IOTHUB_DEVICE_HANDLE handle); + typedef void(*pfIoTHubTransport_Unsubscribe_InputQueue)(IOTHUB_DEVICE_HANDLE handle); + +#define TRANSPORT_PROVIDER_FIELDS \ +pfIotHubTransport_SendMessageDisposition IoTHubTransport_SendMessageDisposition; \ +pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod; \ +pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;\ +pfIoTHubTransport_DeviceMethod_Response IoTHubTransport_DeviceMethod_Response; \ +pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin; \ +pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin; \ +pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem; \ +pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname; \ +pfIoTHubTransport_SetOption IoTHubTransport_SetOption; \ +pfIoTHubTransport_Create IoTHubTransport_Create; \ +pfIoTHubTransport_Destroy IoTHubTransport_Destroy; \ +pfIotHubTransport_Register IoTHubTransport_Register; \ +pfIotHubTransport_Unregister IoTHubTransport_Unregister; \ +pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe; \ +pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe; \ +pfIoTHubTransport_DoWork IoTHubTransport_DoWork; \ +pfIoTHubTransport_SetRetryPolicy IoTHubTransport_SetRetryPolicy; \ +pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus; \ +pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; \ +pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue /*there's an intentional missing ; on this line*/ + + struct TRANSPORT_PROVIDER_TAG + { + TRANSPORT_PROVIDER_FIELDS; + }; + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_TRANSPORT_LL_PRIVATE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_TRANSPORT_H +#define IOTHUB_TRANSPORT_H + +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core.h" +#include "internal/iothub_client_private.h" +#include "internal/iothub_transport_ll_private.h" +#include "iothub_client_authorization.h" + +#ifndef IOTHUB_CLIENT_CORE_INSTANCE_TYPE +typedef struct IOTHUB_CLIENT_CORE_INSTANCE_TAG* IOTHUB_CLIENT_CORE_HANDLE; +#define IOTHUB_CLIENT_CORE_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_CORE_INSTANCE + +#ifdef __cplusplus +extern "C" +{ +#else +#include <stdbool.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + + /** @brief This struct captures IoTHub transport configuration. */ + struct IOTHUBTRANSPORT_CONFIG_TAG + { + const IOTHUB_CLIENT_CONFIG* upperConfig; + PDLIST_ENTRY waitingToSend; + IOTHUB_AUTHORIZATION_HANDLE auth_module_handle; + const char* moduleId; + }; + + typedef void(*IOTHUB_CLIENT_MULTIPLEXED_DO_WORK)(void* iotHubClientInstance); + + MOCKABLE_FUNCTION(, LOCK_HANDLE, IoTHubTransport_GetLock, TRANSPORT_HANDLE, transportHandle); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_StartWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_CORE_HANDLE, clientHandle, IOTHUB_CLIENT_MULTIPLEXED_DO_WORK, muxDoWork); + MOCKABLE_FUNCTION(, bool, IoTHubTransport_SignalEndWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_CORE_HANDLE, clientHandle); + MOCKABLE_FUNCTION(, void, IoTHubTransport_JoinWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_CORE_HANDLE, clientHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_TRANSPORT_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_cbs_auth.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_AMQP_CBS_AUTH_H +#define IOTHUBTRANSPORT_AMQP_CBS_AUTH_H + +#include <stdint.h> +#include "internal/iothub_transport_ll_private.h" +#include "azure_uamqp_c/cbs.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" + +static const char* AUTHENTICATION_OPTION_SAVED_OPTIONS = "saved_authentication_options"; +static const char* AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS = "cbs_request_timeout_secs"; +static const char* AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS = "sas_token_refresh_time_secs"; +static const char* AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS = "sas_token_lifetime_secs"; + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum AUTHENTICATION_STATE_TAG + { + AUTHENTICATION_STATE_STOPPED, + AUTHENTICATION_STATE_STARTING, + AUTHENTICATION_STATE_STARTED, + AUTHENTICATION_STATE_ERROR + } AUTHENTICATION_STATE; + + typedef enum AUTHENTICATION_ERROR_TAG + { + AUTHENTICATION_ERROR_AUTH_TIMEOUT, + AUTHENTICATION_ERROR_AUTH_FAILED, + AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT, + AUTHENTICATION_ERROR_SAS_REFRESH_FAILED + } AUTHENTICATION_ERROR_CODE; + + typedef void(*ON_AUTHENTICATION_STATE_CHANGED_CALLBACK)(void* context, AUTHENTICATION_STATE previous_state, AUTHENTICATION_STATE new_state); + typedef void(*ON_AUTHENTICATION_ERROR_CALLBACK)(void* context, AUTHENTICATION_ERROR_CODE error_code); + + typedef struct AUTHENTICATION_CONFIG_TAG + { + const char* device_id; + const char* module_id; + char* iothub_host_fqdn; + + ON_AUTHENTICATION_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_callback_context; + + ON_AUTHENTICATION_ERROR_CALLBACK on_error_callback; + void* on_error_callback_context; + + IOTHUB_AUTHORIZATION_HANDLE authorization_module; // with either SAS Token, x509 Certs, and Device SAS Token + + } AUTHENTICATION_CONFIG; + + typedef struct AUTHENTICATION_INSTANCE* AUTHENTICATION_HANDLE; + + MOCKABLE_FUNCTION(, AUTHENTICATION_HANDLE, authentication_create, const AUTHENTICATION_CONFIG*, config); + MOCKABLE_FUNCTION(, int, authentication_start, AUTHENTICATION_HANDLE, authentication_handle, const CBS_HANDLE, cbs_handle); + MOCKABLE_FUNCTION(, int, authentication_stop, AUTHENTICATION_HANDLE, authentication_handle); + MOCKABLE_FUNCTION(, void, authentication_do_work, AUTHENTICATION_HANDLE, authentication_handle); + MOCKABLE_FUNCTION(, void, authentication_destroy, AUTHENTICATION_HANDLE, authentication_handle); + MOCKABLE_FUNCTION(, int, authentication_set_option, AUTHENTICATION_HANDLE, authentication_handle, const char*, name, void*, value); + MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, authentication_retrieve_options, AUTHENTICATION_HANDLE, authentication_handle); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORT_AMQP_CBS_AUTH_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_common.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_COMMON_H +#define IOTHUBTRANSPORTAMQP_COMMON_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "internal/iothub_transport_ll_private.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct AMQP_TRANSPORT_PROXY_OPTIONS_TAG +{ + const char* host_address; + int port; + const char* username; + const char* password; +} AMQP_TRANSPORT_PROXY_OPTIONS; + +typedef XIO_HANDLE(*AMQP_GET_IO_TRANSPORT)(const char* target_fqdn, const AMQP_TRANSPORT_PROXY_OPTIONS* amqp_transport_proxy_options); + +MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_AMQP_Common_Create, const IOTHUBTRANSPORT_CONFIG*, config, AMQP_GET_IO_TRANSPORT, get_io_transport); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Destroy, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_AMQP_Common_Subscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Unsubscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_AMQP_Common_DeviceMethod_Response, IOTHUB_DEVICE_HANDLE, handle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, status_response); +MOCKABLE_FUNCTION(, IOTHUB_PROCESS_ITEM_RESULT, IoTHubTransport_AMQP_Common_ProcessItem, TRANSPORT_LL_HANDLE, handle, IOTHUB_IDENTITY_TYPE, item_type, IOTHUB_IDENTITY_INFO*, iothub_item); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_DoWork, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_AMQP_Common_SetRetryPolicy, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_AMQP_Common_GetSendStatus, IOTHUB_DEVICE_HANDLE, handle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_AMQP_Common_SetOption, TRANSPORT_LL_HANDLE, handle, const char*, option, const void*, value); +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_HANDLE, IoTHubTransport_AMQP_Common_Register, TRANSPORT_LL_HANDLE, handle, const IOTHUB_DEVICE_CONFIG*, device, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, PDLIST_ENTRY, waitingToSend); +MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Unregister, IOTHUB_DEVICE_HANDLE, deviceHandle); +MOCKABLE_FUNCTION(, STRING_HANDLE, IoTHubTransport_AMQP_Common_GetHostname, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_AMQP_Common_SendMessageDisposition, MESSAGE_CALLBACK_INFO*, message_data, IOTHUBMESSAGE_DISPOSITION_RESULT, disposition); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUBTRANSPORTAMQP_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_connection.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H +#define IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/cbs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define AMQP_CONNECTION_STATE_VALUES \ + AMQP_CONNECTION_STATE_OPENED, \ + AMQP_CONNECTION_STATE_CLOSED, \ + AMQP_CONNECTION_STATE_ERROR + +DEFINE_ENUM(AMQP_CONNECTION_STATE, AMQP_CONNECTION_STATE_VALUES); + +typedef void(*ON_AMQP_CONNECTION_STATE_CHANGED)(const void* context, AMQP_CONNECTION_STATE old_state, AMQP_CONNECTION_STATE new_state); + +typedef struct AMQP_CONNECTION_CONFIG_TAG +{ + const char* iothub_host_fqdn; + XIO_HANDLE underlying_io_transport; + bool create_sasl_io; + bool create_cbs_connection; + bool is_trace_on; + + ON_AMQP_CONNECTION_STATE_CHANGED on_state_changed_callback; + const void* on_state_changed_context; + size_t svc2cl_keep_alive_timeout_secs; + double cl2svc_keep_alive_send_ratio; +} AMQP_CONNECTION_CONFIG; + +typedef struct AMQP_CONNECTION_INSTANCE* AMQP_CONNECTION_HANDLE; + +MOCKABLE_FUNCTION(, AMQP_CONNECTION_HANDLE, amqp_connection_create, AMQP_CONNECTION_CONFIG*, config); +MOCKABLE_FUNCTION(, void, amqp_connection_destroy, AMQP_CONNECTION_HANDLE, conn_handle); +MOCKABLE_FUNCTION(, void, amqp_connection_do_work, AMQP_CONNECTION_HANDLE, conn_handle); +MOCKABLE_FUNCTION(, int, amqp_connection_get_session_handle, AMQP_CONNECTION_HANDLE, conn_handle, SESSION_HANDLE*, session_handle); +MOCKABLE_FUNCTION(, int, amqp_connection_get_cbs_handle, AMQP_CONNECTION_HANDLE, conn_handle, CBS_HANDLE*, cbs_handle); +MOCKABLE_FUNCTION(, int, amqp_connection_set_logging, AMQP_CONNECTION_HANDLE, conn_handle, bool, is_trace_on); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_device.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H +#define IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/cbs.h" +#include "iothub_message.h" +#include "iothub_client_private.h" +#include "iothubtransport_amqp_device.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +// @brief name of option to apply the instance obtained using device_retrieve_options +static const char* DEVICE_OPTION_SAVED_OPTIONS = "saved_device_options"; +static const char* DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS = "event_send_timeout_secs"; +static const char* DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS = "cbs_request_timeout_secs"; +static const char* DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS = "sas_token_refresh_time_secs"; +static const char* DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS = "sas_token_lifetime_secs"; + +#define DEVICE_STATE_VALUES \ + DEVICE_STATE_STOPPED, \ + DEVICE_STATE_STOPPING, \ + DEVICE_STATE_STARTING, \ + DEVICE_STATE_STARTED, \ + DEVICE_STATE_ERROR_AUTH, \ + DEVICE_STATE_ERROR_AUTH_TIMEOUT, \ + DEVICE_STATE_ERROR_MSG + +DEFINE_ENUM(DEVICE_STATE, DEVICE_STATE_VALUES); + +#define DEVICE_AUTH_MODE_VALUES \ + DEVICE_AUTH_MODE_CBS, \ + DEVICE_AUTH_MODE_X509 + +DEFINE_ENUM(DEVICE_AUTH_MODE, DEVICE_AUTH_MODE_VALUES); + +#define DEVICE_SEND_STATUS_VALUES \ + DEVICE_SEND_STATUS_IDLE, \ + DEVICE_SEND_STATUS_BUSY + +DEFINE_ENUM(DEVICE_SEND_STATUS, DEVICE_SEND_STATUS_VALUES); + +#define D2C_EVENT_SEND_RESULT_VALUES \ + D2C_EVENT_SEND_COMPLETE_RESULT_OK, \ + D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, \ + D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, \ + D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, \ + D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED, \ + D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN + +DEFINE_ENUM(D2C_EVENT_SEND_RESULT, D2C_EVENT_SEND_RESULT_VALUES); + +#define DEVICE_MESSAGE_DISPOSITION_RESULT_VALUES \ + DEVICE_MESSAGE_DISPOSITION_RESULT_NONE, \ + DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED, \ + DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED, \ + DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED + +DEFINE_ENUM(DEVICE_MESSAGE_DISPOSITION_RESULT, DEVICE_MESSAGE_DISPOSITION_RESULT_VALUES); + +#define DEVICE_TWIN_UPDATE_RESULT_STRINGS \ + DEVICE_TWIN_UPDATE_RESULT_OK, \ + DEVICE_TWIN_UPDATE_RESULT_ERROR + +DEFINE_ENUM(DEVICE_TWIN_UPDATE_RESULT, DEVICE_TWIN_UPDATE_RESULT_STRINGS); + +#define DEVICE_TWIN_UPDATE_TYPE_STRINGS \ + DEVICE_TWIN_UPDATE_TYPE_PARTIAL, \ + DEVICE_TWIN_UPDATE_TYPE_COMPLETE + +DEFINE_ENUM(DEVICE_TWIN_UPDATE_TYPE, DEVICE_TWIN_UPDATE_TYPE_STRINGS) + +typedef struct DEVICE_MESSAGE_DISPOSITION_INFO_TAG +{ + unsigned long message_id; + char* source; +} DEVICE_MESSAGE_DISPOSITION_INFO; + +typedef void(*ON_DEVICE_STATE_CHANGED)(void* context, DEVICE_STATE previous_state, DEVICE_STATE new_state); +typedef DEVICE_MESSAGE_DISPOSITION_RESULT(*ON_DEVICE_C2D_MESSAGE_RECEIVED)(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, void* context); +typedef void(*ON_DEVICE_D2C_EVENT_SEND_COMPLETE)(IOTHUB_MESSAGE_LIST* message, D2C_EVENT_SEND_RESULT result, void* context); +typedef void(*DEVICE_SEND_TWIN_UPDATE_COMPLETE_CALLBACK)(DEVICE_TWIN_UPDATE_RESULT result, int status_code, void* context); +typedef void(*DEVICE_TWIN_UPDATE_RECEIVED_CALLBACK)(DEVICE_TWIN_UPDATE_TYPE update_type, const unsigned char* message, size_t length, void* context); + +typedef struct DEVICE_CONFIG_TAG +{ + const char* device_id; + const char* module_id; + char* product_info; + char* iothub_host_fqdn; + DEVICE_AUTH_MODE authentication_mode; + ON_DEVICE_STATE_CHANGED on_state_changed_callback; + void* on_state_changed_context; + + // Auth module used to generating handle authorization + // with either SAS Token, x509 Certs, and Device SAS Token + IOTHUB_AUTHORIZATION_HANDLE authorization_module; +} DEVICE_CONFIG; + +typedef struct AMQP_DEVICE_INSTANCE* AMQP_DEVICE_HANDLE; + +MOCKABLE_FUNCTION(, AMQP_DEVICE_HANDLE, device_create, DEVICE_CONFIG*, config); +MOCKABLE_FUNCTION(, void, device_destroy, AMQP_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, device_start_async, AMQP_DEVICE_HANDLE, handle, SESSION_HANDLE, session_handle, CBS_HANDLE, cbs_handle); +MOCKABLE_FUNCTION(, int, device_stop, AMQP_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, device_do_work, AMQP_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, device_send_event_async, AMQP_DEVICE_HANDLE, handle, IOTHUB_MESSAGE_LIST*, message, ON_DEVICE_D2C_EVENT_SEND_COMPLETE, on_device_d2c_event_send_complete_callback, void*, context); +MOCKABLE_FUNCTION(, int, device_send_twin_update_async, AMQP_DEVICE_HANDLE, handle, CONSTBUFFER_HANDLE, data, DEVICE_SEND_TWIN_UPDATE_COMPLETE_CALLBACK, on_send_twin_update_complete_callback, void*, context); +MOCKABLE_FUNCTION(, int, device_subscribe_for_twin_updates, AMQP_DEVICE_HANDLE, handle, DEVICE_TWIN_UPDATE_RECEIVED_CALLBACK, on_device_twin_update_received_callback, void*, context); +MOCKABLE_FUNCTION(, int, device_unsubscribe_for_twin_updates, AMQP_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, device_get_send_status, AMQP_DEVICE_HANDLE, handle, DEVICE_SEND_STATUS*, send_status); +MOCKABLE_FUNCTION(, int, device_subscribe_message, AMQP_DEVICE_HANDLE, handle, ON_DEVICE_C2D_MESSAGE_RECEIVED, on_message_received_callback, void*, context); +MOCKABLE_FUNCTION(, int, device_unsubscribe_message, AMQP_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, device_send_message_disposition, AMQP_DEVICE_HANDLE, AMQP_DEVICE_HANDLE, DEVICE_MESSAGE_DISPOSITION_INFO*, disposition_info, DEVICE_MESSAGE_DISPOSITION_RESULT, disposition_result); +MOCKABLE_FUNCTION(, int, device_set_retry_policy, AMQP_DEVICE_HANDLE, handle, IOTHUB_CLIENT_RETRY_POLICY, policy, size_t, retry_timeout_limit_in_seconds); +MOCKABLE_FUNCTION(, int, device_set_option, AMQP_DEVICE_HANDLE, handle, const char*, name, void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, device_retrieve_options, AMQP_DEVICE_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_messenger.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_AMQP_MESSENGER +#define IOTHUBTRANSPORT_AMQP_MESSENGER + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/map.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/amqp_definitions_sequence_no.h" +#include "azure_uamqp_c/amqp_definitions_delivery_number.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +static const char* AMQP_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS = "amqp_event_send_timeout_secs"; + +typedef struct AMQP_MESSENGER_INSTANCE* AMQP_MESSENGER_HANDLE; + +#define AMQP_MESSENGER_SEND_STATUS_VALUES \ + AMQP_MESSENGER_SEND_STATUS_IDLE, \ + AMQP_MESSENGER_SEND_STATUS_BUSY + +DEFINE_ENUM(AMQP_MESSENGER_SEND_STATUS, AMQP_MESSENGER_SEND_STATUS_VALUES); + +#define AMQP_MESSENGER_SEND_RESULT_VALUES \ + AMQP_MESSENGER_SEND_RESULT_SUCCESS, \ + AMQP_MESSENGER_SEND_RESULT_ERROR, \ + AMQP_MESSENGER_SEND_RESULT_CANCELLED + +DEFINE_ENUM(AMQP_MESSENGER_SEND_RESULT, AMQP_MESSENGER_SEND_RESULT_VALUES); + +#define AMQP_MESSENGER_REASON_VALUES \ + AMQP_MESSENGER_REASON_NONE, \ + AMQP_MESSENGER_REASON_CANNOT_PARSE, \ + AMQP_MESSENGER_REASON_FAIL_SENDING, \ + AMQP_MESSENGER_REASON_TIMEOUT, \ + AMQP_MESSENGER_REASON_MESSENGER_DESTROYED + +DEFINE_ENUM(AMQP_MESSENGER_REASON, AMQP_MESSENGER_REASON_VALUES); + +#define AMQP_MESSENGER_DISPOSITION_RESULT_VALUES \ + AMQP_MESSENGER_DISPOSITION_RESULT_NONE, \ + AMQP_MESSENGER_DISPOSITION_RESULT_ACCEPTED, \ + AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED, \ + AMQP_MESSENGER_DISPOSITION_RESULT_RELEASED + +DEFINE_ENUM(AMQP_MESSENGER_DISPOSITION_RESULT, AMQP_MESSENGER_DISPOSITION_RESULT_VALUES); + +#define AMQP_MESSENGER_STATE_VALUES \ + AMQP_MESSENGER_STATE_STARTING, \ + AMQP_MESSENGER_STATE_STARTED, \ + AMQP_MESSENGER_STATE_STOPPING, \ + AMQP_MESSENGER_STATE_STOPPED, \ + AMQP_MESSENGER_STATE_ERROR + +DEFINE_ENUM(AMQP_MESSENGER_STATE, AMQP_MESSENGER_STATE_VALUES); + +typedef struct AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO_TAG +{ + delivery_number message_id; + char* source; +} AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO; + +typedef void(*AMQP_MESSENGER_SEND_COMPLETE_CALLBACK)(AMQP_MESSENGER_SEND_RESULT result, AMQP_MESSENGER_REASON reason, void* context); +typedef void(*AMQP_MESSENGER_STATE_CHANGED_CALLBACK)(void* context, AMQP_MESSENGER_STATE previous_state, AMQP_MESSENGER_STATE new_state); +typedef void(*AMQP_MESSENGER_SUBSCRIPTION_CALLBACK)(void* context, bool is_subscribed); +typedef AMQP_MESSENGER_DISPOSITION_RESULT(*ON_AMQP_MESSENGER_MESSAGE_RECEIVED)(MESSAGE_HANDLE message, AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context); + +typedef struct AMQP_MESSENGER_LINK_CONFIG_TAG +{ + /** + * @brief Sample format: "/messages/devicebound" + */ + char* source_suffix; + + /** + * @brief Sample format: "/messages/events" + */ + char* target_suffix; + + receiver_settle_mode rcv_settle_mode; + sender_settle_mode snd_settle_mode; + + MAP_HANDLE attach_properties; +} AMQP_MESSENGER_LINK_CONFIG; + +typedef struct AMQP_MESSENGER_CONFIG_TAG +{ + char* client_version; + char* device_id; + char* module_id; + char* iothub_host_fqdn; + + AMQP_MESSENGER_LINK_CONFIG send_link; + AMQP_MESSENGER_LINK_CONFIG receive_link; + + AMQP_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_context; + + AMQP_MESSENGER_SUBSCRIPTION_CALLBACK on_subscription_changed_callback; + void* on_subscription_changed_context; +} AMQP_MESSENGER_CONFIG; + +MOCKABLE_FUNCTION(, AMQP_MESSENGER_HANDLE, amqp_messenger_create, const AMQP_MESSENGER_CONFIG*, messenger_config); +MOCKABLE_FUNCTION(, int, amqp_messenger_send_async, AMQP_MESSENGER_HANDLE, messenger_handle, MESSAGE_HANDLE, message, AMQP_MESSENGER_SEND_COMPLETE_CALLBACK, on_messenger_event_send_complete_callback, void*, context); +MOCKABLE_FUNCTION(, int, amqp_messenger_subscribe_for_messages, AMQP_MESSENGER_HANDLE, messenger_handle, ON_AMQP_MESSENGER_MESSAGE_RECEIVED, on_message_received_callback, void*, context); +MOCKABLE_FUNCTION(, int, amqp_messenger_unsubscribe_for_messages, AMQP_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, int, amqp_messenger_send_message_disposition, AMQP_MESSENGER_HANDLE, messenger_handle, AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO*, disposition_info, AMQP_MESSENGER_DISPOSITION_RESULT, disposition_result); +MOCKABLE_FUNCTION(, int, amqp_messenger_get_send_status, AMQP_MESSENGER_HANDLE, messenger_handle, AMQP_MESSENGER_SEND_STATUS*, send_status); +MOCKABLE_FUNCTION(, int, amqp_messenger_start, AMQP_MESSENGER_HANDLE, messenger_handle, SESSION_HANDLE, session_handle); +MOCKABLE_FUNCTION(, int, amqp_messenger_stop, AMQP_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, void, amqp_messenger_do_work, AMQP_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, void, amqp_messenger_destroy, AMQP_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, int, amqp_messenger_set_option, AMQP_MESSENGER_HANDLE, messenger_handle, const char*, name, void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, amqp_messenger_retrieve_options, AMQP_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, void, amqp_messenger_destroy_disposition_info, AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO*, disposition_info); + + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORT_AMQP_AMQP_MESSENGER*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_telemetry_messenger.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_AMQP_TELEMETRY_MESSENGER +#define IOTHUBTRANSPORT_AMQP_TELEMETRY_MESSENGER + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_uamqp_c/session.h" + +#include "azure_uamqp_c/amqp_definitions_sequence_no.h" +#include "azure_uamqp_c/amqp_definitions_delivery_number.h" + +#include "iothub_client_private.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +static const char* TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS = "telemetry_event_send_timeout_secs"; +static const char* TELEMETRY_MESSENGER_OPTION_SAVED_OPTIONS = "saved_telemetry_messenger_options"; + +typedef struct TELEMETRY_MESSENGER_INSTANCE* TELEMETRY_MESSENGER_HANDLE; + +typedef enum TELEMETRY_MESSENGER_SEND_STATUS_TAG +{ + TELEMETRY_MESSENGER_SEND_STATUS_IDLE, + TELEMETRY_MESSENGER_SEND_STATUS_BUSY +} TELEMETRY_MESSENGER_SEND_STATUS; + +typedef enum TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_TAG +{ + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK, + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED +} TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT; + +typedef enum TELEMETRY_MESSENGER_DISPOSITION_RESULT_TAG +{ + TELEMETRY_MESSENGER_DISPOSITION_RESULT_NONE, + TELEMETRY_MESSENGER_DISPOSITION_RESULT_ACCEPTED, + TELEMETRY_MESSENGER_DISPOSITION_RESULT_REJECTED, + TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED +} TELEMETRY_MESSENGER_DISPOSITION_RESULT; + +typedef enum TELEMETRY_MESSENGER_STATE_TAG +{ + TELEMETRY_MESSENGER_STATE_STARTING, + TELEMETRY_MESSENGER_STATE_STARTED, + TELEMETRY_MESSENGER_STATE_STOPPING, + TELEMETRY_MESSENGER_STATE_STOPPED, + TELEMETRY_MESSENGER_STATE_ERROR +} TELEMETRY_MESSENGER_STATE; + +typedef struct TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO_TAG +{ + delivery_number message_id; + char* source; +} TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO; + +typedef void(*ON_TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE)(IOTHUB_MESSAGE_LIST* iothub_message_list, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_event_send_complete_result, void* context); +typedef void(*ON_TELEMETRY_MESSENGER_STATE_CHANGED_CALLBACK)(void* context, TELEMETRY_MESSENGER_STATE previous_state, TELEMETRY_MESSENGER_STATE new_state); +typedef TELEMETRY_MESSENGER_DISPOSITION_RESULT(*ON_TELEMETRY_MESSENGER_MESSAGE_RECEIVED)(IOTHUB_MESSAGE_HANDLE message, TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context); + +typedef struct TELEMETRY_MESSENGER_CONFIG_TAG +{ + const char* device_id; + const char* module_id; + char* iothub_host_fqdn; + ON_TELEMETRY_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_context; +} TELEMETRY_MESSENGER_CONFIG; + +#define AMQP_BATCHING_RESERVE_SIZE (1024) + +MOCKABLE_FUNCTION(, TELEMETRY_MESSENGER_HANDLE, telemetry_messenger_create, const TELEMETRY_MESSENGER_CONFIG*, messenger_config, const char*, product_info); +MOCKABLE_FUNCTION(, int, telemetry_messenger_send_async, TELEMETRY_MESSENGER_HANDLE, messenger_handle, IOTHUB_MESSAGE_LIST*, message, ON_TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE, on_messenger_event_send_complete_callback, void*, context); +MOCKABLE_FUNCTION(, int, telemetry_messenger_subscribe_for_messages, TELEMETRY_MESSENGER_HANDLE, messenger_handle, ON_TELEMETRY_MESSENGER_MESSAGE_RECEIVED, on_message_received_callback, void*, context); +MOCKABLE_FUNCTION(, int, telemetry_messenger_unsubscribe_for_messages, TELEMETRY_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, int, telemetry_messenger_send_message_disposition, TELEMETRY_MESSENGER_HANDLE, messenger_handle, TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO*, disposition_info, TELEMETRY_MESSENGER_DISPOSITION_RESULT, disposition_result); +MOCKABLE_FUNCTION(, int, telemetry_messenger_get_send_status, TELEMETRY_MESSENGER_HANDLE, messenger_handle, TELEMETRY_MESSENGER_SEND_STATUS*, send_status); +MOCKABLE_FUNCTION(, int, telemetry_messenger_start, TELEMETRY_MESSENGER_HANDLE, messenger_handle, SESSION_HANDLE, session_handle); +MOCKABLE_FUNCTION(, int, telemetry_messenger_stop, TELEMETRY_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, void, telemetry_messenger_do_work, TELEMETRY_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, void, telemetry_messenger_destroy, TELEMETRY_MESSENGER_HANDLE, messenger_handle); +MOCKABLE_FUNCTION(, int, telemetry_messenger_set_option, TELEMETRY_MESSENGER_HANDLE, messenger_handle, const char*, name, void*, value); +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, telemetry_messenger_retrieve_options, TELEMETRY_MESSENGER_HANDLE, messenger_handle); + + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORT_AMQP_TELEMETRY_MESSENGER*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_amqp_twin_messenger.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER +#define IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_uamqp_c/session.h" +#include "iothub_client_private.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct TWIN_MESSENGER_INSTANCE* TWIN_MESSENGER_HANDLE; + + #define TWIN_MESSENGER_SEND_STATUS_VALUES \ + TWIN_MESSENGER_SEND_STATUS_IDLE, \ + TWIN_MESSENGER_SEND_STATUS_BUSY + + DEFINE_ENUM(TWIN_MESSENGER_SEND_STATUS, TWIN_MESSENGER_SEND_STATUS_VALUES); + + #define TWIN_REPORT_STATE_RESULT_VALUES \ + TWIN_REPORT_STATE_RESULT_SUCCESS, \ + TWIN_REPORT_STATE_RESULT_ERROR, \ + TWIN_REPORT_STATE_RESULT_CANCELLED + + DEFINE_ENUM(TWIN_REPORT_STATE_RESULT, TWIN_REPORT_STATE_RESULT_VALUES); + + #define TWIN_REPORT_STATE_REASON_VALUES \ + TWIN_REPORT_STATE_REASON_NONE, \ + TWIN_REPORT_STATE_REASON_INTERNAL_ERROR, \ + TWIN_REPORT_STATE_REASON_FAIL_SENDING, \ + TWIN_REPORT_STATE_REASON_TIMEOUT, \ + TWIN_REPORT_STATE_REASON_INVALID_RESPONSE, \ + TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED + + DEFINE_ENUM(TWIN_REPORT_STATE_REASON, TWIN_REPORT_STATE_REASON_VALUES); + + #define TWIN_MESSENGER_STATE_VALUES \ + TWIN_MESSENGER_STATE_STARTING, \ + TWIN_MESSENGER_STATE_STARTED, \ + TWIN_MESSENGER_STATE_STOPPING, \ + TWIN_MESSENGER_STATE_STOPPED, \ + TWIN_MESSENGER_STATE_ERROR + + DEFINE_ENUM(TWIN_MESSENGER_STATE, TWIN_MESSENGER_STATE_VALUES); + + #define TWIN_UPDATE_TYPE_VALUES \ + TWIN_UPDATE_TYPE_PARTIAL, \ + TWIN_UPDATE_TYPE_COMPLETE + + DEFINE_ENUM(TWIN_UPDATE_TYPE, TWIN_UPDATE_TYPE_VALUES); + + typedef void(*TWIN_MESSENGER_STATE_CHANGED_CALLBACK)(void* context, TWIN_MESSENGER_STATE previous_state, TWIN_MESSENGER_STATE new_state); + typedef void(*TWIN_MESSENGER_REPORT_STATE_COMPLETE_CALLBACK)(TWIN_REPORT_STATE_RESULT result, TWIN_REPORT_STATE_REASON reason, int status_code, const void* context); + typedef void(*TWIN_STATE_UPDATE_CALLBACK)(TWIN_UPDATE_TYPE update_type, const char* payload, size_t size, const void* context); + + typedef struct TWIN_MESSENGER_CONFIG_TAG + { + const char* client_version; + const char* device_id; + const char* module_id; + char* iothub_host_fqdn; + TWIN_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_context; + } TWIN_MESSENGER_CONFIG; + + MOCKABLE_FUNCTION(, TWIN_MESSENGER_HANDLE, twin_messenger_create, const TWIN_MESSENGER_CONFIG*, messenger_config); + MOCKABLE_FUNCTION(, int, twin_messenger_report_state_async, TWIN_MESSENGER_HANDLE, twin_msgr_handle, CONSTBUFFER_HANDLE, data, TWIN_MESSENGER_REPORT_STATE_COMPLETE_CALLBACK, on_report_state_complete_callback, const void*, context); + MOCKABLE_FUNCTION(, int, twin_messenger_subscribe, TWIN_MESSENGER_HANDLE, twin_msgr_handle, TWIN_STATE_UPDATE_CALLBACK, on_twin_state_update_callback, void*, context); + MOCKABLE_FUNCTION(, int, twin_messenger_unsubscribe, TWIN_MESSENGER_HANDLE, twin_msgr_handle); + MOCKABLE_FUNCTION(, int, twin_messenger_get_send_status, TWIN_MESSENGER_HANDLE, twin_msgr_handle, TWIN_MESSENGER_SEND_STATUS*, send_status); + MOCKABLE_FUNCTION(, int, twin_messenger_start, TWIN_MESSENGER_HANDLE, twin_msgr_handle, SESSION_HANDLE, session_handle); + MOCKABLE_FUNCTION(, int, twin_messenger_stop, TWIN_MESSENGER_HANDLE, twin_msgr_handle); + MOCKABLE_FUNCTION(, void, twin_messenger_do_work, TWIN_MESSENGER_HANDLE, twin_msgr_handle); + MOCKABLE_FUNCTION(, void, twin_messenger_destroy, TWIN_MESSENGER_HANDLE, twin_msgr_handle); + MOCKABLE_FUNCTION(, int, twin_messenger_set_option, TWIN_MESSENGER_HANDLE, twin_msgr_handle, const char*, name, void*, value); + MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, twin_messenger_retrieve_options, TWIN_MESSENGER_HANDLE, twin_msgr_handle); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransport_mqtt_common.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_MQTT_COMMON_H +#define IOTHUBTRANSPORT_MQTT_COMMON_H + +#include "../iothub_client_core_common.h" +#include "internal/iothub_transport_ll_private.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_transport_ll_private.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct MQTT_TRANSPORT_PROXY_OPTIONS_TAG +{ + const char* host_address; + int port; + const char* username; + const char* password; +} MQTT_TRANSPORT_PROXY_OPTIONS; + +typedef XIO_HANDLE(*MQTT_GET_IO_TRANSPORT)(const char* fully_qualified_name, const MQTT_TRANSPORT_PROXY_OPTIONS* mqtt_transport_proxy_options); + +MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_MQTT_Common_Create, const IOTHUBTRANSPORT_CONFIG*, config, MQTT_GET_IO_TRANSPORT, get_io_transport); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Destroy, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_DeviceMethod_Response, IOTHUB_DEVICE_HANDLE, handle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, status_response); +MOCKABLE_FUNCTION(, IOTHUB_PROCESS_ITEM_RESULT, IoTHubTransport_MQTT_Common_ProcessItem, TRANSPORT_LL_HANDLE, handle, IOTHUB_IDENTITY_TYPE, item_type, IOTHUB_IDENTITY_INFO*, iothub_item); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_DoWork, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_GetSendStatus, IOTHUB_DEVICE_HANDLE, handle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_SetOption, TRANSPORT_LL_HANDLE, handle, const char*, option, const void*, value); +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_HANDLE, IoTHubTransport_MQTT_Common_Register, TRANSPORT_LL_HANDLE, handle, const IOTHUB_DEVICE_CONFIG*, device, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, PDLIST_ENTRY, waitingToSend); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unregister, IOTHUB_DEVICE_HANDLE, deviceHandle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_SetRetryPolicy, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); +MOCKABLE_FUNCTION(, STRING_HANDLE, IoTHubTransport_MQTT_Common_GetHostname, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_SendMessageDisposition, MESSAGE_CALLBACK_INFO*, message_data, IOTHUBMESSAGE_DISPOSITION_RESULT, disposition); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe_InputQueue, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue, IOTHUB_DEVICE_HANDLE, handle); + + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUBTRANSPORT_MQTT_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/iothubtransportamqp_methods.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_METHODS_H +#define IOTHUBTRANSPORTAMQP_METHODS_H + +#include "azure_uamqp_c/session.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> + +extern "C" +{ +#else +#include <stddef.h> +#endif + + typedef struct IOTHUBTRANSPORT_AMQP_METHOD_TAG* IOTHUBTRANSPORT_AMQP_METHOD_HANDLE; + typedef struct IOTHUBTRANSPORT_AMQP_METHODS_TAG* IOTHUBTRANSPORT_AMQP_METHODS_HANDLE; + typedef void(*ON_METHODS_ERROR)(void* context); + typedef int(*ON_METHOD_REQUEST_RECEIVED)(void* context, const char* method_name, const unsigned char* request, size_t request_size, IOTHUBTRANSPORT_AMQP_METHOD_HANDLE response); + typedef void(*ON_METHODS_UNSUBSCRIBED)(void* context); + + MOCKABLE_FUNCTION(, IOTHUBTRANSPORT_AMQP_METHODS_HANDLE, iothubtransportamqp_methods_create, const char*, hostname, const char*, device_id, const char*, module_id); + MOCKABLE_FUNCTION(, void, iothubtransportamqp_methods_destroy, IOTHUBTRANSPORT_AMQP_METHODS_HANDLE, iothubtransport_amqp_methods_handle); + MOCKABLE_FUNCTION(, int, iothubtransportamqp_methods_subscribe, IOTHUBTRANSPORT_AMQP_METHODS_HANDLE, iothubtransport_amqp_methods_handle, + SESSION_HANDLE, session_handle, ON_METHODS_ERROR, on_methods_error, void*, on_methods_error_context, + ON_METHOD_REQUEST_RECEIVED, on_method_request_received, void*, on_method_request_received_context, + ON_METHODS_UNSUBSCRIBED, on_methods_unsubscribed, void*, on_methods_unsubscribed_context); + MOCKABLE_FUNCTION(, int, iothubtransportamqp_methods_respond, IOTHUBTRANSPORT_AMQP_METHOD_HANDLE, method_handle, + const unsigned char*, response, size_t, response_size, int, status_code); + MOCKABLE_FUNCTION(, void, iothubtransportamqp_methods_unsubscribe, IOTHUBTRANSPORT_AMQP_METHODS_HANDLE, iothubtransport_amqp_methods_handle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUBTRANSPORTAMQP_METHODS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/message_queue.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file message_queue.h +* @brief A generic message queue. +*/ + +#ifndef MESSAGE_QUEUE_H +#define MESSAGE_QUEUE_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/optionhandler.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct MESSAGE_QUEUE_TAG* MESSAGE_QUEUE_HANDLE; +typedef void* MQ_MESSAGE_HANDLE; +typedef void* USER_DEFINED_REASON; + +#define MESSAGE_QUEUE_RESULT_STRINGS \ + MESSAGE_QUEUE_SUCCESS, \ + MESSAGE_QUEUE_ERROR, \ + MESSAGE_QUEUE_RETRYABLE_ERROR, \ + MESSAGE_QUEUE_TIMEOUT, \ + MESSAGE_QUEUE_CANCELLED + +DEFINE_ENUM(MESSAGE_QUEUE_RESULT, MESSAGE_QUEUE_RESULT_STRINGS); + +/** +* @brief User-provided callback invoked by MESSAGE_QUEUE back to the user when a messages completes being processed. +*/ +typedef void(*MESSAGE_PROCESSING_COMPLETED_CALLBACK)(MQ_MESSAGE_HANDLE message, MESSAGE_QUEUE_RESULT result, USER_DEFINED_REASON reason, void* user_context); + +/** +* @brief Callback that MUST be invoked by PROCESS_MESSAGE_CALLBACK (user provided) to signal to MESSAGE_QUEUE that a message has been processed. +* @remarks Besides causing MESSAGE_QUEUE to dequeue the message from its internal lists, causes MESSAGE_PROCESSING_COMPLETED_CALLBACK to be triggered. +*/ +typedef void(*PROCESS_MESSAGE_COMPLETED_CALLBACK)(MESSAGE_QUEUE_HANDLE message_queue, MQ_MESSAGE_HANDLE message, MESSAGE_QUEUE_RESULT result, USER_DEFINED_REASON reason); + +/** +* @brief User-provided callback invoked by MESSAGE_QUEUE when a messages is ready to be processed, getting internally moved from "pending" to "in-progress". +*/ +typedef void(*PROCESS_MESSAGE_CALLBACK)(MESSAGE_QUEUE_HANDLE message_queue, MQ_MESSAGE_HANDLE message, PROCESS_MESSAGE_COMPLETED_CALLBACK on_process_message_completed_callback, void* user_context); + +typedef struct MESSAGE_QUEUE_CONFIG_TAG +{ + /** + * @brief Function that actually process (a.k.a, e.g, sends) a message previously queued. + * + * @remarks When MESSAGE_QUEUE is summoned to invoke @c on_process_message_callback (upon call to message_queue_do_work, when a message is moved from the pending to in-progress list), + * it passes as arguments the MESSAGE_QUEUE handle and a callback function that MUST be invoked by @c on_process_message_callback once it completes. + * The @c user_context passed is the same provided as argument by the upper layer on @c message_queue_add. + */ + PROCESS_MESSAGE_CALLBACK on_process_message_callback; + size_t max_message_enqueued_time_secs; + size_t max_message_processing_time_secs; + size_t max_retry_count; +} MESSAGE_QUEUE_CONFIG; + +/** +* @brief Creates a new instance of MESSAGE_QUEUE. +* +* @param config Pointer to an @c MESSAGE_QUEUE_CONFIG structure +* +* @returns A non-NULL @c MESSAGE_QUEUE_HANDLE value that is used when invoking other API functions. +*/ +MOCKABLE_FUNCTION(, MESSAGE_QUEUE_HANDLE, message_queue_create, MESSAGE_QUEUE_CONFIG*, config); + +/** +* @brief Destroys an instance of MESSAGE_QUEUE, releasing all memory it allocated. +* +* @remarks All messages still pending to be processed and currently in-progress get bubbled up back to the upper-layer +* through the @c on_message_processing_completed_callback (passed on the @c MESSAGE_QUEUE_CONFIG instance) +* with the @c result set as @c MESSAGE_QUEUE_CANCELLED and @c reason set to @c NULL. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +*/ +MOCKABLE_FUNCTION(, void, message_queue_destroy, MESSAGE_QUEUE_HANDLE, message_queue); + +/** +* @brief Adds a new generic message to MESSAGE_QUEUE's pending list. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @param message A generic message to be queued and then processed (i.e., sent, consolidated, etc). +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_add, MESSAGE_QUEUE_HANDLE, message_queue, MQ_MESSAGE_HANDLE, message, MESSAGE_PROCESSING_COMPLETED_CALLBACK, on_message_processing_completed_callback, void*, user_context); + +/** +* @brief Causes all messages in-progress to be moved back to the beginning of the pending list. +* +* @remarks If on_message_process_completed_callback is invoked for any of message not in in-progress, it is disregarded. +* Messages are queued back into the pending list in a way they will be sent first when message_queue_do_work is invoked again. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_move_all_back_to_pending, MESSAGE_QUEUE_HANDLE, message_queue); + +/** +* @brief Causes all messages pending to be sent and in-progress to be flushed back to the user through @c on_message_processing_completed_callback. +* +* @remarks @c on_message_processing_completed_callback gets invoked with @c result set as MESSAGE_QUEUE_CANCELLED and @c reason set to NULL. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +*/ +MOCKABLE_FUNCTION(, void, message_queue_remove_all, MESSAGE_QUEUE_HANDLE, message_queue); + +/** +* @brief Informs if there are messages pending to be sent and/or currently in-progress. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @param @c is_empty Set to @c true if there are any messages in pending to be sent and/or currently in-progress, @c false otherwise. +* +* @remarks The parameter @c is_empty is only set if no errors occur (like passing a NULL @c message_queue). +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_is_empty, MESSAGE_QUEUE_HANDLE, message_queue, bool*, is_empty); + +/** +* @brief Causes MESSAGE_QUEUE to go through its list of pending messages and move them to in-progress, as well as trigering retry and timeout controls. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +*/ +MOCKABLE_FUNCTION(, void, message_queue_do_work, MESSAGE_QUEUE_HANDLE, message_queue); + +/** +* @brief Sets the maximum time, in seconds, a message will be within MESSAGE_QUEUE (in either pending or in-progress lists). +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @param seconds Number of seconds to set for this timeout. A value of zero de-activates this timeout control. +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_set_max_message_enqueued_time_secs, MESSAGE_QUEUE_HANDLE, message_queue, size_t, seconds); + +/** +* @brief Sets the maximum time, in seconds, a message will be in-progress within MESSAGE_QUEUE. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @param seconds Number of seconds to set for this timeout. A value of zero de-activates this timeout control. +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_set_max_message_processing_time_secs, MESSAGE_QUEUE_HANDLE, message_queue, size_t, seconds); + +/** +* @brief Sets the maximum number of times MESSAGE_QUEUE will try to re-process a message (no counting the initial attempt). +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @param max_retry_count The number of times MESSAGE_QUEUE will try to re-process a message. +* +* @returns Zero if the no errors occur, non-zero otherwise. +*/ +MOCKABLE_FUNCTION(, int, message_queue_set_max_retry_count, MESSAGE_QUEUE_HANDLE, message_queue, size_t, max_retry_count); + +/** +* @brief Retrieves a blob with all the options currently set in the instance of MESSAGE_QUEUE. +* +* @param message_queue A @c MESSAGE_QUEUE_HANDLE obtained using message_queue_create. +* +* @returns A non-NULL @c OPTIONHANDLER_HANDLE if no errors occur, or NULL otherwise. +*/ +MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, message_queue_retrieve_options, MESSAGE_QUEUE_HANDLE, message_queue); + +#ifdef __cplusplus +} +#endif + +#endif /*MESSAGE_QUEUE_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/internal/uamqp_messaging.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UAMQP_MESSAGING_H +#define UAMQP_MESSAGING_H + +#include "iothub_message.h" +#include "azure_uamqp_c/message.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + MOCKABLE_FUNCTION(, int, message_create_IoTHubMessage_from_uamqp_message, MESSAGE_HANDLE, uamqp_message, IOTHUB_MESSAGE_HANDLE*, iothubclient_message); + MOCKABLE_FUNCTION(, int, message_create_uamqp_encoding_from_iothub_message, MESSAGE_HANDLE, message_batch_container, IOTHUB_MESSAGE_HANDLE, message_handle, BINARY_DATA*, body_binary_data); + +#ifdef __cplusplus +} +#endif + +#endif /*UAMQP_MESSAGING_H*/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_H +#define IOTHUB_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + /** + * @brief IoTHubClient_Init Initializes the IoTHub Client System. + * + * @return int zero upon success, any other value upon failure. + */ + MOCKABLE_FUNCTION(, int, IoTHub_Init); + + /** + * @brief IoTHubClient_Deinit Frees resources initialized in the IoTHubClient_Init function call. + * + */ + MOCKABLE_FUNCTION(, void, IoTHub_Deinit); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,392 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client.h +* @brief Extends the IoTHubCLient_LL module with additional features. +* +* @details IoTHubClient is a module that extends the IoTHubCLient_LL +* module with 2 features: +* - scheduling the work for the IoTHubCLient from a +* thread, so that the user does not need to create their +* own thread +* - thread-safe APIs +*/ + +#ifndef IOTHUB_CLIENT_H +#define IOTHUB_CLIENT_H + +#include <stddef.h> +#include <stdint.h> + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core_ll.h" +#include "iothub_client_core.h" +#include "iothub_client_ll.h" + +#ifndef IOTHUB_CLIENT_INSTANCE_TYPE +typedef IOTHUB_CLIENT_CORE_HANDLE IOTHUB_CLIENT_HANDLE; +#define IOTHUB_CLIENT_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_INSTANCE + + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessSignature=SharedAccessSignature sr=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net]/devices/[Device ID goes here]&sig=[SAS Token goes here]&se=[Expiry Time goes here];</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API does not allow sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_Create, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param transportHandle TRANSPORT_HANDLE which represents a connection. + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API allows sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateWithTransport, TRANSPORT_HANDLE, transportHandle, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the device auth module. + * + * @param iothub_uri Pointer to an ioThub hostname received in the registration process + * @param device_id Pointer to the device Id of the device + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubClient_Destroy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the device for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubClient_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SendEventAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetSendStatus, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetMessageCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the device for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetConnectionStatusCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetRetryPolicy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetRetryPolicy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetLastMessageReceiveTime, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b messageTimeout - the maximum time in milliseconds until a message + * is timeouted. The time starts at IoTHubClient_SendEventAsync. By default, + * messages do not expire. @p is a pointer to a uint64_t + * - @b svc2cl_keep_alive_timeout_secs - the AMQP service side keep alive interval in seconds. + * After the connection established the client requests the server to set the + * keep alive interval for given time. + * If it is not set then the default 240 sec applies. + * If it is set to zero the server will not send keep alive messages to the client. + * - @b cl2svc_keep_alive_send_ratio - the AMQP client side keep alive interval in seconds. + * After the connection established the server requests the client to set the + * keep alive interval for given time. + * If it is not set then the default ratio of 1/2 is applied. + * The ratio has to be greater than 0.0 and equal to or less than 0.9 + + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetOption, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the device receives a state update. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceTwinCallback The callback specified by the device client to be used for updating + * the desired state. The callback will be called in response to a + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceTwinCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + + /** + * @brief This API sends a report of the device's properties and their current values. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param reportedState The current device property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the device client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SendReportedState, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceMethodCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for async cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param inboundDeviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API responses to a asnyc method callback identified the methodId. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param methodId The methodId of the Device Method callback. + * @param response The response data for the method callback. + * @param response_size The size of the response data buffer. + * @param status_response The status response of the method callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_DeviceMethodResponse, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, statusCode); + +#ifndef DONT_USE_UPLOADTOBLOB + /** + * @brief IoTHubClient_UploadToBlobAsync uploads data from memory to a file in Azure Blob Storage. + * + * @param iotHubClientHandle The handle created by a call to the IoTHubClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param source The source of data. + * @param size The size of data. + * @param iotHubClientFileUploadCallback A callback to be invoked when the file upload operation has finished. + * @param context A user-provided context to be passed to the file upload callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadToBlobAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK, iotHubClientFileUploadCallback, void*, context); + + /** + ** DEPRECATED: Use IoTHubClient_UploadMultipleBlocksToBlobAsyncEx instead ** + * @brief Uploads a file to a Blob storage in chunks, fed through the callback function provided by the user. + * @remarks This function allows users to upload large files in chunks, not requiring the whole file content to be passed in memory. + * @param iotHubClientHandle The handle created by a call to the IoTHubClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param getDataCallback A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call. + ** DEPRECATED: Use IoTHubClient_UploadMultipleBlocksToBlobAsyncEx instead ** + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadMultipleBlocksToBlobAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + + /** + * @brief Uploads a file to a Blob storage in chunks, fed through the callback function provided by the user. + * @remarks This function allows users to upload large files in chunks, not requiring the whole file content to be passed in memory. + * @param iotHubClientHandle The handle created by a call to the IoTHubClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call.*/ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadMultipleBlocksToBlobAsyncEx, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + +#endif /* DONT_USE_UPLOADTOBLOB */ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_authorization.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_CLIENT_AUTHORIZATION_H +#define IOTHUB_CLIENT_AUTHORIZATION_H + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "../../c-utility/inc/azure_c_shared_utility/macro_utils.h" +#include "../../c-utility/inc/azure_c_shared_utility/umock_c_prod.h" +#include "../../c-utility/inc/azure_c_shared_utility/xio.h" + +typedef struct IOTHUB_AUTHORIZATION_DATA_TAG* IOTHUB_AUTHORIZATION_HANDLE; + +#define IOTHUB_CREDENTIAL_TYPE_VALUES \ + IOTHUB_CREDENTIAL_TYPE_UNKNOWN, \ + IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY, \ + IOTHUB_CREDENTIAL_TYPE_X509, \ + IOTHUB_CREDENTIAL_TYPE_X509_ECC, \ + IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN, \ + IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH + + +DEFINE_ENUM(IOTHUB_CREDENTIAL_TYPE, IOTHUB_CREDENTIAL_TYPE_VALUES); + +#define SAS_TOKEN_STATUS_VALUES \ + SAS_TOKEN_STATUS_FAILED, \ + SAS_TOKEN_STATUS_VALID, \ + SAS_TOKEN_STATUS_INVALID + +DEFINE_ENUM(SAS_TOKEN_STATUS, SAS_TOKEN_STATUS_VALUES); + +MOCKABLE_FUNCTION(, IOTHUB_AUTHORIZATION_HANDLE, IoTHubClient_Auth_Create, const char*, device_key, const char*, device_id, const char*, device_sas_token); +MOCKABLE_FUNCTION(, IOTHUB_AUTHORIZATION_HANDLE, IoTHubClient_Auth_CreateFromDeviceAuth, const char*, device_id); +MOCKABLE_FUNCTION(, void, IoTHubClient_Auth_Destroy, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Set_x509_Type, IOTHUB_AUTHORIZATION_HANDLE, handle, bool, enable_x509); +MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Get_Credential_Type, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, char*, IoTHubClient_Auth_Get_SasToken, IOTHUB_AUTHORIZATION_HANDLE, handle, const char*, scope, size_t, expiry_time_relative_seconds); +MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Set_xio_Certificate, IOTHUB_AUTHORIZATION_HANDLE, handle, XIO_HANDLE, xio); +MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_DeviceId, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_DeviceKey, IOTHUB_AUTHORIZATION_HANDLE, handle); +MOCKABLE_FUNCTION(, SAS_TOKEN_STATUS, IoTHubClient_Auth_Is_SasToken_Valid, IOTHUB_AUTHORIZATION_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_AUTHORIZATION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_core.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_core.h +* @brief Extends the IoTHubCLient_LL module with additional features. +* +* @details IoTHubClient is a module that extends the IoTHubCLient_LL +* module with 2 features: +* - scheduling the work for the IoTHubCLient from a +* thread, so that the user does not need to create their +* own thread +* - thread-safe APIs +*/ + +#ifndef IOTHUB_CLIENT_CORE_H +#define IOTHUB_CLIENT_CORE_H + +#include <stddef.h> +#include <stdint.h> +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_client_core_ll.h" + +#ifndef IOTHUB_CLIENT_CORE_INSTANCE_TYPE +typedef struct IOTHUB_CLIENT_CORE_INSTANCE_TAG* IOTHUB_CLIENT_CORE_HANDLE; +#define IOTHUB_CLIENT_CORE_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_CORE_INSTANCE + +#ifdef __cplusplus +extern "C" +{ +#endif + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_HANDLE, IoTHubClientCore_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_HANDLE, IoTHubClientCore_Create, const IOTHUB_CLIENT_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_HANDLE, IoTHubClientCore_CreateWithTransport, TRANSPORT_HANDLE, transportHandle, const IOTHUB_CLIENT_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_HANDLE, IoTHubClientCore_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + MOCKABLE_FUNCTION(, void, IoTHubClientCore_Destroy, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SendEventAsync, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_GetSendStatus, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetMessageCallback, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetConnectionStatusCallback, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetRetryPolicy, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_GetRetryPolicy, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_GetLastMessageReceiveTime, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetOption, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetDeviceTwinCallback, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SendReportedState, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetDeviceMethodCallback, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_DeviceMethodResponse, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, statusCode); + +#ifndef DONT_USE_UPLOADTOBLOB + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_UploadToBlobAsync, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK, iotHubClientFileUploadCallback, void*, context); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_UploadMultipleBlocksToBlobAsync, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); +#endif /* DONT_USE_UPLOADTOBLOB */ + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SendEventToOutputAsync, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, const char*, outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_SetInputMessageCallback, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const char*, inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, eventHandlerCallback, void*, userContextCallback); + +#ifdef USE_EDGE_MODULES + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_HANDLE, IoTHubClientCore_CreateFromEnvironment, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_GenericMethodInvoke, IOTHUB_CLIENT_CORE_HANDLE, iotHubClientHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, IOTHUB_METHOD_INVOKE_CALLBACK, methodInvokeCallback, void*, context); +#endif /* USE_EDGE_MODULES */ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_CORE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_core_common.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* Shared structures and enums for iothub convenience layer and LL layer */ + +#ifndef IOTHUB_CLIENT_CORE_COMMON_H +#define IOTHUB_CLIENT_CORE_COMMON_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "../../c-utility/inc/azure_c_shared_utility/doublylinkedlist.h" + +#include "iothub_transport_ll.h" +#include "iothub_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES \ + FILE_UPLOAD_OK, \ + FILE_UPLOAD_ERROR + + DEFINE_ENUM(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES) + typedef void(*IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK)(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, void* userContextCallback); + +#define IOTHUB_CLIENT_RESULT_VALUES \ + IOTHUB_CLIENT_OK, \ + IOTHUB_CLIENT_INVALID_ARG, \ + IOTHUB_CLIENT_ERROR, \ + IOTHUB_CLIENT_INVALID_SIZE, \ + IOTHUB_CLIENT_INDEFINITE_TIME + + /** @brief Enumeration specifying the status of calls to various APIs in this module. + */ + + DEFINE_ENUM(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES); + + typedef void(*IOTHUB_METHOD_INVOKE_CALLBACK)(IOTHUB_CLIENT_RESULT result, int responseStatus, unsigned char* responsePayload, size_t responsePayloadSize, void* context); + +#define IOTHUB_CLIENT_RETRY_POLICY_VALUES \ + IOTHUB_CLIENT_RETRY_NONE, \ + IOTHUB_CLIENT_RETRY_IMMEDIATE, \ + IOTHUB_CLIENT_RETRY_INTERVAL, \ + IOTHUB_CLIENT_RETRY_LINEAR_BACKOFF, \ + IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF, \ + IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, \ + IOTHUB_CLIENT_RETRY_RANDOM + + /** @brief Enumeration passed in by the IoT Hub when the event confirmation + * callback is invoked to indicate status of the event processing in + * the hub. + */ + DEFINE_ENUM(IOTHUB_CLIENT_RETRY_POLICY, IOTHUB_CLIENT_RETRY_POLICY_VALUES); + + struct IOTHUBTRANSPORT_CONFIG_TAG; + typedef struct IOTHUBTRANSPORT_CONFIG_TAG IOTHUBTRANSPORT_CONFIG; + +#define IOTHUB_CLIENT_STATUS_VALUES \ + IOTHUB_CLIENT_SEND_STATUS_IDLE, \ + IOTHUB_CLIENT_SEND_STATUS_BUSY + + /** @brief Enumeration returned by the ::IoTHubClient_LL_GetSendStatus + * API to indicate the current sending status of the IoT Hub client. + */ + DEFINE_ENUM(IOTHUB_CLIENT_STATUS, IOTHUB_CLIENT_STATUS_VALUES); + +#define IOTHUB_IDENTITY_TYPE_VALUE \ + IOTHUB_TYPE_TELEMETRY, \ + IOTHUB_TYPE_DEVICE_TWIN, \ + IOTHUB_TYPE_DEVICE_METHODS, \ + IOTHUB_TYPE_EVENT_QUEUE + DEFINE_ENUM(IOTHUB_IDENTITY_TYPE, IOTHUB_IDENTITY_TYPE_VALUE); + +#define IOTHUB_PROCESS_ITEM_RESULT_VALUE \ + IOTHUB_PROCESS_OK, \ + IOTHUB_PROCESS_ERROR, \ + IOTHUB_PROCESS_NOT_CONNECTED, \ + IOTHUB_PROCESS_CONTINUE + DEFINE_ENUM(IOTHUB_PROCESS_ITEM_RESULT, IOTHUB_PROCESS_ITEM_RESULT_VALUE); + +#define IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES \ + IOTHUBMESSAGE_ACCEPTED, \ + IOTHUBMESSAGE_REJECTED, \ + IOTHUBMESSAGE_ABANDONED + + /** @brief Enumeration returned by the callback which is invoked whenever the + * IoT Hub sends a message to the device. + */ + DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES); + +#define IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES \ + IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_SUCCESS, \ + IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_ERROR \ + + /** @brief Enumeration returned by remotely executed functions + */ + DEFINE_ENUM(IOTHUB_CLIENT_IOTHUB_METHOD_STATUS, IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES); + + +#define IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES \ + IOTHUB_CLIENT_CONFIRMATION_OK, \ + IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY, \ + IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT, \ + IOTHUB_CLIENT_CONFIRMATION_ERROR \ + + /** @brief Enumeration passed in by the IoT Hub when the event confirmation + * callback is invoked to indicate status of the event processing in + * the hub. + */ + DEFINE_ENUM(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES); + +#define IOTHUB_CLIENT_CONNECTION_STATUS_VALUES \ + IOTHUB_CLIENT_CONNECTION_AUTHENTICATED, \ + IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED \ + + + /** @brief Enumeration passed in by the IoT Hub when the connection status + * callback is invoked to indicate status of the connection in + * the hub. + */ + DEFINE_ENUM(IOTHUB_CLIENT_CONNECTION_STATUS, IOTHUB_CLIENT_CONNECTION_STATUS_VALUES); + +#define IOTHUB_CLIENT_CONNECTION_STATUS_REASON_VALUES \ + IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN, \ + IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED, \ + IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL, \ + IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED, \ + IOTHUB_CLIENT_CONNECTION_NO_NETWORK, \ + IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR, \ + IOTHUB_CLIENT_CONNECTION_OK \ + + /** @brief Enumeration passed in by the IoT Hub when the connection status + * callback is invoked to indicate status of the connection in + * the hub. + */ + DEFINE_ENUM(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, IOTHUB_CLIENT_CONNECTION_STATUS_REASON_VALUES); + +#define TRANSPORT_TYPE_VALUES \ + TRANSPORT_LL, /*LL comes from "LowLevel" */ \ + TRANSPORT_THREADED + + DEFINE_ENUM(TRANSPORT_TYPE, TRANSPORT_TYPE_VALUES); + +#define DEVICE_TWIN_UPDATE_STATE_VALUES \ + DEVICE_TWIN_UPDATE_COMPLETE, \ + DEVICE_TWIN_UPDATE_PARTIAL + + DEFINE_ENUM(DEVICE_TWIN_UPDATE_STATE, DEVICE_TWIN_UPDATE_STATE_VALUES); + + typedef void(*IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK)(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback); + typedef void(*IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK)(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback); + typedef IOTHUBMESSAGE_DISPOSITION_RESULT (*IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC)(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback); + + typedef void(*IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK)(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback); + typedef void(*IOTHUB_CLIENT_REPORTED_STATE_CALLBACK)(int status_code, void* userContextCallback); + typedef int(*IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC)(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* response_size, void* userContextCallback); + typedef int(*IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK)(const char* method_name, const unsigned char* payload, size_t size, METHOD_HANDLE method_id, void* userContextCallback); + + +#define IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES \ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK, \ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT + + DEFINE_ENUM(IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES); + + /** + * @brief Callback invoked by IoTHubClient_UploadMultipleBlocksToBlobAsync requesting the chunks of data to be uploaded. + * @param result The result of the upload of the previous block of data provided by the user (IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX only) + * @param data Next block of data to be uploaded, to be provided by the user when this callback is invoked. + * @param size Size of the data parameter. + * @param context User context provided on the call to IoTHubClient_UploadMultipleBlocksToBlobAsync. + * @remarks If the user wants to abort the upload, the callback should return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT + * It should return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK otherwise. + * If a NULL is provided for parameter "data" and/or zero is provided for "size", the user indicates to the client that the complete file has been uploaded. + * In such case this callback will be invoked only once more to indicate the status of the final block upload. + * If result is not FILE_UPLOAD_OK, the download is cancelled and this callback stops being invoked. + * When this callback is called for the last time, no data or size is expected, so data and size are set to NULL + */ + typedef void(*IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK)(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context); + typedef IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT(*IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX)(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context); + + /** @brief This struct captures IoTHub client configuration. */ + typedef struct IOTHUB_CLIENT_CONFIG_TAG + { + /** @brief A function pointer that is passed into the @c IoTHubClientCreate. + * A function definition for AMQP is defined in the include @c iothubtransportamqp.h. + * A function definition for HTTP is defined in the include @c iothubtransporthttp.h + * A function definition for MQTT is defined in the include @c iothubtransportmqtt.h */ + IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol; + + /** @brief A string that identifies the device. */ + const char* deviceId; + + /** @brief The device key used to authenticate the device. + If neither deviceSasToken nor deviceKey is present then the authentication is assumed x509.*/ + const char* deviceKey; + + /** @brief The device SAS Token used to authenticate the device in place of device key. + If neither deviceSasToken nor deviceKey is present then the authentication is assumed x509.*/ + const char* deviceSasToken; + + /** @brief The IoT Hub name to which the device is connecting. */ + const char* iotHubName; + + /** @brief IoT Hub suffix goes here, e.g., private.azure-devices-int.net. */ + const char* iotHubSuffix; + + const char* protocolGatewayHostName; + } IOTHUB_CLIENT_CONFIG; + + /** @brief This struct captures IoTHub client device configuration. */ + typedef struct IOTHUB_CLIENT_DEVICE_CONFIG_TAG + { + /** @brief A function pointer that is passed into the @c IoTHubClientCreate. + * A function definition for AMQP is defined in the include @c iothubtransportamqp.h. + * A function definition for HTTP is defined in the include @c iothubtransporthttp.h + * A function definition for MQTT is defined in the include @c iothubtransportmqtt.h */ + IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol; + + /** @brief a transport handle implementing the protocol */ + void * transportHandle; + + /** @brief A string that identifies the device. */ + const char* deviceId; + + /** @brief The device key used to authenticate the device. + x509 authentication is is not supported for multiplexed connections*/ + const char* deviceKey; + + /** @brief The device SAS Token used to authenticate the device in place of device key. + x509 authentication is is not supported for multiplexed connections.*/ + const char* deviceSasToken; + } IOTHUB_CLIENT_DEVICE_CONFIG; + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_CORE_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_core_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a device) to communicate +* with an Azure IoTHub. +* +* @details IoTHubClient_LL is a module that allows a user (usually a +* device) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef IOTHUB_CLIENT_CORE_LL_H +#define IOTHUB_CLIENT_CORE_LL_H + +typedef struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA_TAG* IOTHUB_CLIENT_CORE_LL_HANDLE; + +#include <time.h> +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core_common.h" + +#include "iothub_client_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_LL_HANDLE, IoTHubClientCore_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_LL_HANDLE, IoTHubClientCore_LL_Create, const IOTHUB_CLIENT_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_LL_HANDLE, IoTHubClientCore_LL_CreateWithTransport, const IOTHUB_CLIENT_DEVICE_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_LL_HANDLE, IoTHubClientCore_LL_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_Destroy, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SendEventAsync, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_GetSendStatus, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetMessageCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetConnectionStatusCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetRetryPolicy, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_GetRetryPolicy, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_GetLastMessageReceiveTime, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + MOCKABLE_FUNCTION(, void, IoTHubClientCore_LL_DoWork, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetOption, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetDeviceTwinCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SendReportedState, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetDeviceMethodCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_DeviceMethodResponse, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, respSize, int, statusCode); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SendEventToOutputAsync, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, const char*, outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_SetInputMessageCallback, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, eventHandlerCallback, void*, userContextCallback); + +#ifndef DONT_USE_UPLOADTOBLOB + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_UploadToBlob, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_UploadMultipleBlocksToBlob, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx, IOTHUB_CLIENT_CORE_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); +#endif /*DONT_USE_UPLOADTOBLOB*/ + +#ifdef USE_EDGE_MODULES + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_CORE_LL_HANDLE, IoTHubClientCore_LL_CreateFromEnvironment, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_CORE_LL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,405 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a device) to communicate +* with an Azure IoTHub. +* +* @details IoTHubClient_LL is a module that allows a user (usually a +* device) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef IOTHUB_CLIENT_LL_H +#define IOTHUB_CLIENT_LL_H + +#include <stddef.h> +#include <stdint.h> + +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_transport_ll.h" +#include "iothub_client_core_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA_TAG* IOTHUB_CLIENT_LL_HANDLE; + + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API does not allow sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_Create, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using an existing transport. + * + * @param config Pointer to an @c IOTHUB_CLIENT_DEVICE_CONFIG structure + * + * The API *allows* sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateWithTransport, const IOTHUB_CLIENT_DEVICE_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the device auth module. + * + * @param iothub_uri Pointer to an ioThub hostname received in the registration process + * @param device_id Pointer to the device Id of the device + * @param device_auth_handle a device auth handle used to generate the connection string + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubClient_LL_Destroy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the device for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubClient_LL_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendEventAsync, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetSendStatus, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetMessageCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the device for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetConnectionStatusCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetLastMessageReceiveTime, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This function is meant to be called by the user when work + * (sending/receiving) can be done by the IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * + * All IoTHubClient interactions (in regards to network traffic + * and/or user level callbacks) are the effect of calling this + * function and they take place synchronously inside _DoWork. + */ + MOCKABLE_FUNCTION(, void, IoTHubClient_LL_DoWork, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b keepalive - available for MQTT protocol. Integer value that sets the + * interval in seconds when pings are sent to the server. + * - @b logtrace - available for MQTT protocol. Boolean value that turns on and + * off the diagnostic logging. + * - @b sas_token_lifetime - available for MQTT & AMQP protocol. size_t value that that determines the + * sas token timeout length. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetOption, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the device receives a desired state update. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceTwinCallback The callback specified by the device client to be used for updating + * the desired state. The callback will be called in response to patch + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceTwinCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + + /** + * @brief This API sneds a report of the device's properties and their current values. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param reportedState The current device property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the device client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendReportedState, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for async cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param inboundDeviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API responses to a asnyc method callback identified the methodId. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param methodId The methodId of the Device Method callback. + * @param response The response data for the method callback. + * @param response_size The size of the response data buffer. + * @param status_response The status response of the method callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_DeviceMethodResponse, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, respSize, int, statusCode); + +#ifndef DONT_USE_UPLOADTOBLOB + /** + * @brief This API uploads to Azure Storage the content pointed to by @p source having the size @p size + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param source pointer to the source for file content (can be NULL) + * @param size the size of the source in memory (if @p source is NULL then size needs to be 0). + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size); + + /** + ** DEPRECATED: Use IoTHubClient_LL_UploadMultipleBlocksToBlobAsyncEx instead ** + * @brief This API uploads to Azure Storage the content provided block by block by @p getDataCallback + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param getDataCallback A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + ** DEPRECATED: Use IoTHubClient_LL_UploadMultipleBlocksToBlobAsyncEx instead ** + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlob, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + + /** + * @brief This API uploads to Azure Storage the content provided block by block by @p getDataCallback + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlobEx, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + +#endif /*DONT_USE_UPLOADTOBLOB*/ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_LL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_ll_uploadtoblob.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a device) to communicate +* with an Azure IoTHub. +* +* @details IoTHubClient_LL is a module that allows a user (usually a +* device) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef DONT_USE_UPLOADTOBLOB + +#ifndef IOTHUB_CLIENT_LL_UPLOADTOBLOB_H +#define IOTHUB_CLIENT_LL_UPLOADTOBLOB_H + +#include "iothub_client_ll.h" + +#include "../../c-utility/inc/azure_c_shared_utility/umock_c_prod.h" +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +typedef struct IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE; + + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, IoTHubClient_LL_UploadToBlob_Create, const IOTHUB_CLIENT_CONFIG*, config); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, const unsigned char*, source, size_t, size); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_SetOption, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, optionName, const void*, value); + MOCKABLE_FUNCTION(, void, IoTHubClient_LL_UploadToBlob_Destroy, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle); +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_LL_UPLOADTOBLOB_H */ + +#else +#error "trying to #include iothub_client_ll_uploadtoblob.h in the presence of #define DONT_USE_UPLOADTOBLOB" +#endif /*DONT_USE_UPLOADTOBLOB*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_options.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_CLIENT_OPTIONS_H +#define IOTHUB_CLIENT_OPTIONS_H + +#include "azure_c_shared_utility/const_defines.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct IOTHUB_PROXY_OPTIONS_TAG + { + const char* host_address; + const char* username; + const char* password; + } IOTHUB_PROXY_OPTIONS; + + static STATIC_VAR_UNUSED const char* OPTION_LOG_TRACE = "logtrace"; + static STATIC_VAR_UNUSED const char* OPTION_X509_CERT = "x509certificate"; + static STATIC_VAR_UNUSED const char* OPTION_X509_PRIVATE_KEY = "x509privatekey"; + static STATIC_VAR_UNUSED const char* OPTION_KEEP_ALIVE = "keepalive"; + static STATIC_VAR_UNUSED const char* OPTION_CONNECTION_TIMEOUT = "connect_timeout"; + + static STATIC_VAR_UNUSED const char* OPTION_PROXY_HOST = "proxy_address"; + static STATIC_VAR_UNUSED const char* OPTION_PROXY_USERNAME = "proxy_username"; + static STATIC_VAR_UNUSED const char* OPTION_PROXY_PASSWORD = "proxy_password"; + + static STATIC_VAR_UNUSED const char* OPTION_SAS_TOKEN_LIFETIME = "sas_token_lifetime"; + static STATIC_VAR_UNUSED const char* OPTION_SAS_TOKEN_REFRESH_TIME = "sas_token_refresh_time"; + static STATIC_VAR_UNUSED const char* OPTION_CBS_REQUEST_TIMEOUT = "cbs_request_timeout"; + + static STATIC_VAR_UNUSED const char* OPTION_MIN_POLLING_TIME = "MinimumPollingTime"; + static STATIC_VAR_UNUSED const char* OPTION_BATCHING = "Batching"; + + /* DEPRECATED:: OPTION_MESSAGE_TIMEOUT is DEPRECATED! Use OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS for AMQP; MQTT has no option available. OPTION_MESSAGE_TIMEOUT legacy variable will be kept for back-compat. */ + static STATIC_VAR_UNUSED const char* OPTION_MESSAGE_TIMEOUT = "messageTimeout"; + static STATIC_VAR_UNUSED const char* OPTION_BLOB_UPLOAD_TIMEOUT_SECS = "blob_upload_timeout_secs"; + static STATIC_VAR_UNUSED const char* OPTION_PRODUCT_INFO = "product_info"; + + /* + * @brief Turns on automatic URL encoding of message properties + system properties. Only valid for use with MQTT Transport + */ + static STATIC_VAR_UNUSED const char* OPTION_AUTO_URL_ENCODE_DECODE = "auto_url_encode_decode"; + + /* + * @brief Informs the service of what is the maximum period the client will wait for a keep-alive message from the service. + * The service must send keep-alives before this timeout is reached, otherwise the client will trigger its re-connection logic. + * Setting this option to a low value results in more aggressive/responsive re-connection by the client. + * The default value for this option is 240 seconds, and the minimum allowed is usually 5 seconds. + * To virtually disable the keep-alives from the service (and consequently the keep-alive timeout control on the client-side), set this option to a high value (e.g., UINT_MAX). + */ + static STATIC_VAR_UNUSED const char* OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS = "svc2cl_keep_alive_timeout_secs"; + + /* DEPRECATED:: OPTION_C2D_KEEP_ALIVE_FREQ_SECS is DEPRECATED! Use OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS, but OPTION_C2D_KEEP_ALIVE_FREQ_SECS legacy variable kept for back-compat. */ + static STATIC_VAR_UNUSED const char* OPTION_C2D_KEEP_ALIVE_FREQ_SECS = "c2d_keep_alive_freq_secs"; + + /* + * @brief Ratio to be used for client side pings in AMQP protocol. + * The client must use this ratio to send keep-alives before service side remote idle timeout is reached, otherwise the service will disconnect the client. + * The default value for this option is 1/2 of the remote idle value sent by the service. + * For AMQP remote idle set to 4 minutes, default client ping will be 2 minutes. For AMQP remote idle set to 25 minutes configured via per Hub basis, the default ping will be 12.5 minutes. + */ + static STATIC_VAR_UNUSED const char* OPTION_REMOTE_IDLE_TIMEOUT_RATIO = "cl2svc_keep_alive_send_ratio"; + + /* + * @brief This option should be used instead of OPTION_MESSAGE_TIMEOUT if using AMQP protocol. + * It defines the maximum ammount of time, in seconds, the client will wait for a telemetry message to complete sending before returning it with a IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT error. + * The default value 5 (five) minutes. + * This option is applicable only to AMQP protocol. + */ + static STATIC_VAR_UNUSED const char* OPTION_EVENT_SEND_TIMEOUT_SECS = "event_send_timeout_secs"; + + //diagnostic sampling percentage value, [0-100] + static STATIC_VAR_UNUSED const char* OPTION_DIAGNOSTIC_SAMPLING_PERCENTAGE = "diag_sampling_percentage"; + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_CLIENT_OPTIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_client_version.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_version.h +* @brief Functions for managing the client SDK version. +*/ + +#ifndef IOTHUB_CLIENT_VERSION_H +#define IOTHUB_CLIENT_VERSION_H + +#define IOTHUB_SDK_VERSION "1.2.8" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Returns a pointer to a null terminated string containing the + * current IoT Hub Client SDK version. + * + * @return Pointer to a null terminated string containing the + * current IoT Hub Client SDK version. + */ + MOCKABLE_FUNCTION(, const char*, IoTHubClient_GetVersionString); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_CLIENT_VERSION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_device_client.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client.h +* @brief Extends the IoTHubCLient_LL module with additional features. +* +* @details IoTHubClient is a module that extends the IoTHubCLient_LL +* module with 2 features: +* - scheduling the work for the IoTHubCLient from a +* thread, so that the user does not need to create their +* own thread +* - thread-safe APIs +*/ + +#ifndef IOTHUB_DEVICE_CLIENT_H +#define IOTHUB_DEVICE_CLIENT_H + +#include <stddef.h> +#include <stdint.h> + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core_ll.h" +#include "iothub_client_core.h" +#include "iothub_device_client_ll.h" + +#ifndef IOTHUB_DEVICE_CLIENT_INSTANCE_TYPE +typedef IOTHUB_CLIENT_CORE_HANDLE IOTHUB_DEVICE_CLIENT_HANDLE; +#define IOTHUB_DEVICE_CLIENT_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_INSTANCE + + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessSignature=SharedAccessSignature sr=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net]/devices/[Device ID goes here]&sig=[SAS Token goes here]&se=[Expiry Time goes here];</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_HANDLE, IoTHubDeviceClient_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API does not allow sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_HANDLE, IoTHubDeviceClient_Create, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param transportHandle TRANSPORT_HANDLE which represents a connection. + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API allows sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_HANDLE, IoTHubDeviceClient_CreateWithTransport, TRANSPORT_HANDLE, transportHandle, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the device auth module. + * + * @param iothub_uri Pointer to an ioThub hostname received in the registration process + * @param device_id Pointer to the device Id of the device + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_HANDLE, IoTHubDeviceClient_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubDeviceClient_Destroy, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the device for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubDeviceClient_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SendEventAsync, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_GetSendStatus, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetMessageCallback, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the device for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetConnectionStatusCallback, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetRetryPolicy, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + * to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_GetRetryPolicy, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_GetLastMessageReceiveTime, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b messageTimeout - the maximum time in milliseconds until a message + * is timeouted. The time starts at IoTHubDeviceClient_SendEventAsync. By default, + * messages do not expire. @p is a pointer to a uint64_t + * - @b svc2cl_keep_alive_timeout_secs - the AMQP service side keep alive interval in seconds. + * After the connection established the client requests the server to set the + * keep alive interval for given time. + * If it is not set then the default 240 sec applies. + * If it is set to zero the server will not send keep alive messages to the client. + * - @b cl2svc_keep_alive_send_ratio - the AMQP client side keep alive interval in seconds. + * After the connection established the server requests the client to set the + * keep alive interval for given time. + * If it is not set then the default ratio of 1/2 is applied. + * The ratio has to be greater than 0.0 and equal to or less than 0.9 + + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetOption, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the device receives a state update. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceTwinCallback The callback specified by the device client to be used for updating + * the desired state. The callback will be called in response to a + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetDeviceTwinCallback, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + + /** + * @brief This API sends a report of the device's properties and their current values. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param reportedState The current device property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the device client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SendReportedState, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for async cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param inboundDeviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_SetDeviceMethodCallback, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API responses to a asnyc method callback identified the methodId. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param methodId The methodId of the Device Method callback. + * @param response The response data for the method callback. + * @param response_size The size of the response data buffer. + * @param status_response The status response of the method callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_DeviceMethodResponse, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, statusCode); + +#ifndef DONT_USE_UPLOADTOBLOB + /** + * @brief IoTHubDeviceClient_UploadToBlobAsync uploads data from memory to a file in Azure Blob Storage. + * + * @param iotHubClientHandle The handle created by a call to the IoTHubDeviceClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param source The source of data. + * @param size The size of data. + * @param iotHubClientFileUploadCallback A callback to be invoked when the file upload operation has finished. + * @param context A user-provided context to be passed to the file upload callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_UploadToBlobAsync, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK, iotHubClientFileUploadCallback, void*, context); + + /** + * @brief Uploads a file to a Blob storage in chunks, fed through the callback function provided by the user. + * @remarks This function allows users to upload large files in chunks, not requiring the whole file content to be passed in memory. + * @param iotHubClientHandle The handle created by a call to the IoTHubDeviceClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_UploadMultipleBlocksToBlobAsync, IOTHUB_DEVICE_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + +#endif /* DONT_USE_UPLOADTOBLOB */ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_DEVICE_CLIENT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_device_client_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,378 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a device) to communicate +* with an Azure IoTHub. +* +* @details IoTHubDeviceClient_LL is a module that allows a user (usually a +* device) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubDeviceClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef IOTHUB_DEVICE_CLIENT_LL_H +#define IOTHUB_DEVICE_CLIENT_LL_H + +#include <stddef.h> +#include <stdint.h> + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_transport_ll.h" +#include "iothub_client_core_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA_TAG* IOTHUB_DEVICE_CLIENT_LL_HANDLE; + + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_LL_HANDLE, IoTHubDeviceClient_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API does not allow sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_LL_HANDLE, IoTHubDeviceClient_LL_Create, const IOTHUB_CLIENT_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using an existing transport. + * + * @param config Pointer to an @c IOTHUB_CLIENT_DEVICE_CONFIG structure + * + * The API *allows* sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_LL_HANDLE, IoTHubDeviceClient_LL_CreateWithTransport, const IOTHUB_CLIENT_DEVICE_CONFIG*, config); + + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the device auth module. + * + * @param iothub_uri Pointer to an ioThub hostname received in the registration process + * @param device_id Pointer to the device Id of the device + * @param device_auth_handle A device auth handle used to generate the connection string + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_DEVICE_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CLIENT_LL_HANDLE, IoTHubDeviceClient_LL_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubDeviceClient_LL_Destroy, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the device for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubDeviceClient_LL_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SendEventAsync, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_GetSendStatus, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetMessageCallback, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the device for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetConnectionStatusCallback, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetRetryPolicy, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_GetRetryPolicy, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_GetLastMessageReceiveTime, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This function is meant to be called by the user when work + * (sending/receiving) can be done by the IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * + * All IoTHubClient interactions (in regards to network traffic + * and/or user level callbacks) are the effect of calling this + * function and they take place synchronously inside _DoWork. + */ + MOCKABLE_FUNCTION(, void, IoTHubDeviceClient_LL_DoWork, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b keepalive - available for MQTT protocol. Integer value that sets the + * interval in seconds when pings are sent to the server. + * - @b logtrace - available for MQTT protocol. Boolean value that turns on and + * off the diagnostic logging. + * - @b sas_token_lifetime - available for MQTT & AMQP protocol. size_t value that that determines the + * sas token timeout length. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetOption, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the device receives a desired state update. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param deviceTwinCallback The callback specified by the device client to be used for updating + * the desired state. The callback will be called in response to patch + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetDeviceTwinCallback, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback); + + /** + * @brief This API sneds a report of the device's properties and their current values. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param reportedState The current device property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the device client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SendReportedState, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + /** + * @brief This API sets callback for async cloud to device method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param inboundDeviceMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_SetDeviceMethodCallback, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback); + + /** + * @brief This API responses to a asnyc method callback identified the methodId. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param methodId The methodId of the Device Method callback. + * @param response The response data for the method callback. + * @param response_size The size of the response data buffer. + * @param status_response The status response of the method callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_DeviceMethodResponse, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, respSize, int, statusCode); + +#ifndef DONT_USE_UPLOADTOBLOB + /** + * @brief This API uploads to Azure Storage the content pointed to by @p source having the size @p size + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param source pointer to the source for file content (can be NULL) + * @param size the size of the source in memory (if @p source is NULL then size needs to be 0). + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_UploadToBlob, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size); + + /** + * @brief This API uploads to Azure Storage the content provided block by block by @p getDataCallback + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob, IOTHUB_DEVICE_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + +#endif /*DONT_USE_UPLOADTOBLOB*/ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_DEVICE_CLIENT_LL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_message.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,360 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_message.h +* @brief The @c IoTHub_Message component encapsulates one message that +* can be transferred by an IoT hub client. +*/ + +#ifndef IOTHUB_MESSAGE_H +#define IOTHUB_MESSAGE_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +#define IOTHUB_MESSAGE_RESULT_VALUES \ + IOTHUB_MESSAGE_OK, \ + IOTHUB_MESSAGE_INVALID_ARG, \ + IOTHUB_MESSAGE_INVALID_TYPE, \ + IOTHUB_MESSAGE_ERROR \ + +/** @brief Enumeration specifying the status of calls to various +* APIs in this module. +*/ +DEFINE_ENUM(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_RESULT_VALUES); + +#define IOTHUBMESSAGE_CONTENT_TYPE_VALUES \ +IOTHUBMESSAGE_BYTEARRAY, \ +IOTHUBMESSAGE_STRING, \ +IOTHUBMESSAGE_UNKNOWN \ + +/** @brief Enumeration specifying the content type of the a given +* message. +*/ +DEFINE_ENUM(IOTHUBMESSAGE_CONTENT_TYPE, IOTHUBMESSAGE_CONTENT_TYPE_VALUES); + +typedef struct IOTHUB_MESSAGE_HANDLE_DATA_TAG* IOTHUB_MESSAGE_HANDLE; + +/** @brief diagnostic related data*/ +typedef struct IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_TAG +{ + char* diagnosticId; + char* diagnosticCreationTimeUtc; +}IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA, *IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE; + +static const char DIAG_CREATION_TIME_UTC_PROPERTY_NAME[] = "diag_creation_time_utc"; + +/** +* @brief Creates a new IoT hub message from a byte array. The type of the +* message will be set to @c IOTHUBMESSAGE_BYTEARRAY. +* +* @param byteArray The byte array from which the message is to be created. +* @param size The size of the byte array. +* +* @return A valid @c IOTHUB_MESSAGE_HANDLE if the message was successfully +* created or @c NULL in case an error occurs. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_HANDLE, IoTHubMessage_CreateFromByteArray, const unsigned char*, byteArray, size_t, size); + +/** +* @brief Creates a new IoT hub message from a null terminated string. The +* type of the message will be set to @c IOTHUBMESSAGE_STRING. +* +* @param source The null terminated string from which the message is to be +* created. +* +* @return A valid @c IOTHUB_MESSAGE_HANDLE if the message was successfully +* created or @c NULL in case an error occurs. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_HANDLE, IoTHubMessage_CreateFromString, const char*, source); + +/** +* @brief Creates a new IoT hub message with the content identical to that +* of the @p iotHubMessageHandle parameter. +* +* @param iotHubMessageHandle Handle to the message that is to be cloned. +* +* @return A valid @c IOTHUB_MESSAGE_HANDLE if the message was successfully +* cloned or @c NULL in case an error occurs. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_HANDLE, IoTHubMessage_Clone, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Fetches a pointer and size for the data associated with the IoT +* hub message handle. If the content type of the message is not +* @c IOTHUBMESSAGE_BYTEARRAY then the function returns +* @c IOTHUB_MESSAGE_INVALID_ARG. +* +* @param iotHubMessageHandle Handle to the message. +* @param buffer Pointer to the memory location where the +* pointer to the buffer will be written. +* @param size The size of the buffer will be written to +* this address. +* +* @return Returns IOTHUB_MESSAGE_OK if the byte array was fetched successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_GetByteArray, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const unsigned char**, buffer, size_t*, size); + +/** +* @brief Returns the null terminated string stored in the message. +* If the content type of the message is not @c IOTHUBMESSAGE_STRING +* then the function returns @c NULL. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return @c NULL if an error occurs or a pointer to the stored null +* terminated string otherwise. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetString, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Returns the content type of the message given by parameter +* @c iotHubMessageHandle. +* +* @param iotHubMessageHandle Handle to the message. +* +* @remarks This function retrieves the standardized type of the payload, which indicates if @c iotHubMessageHandle was created using a String or a Byte Array. +* +* @return An @c IOTHUBMESSAGE_CONTENT_TYPE value. +*/ +MOCKABLE_FUNCTION(, IOTHUBMESSAGE_CONTENT_TYPE, IoTHubMessage_GetContentType, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets the content-type of the message payload, as per supported values on RFC 2046. +* +* @param iotHubMessageHandle Handle to the message. +* +* @param contentType String defining the type of the payload (e.g., text/plain). +* +* @return An @c IOTHUB_MESSAGE_RESULT value. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetContentTypeSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, contentType); + +/** +* @brief Returns the content-type of the message payload, if defined. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A string with the content-type value if defined (or NULL otherwise). +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetContentTypeSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets the content-encoding of the message payload, as per supported values on RFC 2616. +* +* @param iotHubMessageHandle Handle to the message. +* +* @param contentEncoding String defining the encoding of the payload (e.g., utf-8). +* +* @return An @c IOTHUB_MESSAGE_RESULT value. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetContentEncodingSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, contentEncoding); + +/** +* @brief Returns the content-encoding of the message payload, if defined. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A string with the content-encoding value if defined (or NULL otherwise). +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetContentEncodingSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Gets a handle to the message's properties map. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A @c MAP_HANDLE pointing to the properties map for this message. +*/ +MOCKABLE_FUNCTION(, MAP_HANDLE, IoTHubMessage_Properties, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets a property on a Iothub Message. +* +* @param iotHubMessageHandle Handle to the message. +* +* @param key name of the property to set. +* +* @param value of the property to set. +* +* @return An @c IOTHUB_MESSAGE_RESULT value indicating the result of setting the property. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, key, const char*, value); + +/** +* @brief Gets a IotHub Message's properties item. +* +* @param iotHubMessageHandle Handle to the message. +* +* @param key name of the property to retrieve. +* +* @return A string with the property's value, or NULL if it does not exist in the properties list. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, key); + +/** +* @brief Gets the MessageId from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the Message Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetMessageId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets the MessageId for the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* @param messageId Pointer to the memory location of the messageId +* +* @return Returns IOTHUB_MESSAGE_OK if the messageId was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetMessageId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, messageId); + +/** +* @brief Gets the CorrelationId from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the Correlation Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetCorrelationId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets the CorrelationId for the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* @param correlationId Pointer to the memory location of the messageId +* +* @return Returns IOTHUB_MESSAGE_OK if the messageId was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetCorrelationId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, correlationId); + +/** +* @brief Gets the DiagnosticData from the IOTHUB_MESSAGE_HANDLE. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* pointing to the diagnostic property data. +*/ +MOCKABLE_FUNCTION(, const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA*, IoTHubMessage_GetDiagnosticPropertyData, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets the DiagnosticData for the IOTHUB_MESSAGE_HANDLE. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* @param diagnosticData Pointer to the memory location of the diagnosticData +* +* @return Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetDiagnosticPropertyData, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA*, diagnosticData); + +/** +* @brief Gets the output name from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the Output Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetOutputName, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + + +/** +* @brief Sets output for named queues. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* @param outputName Pointer to the queue to output message to +* +* @return Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetOutputName, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, outputName); + + +/** +* @brief Gets the input name from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the Input Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetInputName, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets input for named queues. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* @param inputName Pointer to the queue to input message to +* +* @return Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetInputName, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, inputName); + +/** +* @brief Gets the module name from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the connection module Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetConnectionModuleId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets connection module ID. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* @param connectionModuleId Pointer to the module ID of connector +* +* @return Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetConnectionModuleId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, connectionModuleId); + + +/** +* @brief Gets the connection device ID from the IOTHUB_MESSAGE_HANDLE. +* +* @param iotHubMessageHandle Handle to the message. +* +* @return A const char* pointing to the connection device Id. +*/ +MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetConnectionDeviceId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +/** +* @brief Sets connection device Id. CAUTION: SDK user should not call it directly, it is for internal use only. +* +* @param iotHubMessageHandle Handle to the message. +* @param connectionDeviceId Pointer to the device ID of connector +* +* @return Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully +* or an error code otherwise. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetConnectionDeviceId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, connectionDeviceId); + + +/** +* @brief Frees all resources associated with the given message handle. +* +* @param iotHubMessageHandle Handle to the message. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessage_Destroy, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_MESSAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_module_client.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,370 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client.h +* @brief Extends the IoTHubCLient_LL module with additional features. +* +* @details IoTHubClient is a module that extends the IoTHubCLient_LL +* module with 2 features: +* - scheduling the work for the IoTHubCLient from a +* thread, so that the user does not need to create their +* own thread +* - thread-safe APIs +*/ + +#ifndef IOTHUB_MODULE_CLIENT_H +#define IOTHUB_MODULE_CLIENT_H + +#include <stddef.h> +#include <stdint.h> + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core_ll.h" +#include "iothub_client_core.h" +#include "iothub_module_client_ll.h" + +#ifndef IOTHUB_MODULE_CLIENT_INSTANCE_TYPE +typedef IOTHUB_CLIENT_CORE_HANDLE IOTHUB_MODULE_CLIENT_HANDLE; +#define IOTHUB_MODULE_CLIENT_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_INSTANCE + + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];ModuleId=[Module ID goes here]</pre> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessSignature=SharedAccessSignature sr=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net]/devices/[Device ID goes here]&sig=[SAS Token goes here]&se=[Expiry Time goes here];ModuleId=[Module ID goes here]</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_MODULE_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_MODULE_CLIENT_HANDLE, IoTHubModuleClient_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubModuleClient_Destroy, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the module for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubModuleClient_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SendEventAsync, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_GetSendStatus, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubDeviceClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetMessageCallback, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the module for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetConnectionStatusCallback, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetRetryPolicy, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_GetRetryPolicy, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_GetLastMessageReceiveTime, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b messageTimeout - the maximum time in milliseconds until a message + * is timeouted. The time starts at IoTHubModuleClient_SendEventAsync. By default, + * messages do not expire. @p is a pointer to a uint64_t + * - @b svc2cl_keep_alive_timeout_secs - the AMQP service side keep alive interval in seconds. + * After the connection established the client requests the server to set the + * keep alive interval for given time. + * If it is not set then the default 240 sec applies. + * If it is set to zero the server will not send keep alive messages to the client. + * - @b cl2svc_keep_alive_send_ratio - the AMQP client side keep alive interval in seconds. + * After the connection established the server requests the client to set the + * keep alive interval for given time. + * If it is not set then the default ratio of 1/2 is applied. + * The ratio has to be greater than 0.0 and equal to or less than 0.9 + + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetOption, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the module receives a state update. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param moduleTwinCallback The callback specified by the module client to be used for updating + * the desired state. The callback will be called in response to a + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetModuleTwinCallback, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, moduleTwinCallback, void*, userContextCallback); + + /** + * @brief This API sends a report of the module's properties and their current values. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param reportedState The current module property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the module client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SendReportedState, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for async cloud to module method call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param methodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetModuleMethodCallback, IOTHUB_MODULE_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, methodCallback, void*, userContextCallback); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param outputName The name of the queue to send the message to. + * @param eventConfirmationCallback The callback specified by the module for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubClient_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SendEventToOutputAsync, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, const char*, outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + + /** + * @brief This API sets callback for method call that is directed to specified 'inputName' queue (e.g. messages from IoTHubClient_SendEventToOutputAsync) + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param inputName The name of the queue to listen on for this moduleMethodCallback/userContextCallback. + * @param eventHandlerCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_SetInputMessageCallback, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, const char*, inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, eventHandlerCallback, void*, userContextCallback); + +#ifdef USE_EDGE_MODULES + /** + * @brief This API creates a module handle based on environment variables set in the Edge runtime. + NOTE: It is *ONLY* valid when the code is running in a container initiated by Edge. + * + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + + */ + MOCKABLE_FUNCTION(, IOTHUB_MODULE_CLIENT_HANDLE, IoTHubModuleClient_CreateFromEnvironment, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /* + * @brief This API invokes a device method on a specified device + * + * @param iotHubModuleClientHandle The handle created by a call to a create function + * @param deviceId The device id of the device to invoke a method on + * @param methodName The name of the method + * @param methodPayload The method payload (in json format) + * @param timeout The time in seconds before a timeout occurs + * @param responseStatus This pointer will be filled with the response status after invoking the device method + * @param responsePayload This pointer will be filled with the response payload + * @param responsePayloadSize This pointer will be filled with the response payload size + * + * @return IOTHUB_CLIENT_OK upon success, or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_DeviceMethodInvokeAsync, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, const char*, deviceId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, IOTHUB_METHOD_INVOKE_CALLBACK, methodInvokeCallback, void*, context); + + /* + * @brief This API invokes a module method on a specified module + * + * @param iotHubModuleClientHandle The handle created by a call to a create function + * @param deviceId The device id of the device to invoke a method on + * @param moduleId The module id of the module to invoke a method on + * @param methodName The name of the method + * @param methodPayload The method payload (in json format) + * @param timeout The time in seconds before a timeout occurs + * @param responseStatus This pointer will be filled with the response status after invoking the module method + * @param responsePayload This pointer will be filled with the response payload + * @param responsePayloadSize This pointer will be filled with the response payload size + * + * @return IOTHUB_CLIENT_OK upon success, or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_ModuleMethodInvokeAsync, IOTHUB_MODULE_CLIENT_HANDLE, iotHubModuleClientHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, IOTHUB_METHOD_INVOKE_CALLBACK, methodInvokeCallback, void*, context); + +#endif /*USE_EDGE_MODULES*/ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_MODULE_CLIENT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_module_client_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,387 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_client_ll.h +* @brief APIs that allow a user (usually a module) to communicate +* with an Azure IoTHub. +* +* @details IoTHubModuleClient_LL is a module that allows a user (usually a +* module) to communicate with an Azure IoTHub. It can send events +* and receive messages. At any given moment in time there can only +* be at most 1 message callback function. +* +* This API surface contains a set of APIs that allows the user to +* interact with the lower layer portion of the IoTHubClient. These APIs +* contain @c _LL_ in their name, but retain the same functionality like the +* @c IoTHubModuleClient_... APIs, with one difference. If the @c _LL_ APIs are +* used then the user is responsible for scheduling when the actual work done +* by the IoTHubClient happens (when the data is sent/received on/from the wire). +* This is useful for constrained devices where spinning a separate thread is +* often not desired. +*/ + +#ifndef IOTHUB_MODULE_CLIENT_LL_H +#define IOTHUB_MODULE_CLIENT_LL_H + +#include <time.h> +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_transport_ll.h" +#include "iothub_client_core_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA_TAG* IOTHUB_MODULE_CLIENT_LL_HANDLE; + +#ifdef __cplusplus +} +#endif + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + * <blockquote> + * <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];ModuleId=[Module ID goes here]</pre> + * </blockquote> + * + * @return A non-NULL @c IOTHUB_MODULE_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_MODULE_CLIENT_LL_HANDLE, IoTHubModuleClient_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + */ + MOCKABLE_FUNCTION(, void, IoTHubModuleClient_LL_Destroy, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the module for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubModuleClient_LL_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SendEventAsync, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_GetSendStatus, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); + + /** + * @brief Sets up the message callback to be invoked when Edge issues a + * message to the module. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the module for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetMessageCallback, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param connectionStatusCallback The callback specified by the module for receiving + * updates about the status of the connection to IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetConnectionStatusCallback, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback); + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param retryPolicy The policy to use to reconnect to IoT Hub when a + * connection drops. + * @param retryTimeoutLimitInSeconds Maximum amount of time(seconds) to attempt reconnection when a + * connection drops to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetRetryPolicy, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); + + + /** + * @brief Sets up the connection status callback to be invoked representing the status of + * the connection to IOT Hub. This is a blocking call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param retryPolicy Out parameter containing the policy to use to reconnect to IoT Hub. + * @param retryTimeoutLimitInSeconds Out parameter containing maximum amount of time in seconds to attempt reconnection + to IOT Hub. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_GetRetryPolicy, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds); + + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_GetLastMessageReceiveTime, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, time_t*, lastMessageReceiveTime); + + /** + * @brief This function is meant to be called by the user when work + * (sending/receiving) can be done by the IoTHubClient. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * + * All IoTHubClient interactions (in regards to network traffic + * and/or user level callbacks) are the effect of calling this + * function and they take place synchronously inside _DoWork. + */ + MOCKABLE_FUNCTION(, void, IoTHubModuleClient_LL_DoWork, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle); + + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is <em>total request time</em>. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx"> + * WinHttpSetTimeouts</a> API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b keepalive - available for MQTT protocol. Integer value that sets the + * interval in seconds when pings are sent to the server. + * - @b logtrace - available for MQTT protocol. Boolean value that turns on and + * off the diagnostic logging. + * - @b sas_token_lifetime - available for MQTT & AMQP protocol. size_t value that that determines the + * sas token timeout length. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetOption, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, const char*, optionName, const void*, value); + + /** + * @brief This API specifies a call back to be used when the module receives a desired state update. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param moduleTwinCallback The callback specified by the module client to be used for updating + * the desired state. The callback will be called in response to patch + * request send by the IoTHub services. The payload will be passed to the + * callback, along with two version numbers: + * - Desired: + * - LastSeenReported: + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetModuleTwinCallback, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, moduleTwinCallback, void*, userContextCallback); + + /** + * @brief This API sneds a report of the module's properties and their current values. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param reportedState The current module property values to be 'reported' to the IoTHub. + * @param reportedStateCallback The callback specified by the module client to be called with the + * result of the transaction. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubModuleClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SendReportedState, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback); + /** + * @brief This API sets callback for async cloud to module method call. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param moduleMethodCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetModuleMethodCallback, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, moduleMethodCallback, void*, userContextCallback); + + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param outputName The name of the queue to send the message to. + * @param eventConfirmationCallback The callback specified by the module for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubClient_LL_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_LL_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SendEventToOutputAsync, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, const char*, outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback); + + /** + * @brief This API sets callback for method call that is directed to specified 'inputName' queue (e.g. messages from IoTHubClient_SendEventToOutputAsync) + * + * @param iotHubModuleClientHandle The handle created by a call to the create function. + * @param inputName The name of the queue to listen on for this moduleMethodCallback/userContextCallback. + * @param eventHandlerCallback The callback which will be called by IoTHub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_SetInputMessageCallback, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, const char*, inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, eventHandlerCallback, void*, userContextCallback); + +#ifdef USE_EDGE_MODULES + + /** + * @brief This API creates a module handle based on environment variables set in the Edge runtime. + * NOTE: It is *ONLY* valid when the code is running in a container initiated by Edge. + * + * @param protocol Function pointer for protocol implementation + * + * @return A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_MODULE_CLIENT_LL_HANDLE, IoTHubModuleClient_LL_CreateFromEnvironment, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol); + + /* + * @brief This API invokes a device method on a specified device + * + * @param iotHubModuleClientHandle The handle created by a call to a create function + * @param deviceId The device id of the device to invoke a method on + * @param methodName The name of the method + * @param methodPayload The method payload (in json format) + * @param timeout The time in seconds before a timeout occurs + * @param responseStatus This pointer will be filled with the response status after invoking the device method + * @param responsePayload This pointer will be filled with the response payload + * @param responsePayloadSize This pointer will be filled with the response payload size + * + * @return IOTHUB_CLIENT_OK upon success, or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_DeviceMethodInvoke, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, const char*, deviceId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + + /* + * @brief This API invokes a module method on a specified module + * + * @param iotHubModuleClientHandle The handle created by a call to a create function + * @param deviceId The device id of the device to invoke a method on + * @param moduleId The module id of the module to invoke a method on + * @param methodName The name of the method + * @param methodPayload The method payload (in json format) + * @param timeout The time in seconds before a timeout occurs + * @param responseStatus This pointer will be filled with the response status after invoking the module method + * @param responsePayload This pointer will be filled with the response payload + * @param responsePayloadSize This pointer will be filled with the response payload size + * + * @return IOTHUB_CLIENT_OK upon success, or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubModuleClient_LL_ModuleMethodInvoke, IOTHUB_MODULE_CLIENT_LL_HANDLE, iotHubModuleClientHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + +#endif /*USE_EDGE_MODULES*/ + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_MODULE_CLIENT_LL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothub_transport_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_TRANSPORT_LL_H +#define IOTHUB_TRANSPORT_LL_H + +#ifdef __cplusplus +#include <cstddef> +#include <cstdint> +extern "C" +{ +#else +#include <stddef.h> +#include <stdint.h> +#endif + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef void* TRANSPORT_LL_HANDLE; +typedef void* IOTHUB_DEVICE_HANDLE; + +struct TRANSPORT_HANDLE_DATA_TAG; +typedef struct TRANSPORT_HANDLE_DATA_TAG* TRANSPORT_HANDLE; + +typedef void* METHOD_HANDLE; + +struct TRANSPORT_PROVIDER_TAG; +typedef struct TRANSPORT_PROVIDER_TAG TRANSPORT_PROVIDER; + +typedef const TRANSPORT_PROVIDER*(*IOTHUB_CLIENT_TRANSPORT_PROVIDER)(void); + +MOCKABLE_FUNCTION(, TRANSPORT_HANDLE, IoTHubTransport_Create, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol, const char*, iotHubName, const char*, iotHubSuffix); +MOCKABLE_FUNCTION(, void, IoTHubTransport_Destroy, TRANSPORT_HANDLE, transportHandle); +MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_GetLLTransport, TRANSPORT_HANDLE, transportHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_TRANSPORT_LL_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransport.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUB_TRANSPORT_H +#define IOTHUB_TRANSPORT_H + +typedef struct TRANSPORT_HANDLE_DATA_TAG* TRANSPORT_HANDLE; + + +#include "../../c-utility/inc/azure_c_shared_utility/lock.h" +#include "../../c-utility/inc/azure_c_shared_utility/crt_abstractions.h" + +#include "iothub_client_ll.h" +#include "internal/iothub_client_private.h" +#include "iothub_transport_ll.h" + +#ifndef IOTHUB_CLIENT_INSTANCE_TYPE +typedef struct IOTHUB_CLIENT_INSTANCE_TAG* IOTHUB_CLIENT_HANDLE; +#define IOTHUB_CLIENT_INSTANCE_TYPE +#endif // IOTHUB_CLIENT_INSTANCE + +#include "../../c-utility/inc/azure_c_shared_utility/umock_c_prod.h" + +typedef void(*IOTHUB_CLIENT_MULTIPLEXED_DO_WORK)(void* iotHubClientInstance); + +#ifdef __cplusplus +extern "C" +{ +#endif + + MOCKABLE_FUNCTION(, TRANSPORT_HANDLE, IoTHubTransport_Create, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol, const char*, iotHubName, const char*, iotHubSuffix); + MOCKABLE_FUNCTION(, void, IoTHubTransport_Destroy, TRANSPORT_HANDLE, transportHandle); + MOCKABLE_FUNCTION(, LOCK_HANDLE, IoTHubTransport_GetLock, TRANSPORT_HANDLE, transportHandle); + MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_GetLLTransport, TRANSPORT_HANDLE, transportHandle); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_StartWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_HANDLE, clientHandle, IOTHUB_CLIENT_MULTIPLEXED_DO_WORK, muxDoWork); + MOCKABLE_FUNCTION(, bool, IoTHubTransport_SignalEndWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_HANDLE, clientHandle); + MOCKABLE_FUNCTION(, void, IoTHubTransport_JoinWorkerThread, TRANSPORT_HANDLE, transportHandle, IOTHUB_CLIENT_HANDLE, clientHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUB_TRANSPORT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransport_mqtt_common.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORT_MQTT_COMMON_H +#define IOTHUBTRANSPORT_MQTT_COMMON_H + +#include "iothub_transport_ll.h" +#include "../../c-utility/inc/azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct MQTT_TRANSPORT_PROXY_OPTIONS_TAG +{ + const char* host_address; + int port; + const char* username; + const char* password; +} MQTT_TRANSPORT_PROXY_OPTIONS; + +typedef XIO_HANDLE(*MQTT_GET_IO_TRANSPORT)(const char* fully_qualified_name, const MQTT_TRANSPORT_PROXY_OPTIONS* mqtt_transport_proxy_options); + +MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_MQTT_Common_Create, const IOTHUBTRANSPORT_CONFIG*, config, MQTT_GET_IO_TRANSPORT, get_io_transport); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Destroy, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod, IOTHUB_DEVICE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_DeviceMethod_Response, IOTHUB_DEVICE_HANDLE, handle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, status_response); +MOCKABLE_FUNCTION(, IOTHUB_PROCESS_ITEM_RESULT, IoTHubTransport_MQTT_Common_ProcessItem, TRANSPORT_LL_HANDLE, handle, IOTHUB_IDENTITY_TYPE, item_type, IOTHUB_IDENTITY_INFO*, iothub_item); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_DoWork, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_GetSendStatus, IOTHUB_DEVICE_HANDLE, handle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_SetOption, TRANSPORT_LL_HANDLE, handle, const char*, option, const void*, value); +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_HANDLE, IoTHubTransport_MQTT_Common_Register, TRANSPORT_LL_HANDLE, handle, const IOTHUB_DEVICE_CONFIG*, device, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, PDLIST_ENTRY, waitingToSend); +MOCKABLE_FUNCTION(, void, IoTHubTransport_MQTT_Common_Unregister, IOTHUB_DEVICE_HANDLE, deviceHandle); +MOCKABLE_FUNCTION(, int, IoTHubTransport_MQTT_Common_SetRetryPolicy, TRANSPORT_LL_HANDLE, handle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds); +MOCKABLE_FUNCTION(, STRING_HANDLE, IoTHubTransport_MQTT_Common_GetHostname, TRANSPORT_LL_HANDLE, handle); +MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_MQTT_Common_SendMessageDisposition, MESSAGE_CALLBACK_INFO*, message_data, IOTHUBMESSAGE_DISPOSITION_RESULT, disposition) + + +#ifdef __cplusplus +} +#endif + +#endif /* IOTHUBTRANSPORT_MQTT_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransportamqp.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_H +#define IOTHUBTRANSPORTAMQP_H + +#include "iothub_transport_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const TRANSPORT_PROVIDER* AMQP_Protocol(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTAMQP_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransportamqp_websockets.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTAMQP_WEBSOCKETS_H +#define IOTHUBTRANSPORTAMQP_WEBSOCKETS_H + +#include "iothub_transport_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const TRANSPORT_PROVIDER* AMQP_Protocol_over_WebSocketsTls(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTAMQP_WEBSOCKETS_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransporthttp.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTHTTP_H +#define IOTHUBTRANSPORTHTTP_H + +#include "iothub_transport_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const TRANSPORT_PROVIDER* HTTP_Protocol(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTHTTP_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransportmqtt.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTMQTT_H +#define IOTHUBTRANSPORTMQTT_H + +#include "iothub_transport_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + extern const TRANSPORT_PROVIDER* MQTT_Protocol(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTMQTT_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/inc/iothubtransportmqtt_websockets.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBTRANSPORTMQTT_WEBSOCKETS_H +#define IOTHUBTRANSPORTMQTT_WEBSOCKETS_H + +#include "iothub_transport_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const TRANSPORT_PROVIDER* MQTT_WebSocket_Protocol(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBTRANSPORTMQTT_WEBSOCKETS_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/blob.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,381 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include "azure_c_shared_utility/gballoc.h" +#include "internal/blob.h" +#include "internal/iothub_client_ll_uploadtoblob.h" + +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/shared_util_options.h" + +BLOB_RESULT Blob_UploadBlock( + HTTPAPIEX_HANDLE httpApiExHandle, + const char* relativePath, + BUFFER_HANDLE requestContent, + unsigned int blockID, + STRING_HANDLE blockIDList, + unsigned int* httpStatus, + BUFFER_HANDLE httpResponse) +{ + BLOB_RESULT result; + + if (requestContent == NULL || + blockIDList == NULL || + relativePath == NULL || + httpApiExHandle == NULL || + httpStatus == NULL || + httpResponse == NULL) + { + LogError("invalid argument detected requestContent=%p blockIDList=%p relativePath=%p httpApiExHandle=%p httpStatus=%p httpResponse=%p", requestContent, blockIDList, relativePath, httpApiExHandle, httpStatus, httpResponse); + result = BLOB_ERROR; + } + else + { + char temp[7]; /*this will contain 000000... 049999*/ + if (sprintf(temp, "%6u", (unsigned int)blockID) != 6) /*produces 000000... 049999*/ + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to sprintf"); + result = BLOB_ERROR; + } + else + { + STRING_HANDLE blockIdString = Base64_Encode_Bytes((const unsigned char*)temp, 6); + if (blockIdString == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to Base64_Encode_Bytes"); + result = BLOB_ERROR; + } + else + { + /*add the blockId base64 encoded to the XML*/ + if (!( + (STRING_concat(blockIDList, "<Latest>") == 0) && + (STRING_concat_with_STRING(blockIDList, blockIdString) == 0) && + (STRING_concat(blockIDList, "</Latest>") == 0) + )) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to STRING_concat"); + result = BLOB_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_022: [ Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string: base relativePath + "&comp=block&blockid=BASE64 encoded string of blockId" ]*/ + STRING_HANDLE newRelativePath = STRING_construct(relativePath); + if (newRelativePath == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to STRING_construct"); + result = BLOB_ERROR; + } + else + { + if (!( + (STRING_concat(newRelativePath, "&comp=block&blockid=") == 0) && + (STRING_concat_with_STRING(newRelativePath, blockIdString) == 0) + )) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to STRING concatenate"); + result = BLOB_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_024: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing httpStatus and httpResponse. ]*/ + if (HTTPAPIEX_ExecuteRequest( + httpApiExHandle, + HTTPAPI_REQUEST_PUT, + STRING_c_str(newRelativePath), + NULL, + requestContent, + httpStatus, + NULL, + httpResponse) != HTTPAPIEX_OK + ) + { + /*Codes_SRS_BLOB_02_025: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/ + LogError("unable to HTTPAPIEX_ExecuteRequest"); + result = BLOB_HTTP_ERROR; + } + else if (*httpStatus >= 300) + { + /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/ + LogError("HTTP status from storage does not indicate success (%d)", (int)*httpStatus); + result = BLOB_OK; + } + else + { + /*Codes_SRS_BLOB_02_027: [ Otherwise Blob_UploadMultipleBlocksFromSasUri shall continue execution. ]*/ + result = BLOB_OK; + } + } + STRING_delete(newRelativePath); + } + } + STRING_delete(blockIdString); + } + } + } + return result; +} + +BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions) +{ + BLOB_RESULT result; + /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ + if (SASURI == NULL) + { + LogError("parameter SASURI is NULL"); + result = BLOB_INVALID_ARG; + } + else + { + /*Codes_SRS_BLOB_02_002: [ If getDataCallbackEx is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ + if (getDataCallbackEx == NULL) + { + LogError("IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx is NULL"); + result = BLOB_INVALID_ARG; + } + /*the below define avoid a "condition always false" on some compilers*/ + else + { + /*Codes_SRS_BLOB_02_017: [ Blob_UploadMultipleBlocksFromSasUri shall copy from SASURI the hostname to a new const char* ]*/ + /*to find the hostname, the following logic is applied:*/ + /*the hostname starts at the first character after "://"*/ + /*the hostname ends at the first character before the next "/" after "://"*/ + const char* hostnameBegin = strstr(SASURI, "://"); + if (hostnameBegin == NULL) + { + /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ + LogError("hostname cannot be determined"); + result = BLOB_INVALID_ARG; + } + else + { + hostnameBegin += 3; /*have to skip 3 characters which are "://"*/ + const char* hostnameEnd = strchr(hostnameBegin, '/'); + if (hostnameEnd == NULL) + { + /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ + LogError("hostname cannot be determined"); + result = BLOB_INVALID_ARG; + } + else + { + size_t hostnameSize = hostnameEnd - hostnameBegin; + char* hostname = (char*)malloc(hostnameSize + 1); /*+1 because of '\0' at the end*/ + if (hostname == NULL) + { + /*Codes_SRS_BLOB_02_016: [ If the hostname copy cannot be made then then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("oom - out of memory"); + result = BLOB_ERROR; + } + else + { + HTTPAPIEX_HANDLE httpApiExHandle; + (void)memcpy(hostname, hostnameBegin, hostnameSize); + hostname[hostnameSize] = '\0'; + + /*Codes_SRS_BLOB_02_018: [ Blob_UploadMultipleBlocksFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/ + httpApiExHandle = HTTPAPIEX_Create(hostname); + if (httpApiExHandle == NULL) + { + /*Codes_SRS_BLOB_02_007: [ If HTTPAPIEX_Create fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR. ]*/ + LogError("unable to create a HTTPAPIEX_HANDLE"); + result = BLOB_ERROR; + } + else + { + if ((certificates != NULL)&& (HTTPAPIEX_SetOption(httpApiExHandle, "TrustedCerts", certificates) == HTTPAPIEX_ERROR)) + { + LogError("failure in setting trusted certificates"); + result = BLOB_ERROR; + } + else if ((proxyOptions != NULL && proxyOptions->host_address != NULL) && HTTPAPIEX_SetOption(httpApiExHandle, OPTION_HTTP_PROXY, proxyOptions) == HTTPAPIEX_ERROR) + { + LogError("failure in setting proxy options"); + result = BLOB_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_019: [ Blob_UploadMultipleBlocksFromSasUri shall compute the base relative path of the request from the SASURI parameter. ]*/ + const char* relativePath = hostnameEnd; /*this is where the relative path begins in the SasUri*/ + + /*Codes_SRS_BLOB_02_028: [ Blob_UploadMultipleBlocksFromSasUri shall construct an XML string with the following content: ]*/ + STRING_HANDLE blockIDList = STRING_construct("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<BlockList>"); /*the XML "build as we go"*/ + if (blockIDList == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to STRING_construct"); + result = BLOB_HTTP_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_021: [ For every block returned by `getDataCallbackEx` the following operations shall happen: ]*/ + unsigned int blockID = 0; /* incremented for each new block */ + unsigned int isError = 0; /* set to 1 if a block upload fails or if getDataCallbackEx returns incorrect blocks to upload */ + unsigned int uploadOneMoreBlock = 1; /* set to 1 while getDataCallbackEx returns correct blocks to upload */ + unsigned char const * source; /* data set by getDataCallbackEx */ + size_t size; /* source size set by getDataCallbackEx */ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT getDataReturnValue; + + do + { + getDataReturnValue = getDataCallbackEx(FILE_UPLOAD_OK, &source, &size, context); + if (getDataReturnValue == IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT) + { + /*Codes_SRS_BLOB_99_004: [ If `getDataCallbackEx` returns `IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_ABORT`, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop and return `BLOB_ABORTED`. ]*/ + LogInfo("Upload to blob has been aborted by the user"); + uploadOneMoreBlock = 0; + result = BLOB_ABORTED; + } + else if (source == NULL || size == 0) + { + /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallbackEx` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/ + uploadOneMoreBlock = 0; + result = BLOB_OK; + } + else + { + if (size > BLOCK_SIZE) + { + /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallbackEx` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ + LogError("tried to upload block of size %lu, max allowed size is %d", size, BLOCK_SIZE); + result = BLOB_INVALID_ARG; + isError = 1; + } + else if (blockID >= MAX_BLOCK_COUNT) + { + /*Codes_SRS_BLOB_99_003: [ If `getDataCallbackEx` returns more than 50000 blocks, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ + LogError("unable to upload more than %lu blocks in one blob", MAX_BLOCK_COUNT); + result = BLOB_INVALID_ARG; + isError = 1; + } + else + { + /*Codes_SRS_BLOB_02_023: [ Blob_UploadMultipleBlocksFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/ + BUFFER_HANDLE requestContent = BUFFER_create(source, size); + if (requestContent == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to BUFFER_create"); + result = BLOB_ERROR; + isError = 1; + } + else + { + result = Blob_UploadBlock( + httpApiExHandle, + relativePath, + requestContent, + blockID, + blockIDList, + httpStatus, + httpResponse); + + BUFFER_delete(requestContent); + } + + /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/ + if (result != BLOB_OK || *httpStatus >= 300) + { + LogError("unable to Blob_UploadBlock. Returned value=%d, httpStatus=%u", result, httpStatus); + isError = 1; + } + } + blockID++; + } + } + while(uploadOneMoreBlock && !isError); + + if (isError || result != BLOB_OK) + { + /*do nothing, it will be reported "as is"*/ + } + else + { + /*complete the XML*/ + if (STRING_concat(blockIDList, "</BlockList>") != 0) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to STRING_concat"); + result = BLOB_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_029: [Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string : base relativePath + "&comp=blocklist"]*/ + STRING_HANDLE newRelativePath = STRING_construct(relativePath); + if (newRelativePath == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to STRING_construct"); + result = BLOB_ERROR; + } + else + { + if (STRING_concat(newRelativePath, "&comp=blocklist") != 0) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to STRING_concat"); + result = BLOB_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_030: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing the new relativePath, httpStatus and httpResponse and the XML string as content. ]*/ + const char* s = STRING_c_str(blockIDList); + BUFFER_HANDLE blockIDListAsBuffer = BUFFER_create((const unsigned char*)s, strlen(s)); + if (blockIDListAsBuffer == NULL) + { + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("failed to BUFFER_create"); + result = BLOB_ERROR; + } + else + { + if (HTTPAPIEX_ExecuteRequest( + httpApiExHandle, + HTTPAPI_REQUEST_PUT, + STRING_c_str(newRelativePath), + NULL, + blockIDListAsBuffer, + httpStatus, + NULL, + httpResponse + ) != HTTPAPIEX_OK) + { + /*Codes_SRS_BLOB_02_031: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/ + LogError("unable to HTTPAPIEX_ExecuteRequest"); + result = BLOB_HTTP_ERROR; + } + else + { + /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/ + result = BLOB_OK; + } + BUFFER_delete(blockIDListAsBuffer); + } + } + STRING_delete(newRelativePath); + } + } + } + STRING_delete(blockIDList); + } + + } + HTTPAPIEX_Destroy(httpApiExHandle); + } + free(hostname); + } + } + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> + +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "iothub.h" + +int IoTHub_Init() +{ + int result; + if (platform_init() != 0) + { + LogError("Platform initialization failed"); + result = __FAILURE__; + } + else + { + result = 0; + } + return result; +} + +void IoTHub_Deinit() +{ + platform_deinit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/gballoc.h" + +#include "iothub_client_core.h" +#include "iothub_client.h" + +IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_CLIENT_HANDLE)IoTHubClientCore_CreateFromConnectionString(connectionString, protocol); +} + +IOTHUB_CLIENT_HANDLE IoTHubClient_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + return (IOTHUB_CLIENT_HANDLE)IoTHubClientCore_Create(config); +} + +IOTHUB_CLIENT_HANDLE IoTHubClient_CreateWithTransport(TRANSPORT_HANDLE transportHandle, const IOTHUB_CLIENT_CONFIG* config) +{ + return (IOTHUB_CLIENT_HANDLE)IoTHubClientCore_CreateWithTransport(transportHandle, config); +} + +IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromDeviceAuth(const char* iothub_uri, const char* device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_CLIENT_HANDLE)IoTHubClientCore_CreateFromDeviceAuth(iothub_uri, device_id, protocol); +} + +void IoTHubClient_Destroy(IOTHUB_CLIENT_HANDLE iotHubClientHandle) +{ + IoTHubClientCore_Destroy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SendEventAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendEventAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_GetSendStatus(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + return IoTHubClientCore_GetSendStatus((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, iotHubClientStatus); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetMessageCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, messageCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetConnectionStatusCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + return IoTHubClientCore_SetConnectionStatusCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, connectionStatusCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetRetryPolicy(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_SetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_GetRetryPolicy(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_GetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_GetLastMessageReceiveTime(IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) +{ + return IoTHubClientCore_GetLastMessageReceiveTime((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, lastMessageReceiveTime); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetOption(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value) +{ + return IoTHubClientCore_SetOption((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, optionName, value); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetDeviceTwinCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceTwinCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, deviceTwinCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SendReportedState(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendReportedState((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, reportedState, size, reportedStateCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetDeviceMethodCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceMethodCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, deviceMethodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetDeviceMethodCallback_Ex(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inboundDeviceMethodCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceMethodCallback_Ex((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, inboundDeviceMethodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_DeviceMethodResponse(IOTHUB_CLIENT_HANDLE iotHubClientHandle, METHOD_HANDLE methodId, const unsigned char* response, size_t respSize, int statusCode) +{ + return IoTHubClientCore_DeviceMethodResponse((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, methodId, response, respSize, statusCode); +} + +#ifndef DONT_USE_UPLOADTOBLOB + +IOTHUB_CLIENT_RESULT IoTHubClient_UploadToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback, void* context) +{ + return IoTHubClientCore_UploadToBlobAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, destinationFileName, source, size, iotHubClientFileUploadCallback, context); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +{ + return IoTHubClientCore_UploadMultipleBlocksToBlobAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, destinationFileName, getDataCallback, NULL, context); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsyncEx(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + return IoTHubClientCore_UploadMultipleBlocksToBlobAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, destinationFileName, NULL, getDataCallbackEx, context); +} + +#endif /*DONT_USE_UPLOADTOBLOB*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_authorization.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,555 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/sastoken.h" +#include "azure_c_shared_utility/shared_util_options.h" + +#ifdef USE_PROV_MODULE +#include "azure_prov_client/internal/iothub_auth_client.h" +#endif + +#include "internal/iothub_client_authorization.h" + +#define DEFAULT_SAS_TOKEN_EXPIRY_TIME_SECS 3600 +#define INDEFINITE_TIME ((time_t)(-1)) + +typedef struct IOTHUB_AUTHORIZATION_DATA_TAG +{ + char* device_sas_token; + char* device_key; + char* device_id; + char* module_id; + size_t token_expiry_time_sec; + IOTHUB_CREDENTIAL_TYPE cred_type; +#ifdef USE_PROV_MODULE + IOTHUB_SECURITY_HANDLE device_auth_handle; +#endif +} IOTHUB_AUTHORIZATION_DATA; + +static int get_seconds_since_epoch(size_t* seconds) +{ + int result; + time_t current_time; + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed getting the current local time (get_time() failed)"); + result = __FAILURE__; + } + else + { + *seconds = (size_t)get_difftime(current_time, (time_t)0); + result = 0; + } + return result; +} + +IOTHUB_AUTHORIZATION_HANDLE IoTHubClient_Auth_Create(const char* device_key, const char* device_id, const char* device_sas_token, const char *module_id) +{ + IOTHUB_AUTHORIZATION_DATA* result; + /* Codes_SRS_IoTHub_Authorization_07_001: [if device_id is NULL IoTHubClient_Auth_Create, shall return NULL. ] */ + if (device_id == NULL) + { + LogError("Invalid Parameter device_id: %p", device_id); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_002: [IoTHubClient_Auth_Create shall allocate a IOTHUB_AUTHORIZATION_HANDLE that is needed for subsequent calls. ] */ + result = (IOTHUB_AUTHORIZATION_DATA*)malloc(sizeof(IOTHUB_AUTHORIZATION_DATA) ); + if (result == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_019: [ On error IoTHubClient_Auth_Create shall return NULL. ] */ + LogError("Failed allocating IOTHUB_AUTHORIZATION_DATA"); + result = NULL; + } + else + { + memset(result, 0, sizeof(IOTHUB_AUTHORIZATION_DATA) ); + result->token_expiry_time_sec = DEFAULT_SAS_TOKEN_EXPIRY_TIME_SECS; + + if (device_key != NULL && mallocAndStrcpy_s(&result->device_key, device_key) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_019: [ On error IoTHubClient_Auth_Create shall return NULL. ] */ + LogError("Failed allocating device_key"); + free(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->device_id, device_id) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_019: [ On error IoTHubClient_Auth_Create shall return NULL. ] */ + LogError("Failed allocating device_key"); + free(result->device_key); + free(result); + result = NULL; + } + else if (module_id != NULL && mallocAndStrcpy_s(&result->module_id, module_id) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_019: [ On error IoTHubClient_Auth_Create shall return NULL. ] */ + LogError("Failed allocating module_id"); + free(result->device_id); + free(result->device_key); + free(result); + result = NULL; + } + else + { + if (device_key != NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_003: [ IoTHubClient_Auth_Create shall set the credential type to IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY if the device_sas_token is NULL. ]*/ + result->cred_type = IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY; + } + else if (device_sas_token != NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_020: [ else IoTHubClient_Auth_Create shall set the credential type to IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN. ] */ + result->cred_type = IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN; + if (mallocAndStrcpy_s(&result->device_sas_token, device_sas_token) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_019: [ On error IoTHubClient_Auth_Create shall return NULL. ] */ + LogError("Failed allocating device_key"); + free(result->device_key); + free(result->device_id); + free(result->module_id); + free(result); + result = NULL; + } + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_024: [ if device_sas_token and device_key are NULL IoTHubClient_Auth_Create shall set the credential type to IOTHUB_CREDENTIAL_TYPE_UNKNOWN. ] */ + result->cred_type = IOTHUB_CREDENTIAL_TYPE_UNKNOWN; + } + } + } + } + /* Codes_SRS_IoTHub_Authorization_07_004: [ If successful IoTHubClient_Auth_Create shall return a IOTHUB_AUTHORIZATION_HANDLE value. ] */ + return result; +} + +IOTHUB_AUTHORIZATION_HANDLE IoTHubClient_Auth_CreateFromDeviceAuth(const char* device_id, const char* module_id) +{ + IOTHUB_AUTHORIZATION_DATA* result; + if (device_id == NULL) + { + LogError("Invalid Parameter device_id: %p", device_id); + result = NULL; + } + else + { +#ifdef USE_PROV_MODULE + result = (IOTHUB_AUTHORIZATION_DATA*)malloc(sizeof(IOTHUB_AUTHORIZATION_DATA)); + if (result == NULL) + { + LogError("Failed allocating IOTHUB_AUTHORIZATION_DATA"); + result = NULL; + } + else + { + memset(result, 0, sizeof(IOTHUB_AUTHORIZATION_DATA)); + + result->device_auth_handle = iothub_device_auth_create(); + if (result->device_auth_handle == NULL) + { + LogError("Failed allocating IOTHUB_AUTHORIZATION_DATA"); + free(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->device_id, device_id) != 0) + { + LogError("Failed allocating device_id"); + iothub_device_auth_destroy(result->device_auth_handle); + free(result); + result = NULL; + } + else if ((module_id != NULL) && (mallocAndStrcpy_s(&result->module_id, module_id) != 0)) + { + LogError("Failed allocating module_id"); + iothub_device_auth_destroy(result->device_auth_handle); + free(result->device_id); + free(result); + result = NULL; + } + else + { + if (iothub_device_auth_get_type(result->device_auth_handle) == AUTH_TYPE_SAS) + { + result->cred_type = IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH; + } + else + { + result->cred_type = IOTHUB_CREDENTIAL_TYPE_X509_ECC; + } + } + } +#else + (void)module_id; + LogError("Failed HSM module is not supported"); + result = NULL; +#endif + } + return result; +} + +void IoTHubClient_Auth_Destroy(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + /* Codes_SRS_IoTHub_Authorization_07_005: [ if handle is NULL IoTHubClient_Auth_Destroy shall do nothing. ] */ + if (handle != NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_006: [ IoTHubClient_Auth_Destroy shall free all resources associated with the IOTHUB_AUTHORIZATION_HANDLE handle. ] */ +#ifdef USE_PROV_MODULE + iothub_device_auth_destroy(handle->device_auth_handle); +#endif + free(handle->device_key); + free(handle->device_id); + free(handle->module_id); + free(handle->device_sas_token); + free(handle); + } +} + +IOTHUB_CREDENTIAL_TYPE IoTHubClient_Auth_Set_x509_Type(IOTHUB_AUTHORIZATION_HANDLE handle, bool enable_x509) +{ + IOTHUB_CREDENTIAL_TYPE result; + if (handle != NULL) + { + if (enable_x509) + { + result = handle->cred_type = IOTHUB_CREDENTIAL_TYPE_X509; + } + else + { + if (handle->device_sas_token == NULL) + { + result = handle->cred_type = IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY; + } + else if (handle->device_key == NULL) + { + result = handle->cred_type = IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN; + } + else + { + result = handle->cred_type = IOTHUB_CREDENTIAL_TYPE_UNKNOWN; + } + } + } + else + { + result = IOTHUB_CREDENTIAL_TYPE_UNKNOWN; + } + return result; +} + +int IoTHubClient_Auth_Set_xio_Certificate(IOTHUB_AUTHORIZATION_HANDLE handle, XIO_HANDLE xio) +{ + int result; + if (handle == NULL || xio == NULL) + { + LogError("Invalid Parameter handle: %p xio: %p", handle, xio); + result = __FAILURE__; + } + else if (handle->cred_type != IOTHUB_CREDENTIAL_TYPE_X509_ECC) + { + LogError("Invalid credential types for this operation"); + result = __FAILURE__; + } + else + { +#ifdef USE_PROV_MODULE + CREDENTIAL_RESULT* cred_result = iothub_device_auth_generate_credentials(handle->device_auth_handle, NULL); + if (cred_result == NULL) + { + LogError("Failure generating credentials"); + result = __FAILURE__; + } + else + { + if (xio_setoption(xio, OPTION_X509_ECC_CERT, cred_result->auth_cred_result.x509_result.x509_cert) != 0) + { + LogError("Failure setting x509 cert on xio"); + result = __FAILURE__; + } + else if (xio_setoption(xio, OPTION_X509_ECC_KEY, cred_result->auth_cred_result.x509_result.x509_alias_key) != 0) + { + LogError("Failure setting x509 key on xio"); + result = __FAILURE__; + } + else + { + result = 0; + } + free(cred_result); + } +#else + LogError("Failed HSM module is not supported"); + result = __FAILURE__; +#endif + } + return result; +} + +IOTHUB_CREDENTIAL_TYPE IoTHubClient_Auth_Get_Credential_Type(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + IOTHUB_CREDENTIAL_TYPE result; + /* Codes_SRS_IoTHub_Authorization_07_007: [ if handle is NULL IoTHub_Auth_Get_Credential_Type shall return IOTHUB_CREDENTIAL_TYPE_UNKNOWN. ] */ + if (handle == NULL) + { + LogError("Invalid Parameter handle: %p", handle); + result = IOTHUB_CREDENTIAL_TYPE_UNKNOWN; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_008: [ IoTHub_Auth_Get_Credential_Type shall return the credential type that is set upon creation. ] */ + result = handle->cred_type; + } + return result; +} + +char* IoTHubClient_Auth_Get_SasToken(IOTHUB_AUTHORIZATION_HANDLE handle, const char* scope, size_t expiry_time_relative_seconds, const char* key_name) +{ + char* result; + /* Codes_SRS_IoTHub_Authorization_07_009: [ if handle or scope are NULL, IoTHubClient_Auth_Get_SasToken shall return NULL. ] */ + if (handle == NULL) + { + LogError("Invalid Parameter handle: %p", handle); + result = NULL; + } + else + { + if (handle->cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH) + { +#ifdef USE_PROV_MODULE + DEVICE_AUTH_CREDENTIAL_INFO dev_auth_cred; + size_t sec_since_epoch; + + if (get_seconds_since_epoch(&sec_since_epoch) != 0) + { + LogError("failure getting seconds from epoch"); + result = NULL; + } + else + { + memset(&dev_auth_cred, 0, sizeof(DEVICE_AUTH_CREDENTIAL_INFO)); + size_t expiry_time = sec_since_epoch+expiry_time_relative_seconds; + dev_auth_cred.sas_info.expiry_seconds = expiry_time; + dev_auth_cred.sas_info.token_scope = scope; + dev_auth_cred.sas_info.key_name = key_name; + dev_auth_cred.dev_auth_type = AUTH_TYPE_SAS; + + CREDENTIAL_RESULT* cred_result = iothub_device_auth_generate_credentials(handle->device_auth_handle, &dev_auth_cred); + if (cred_result == NULL) + { + LogError("failure getting credentials from device auth module"); + result = NULL; + } + else + { + if (mallocAndStrcpy_s(&result, cred_result->auth_cred_result.sas_result.sas_token) != 0) + { + LogError("failure allocating Sas Token"); + result = NULL; + } + free(cred_result); + } + } +#else + LogError("Failed HSM module is not supported"); + result = NULL; +#endif + } + else if (handle->cred_type == IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN) + { + /* Codes_SRS_IoTHub_Authorization_07_021: [If the device_sas_token is NOT NULL IoTHubClient_Auth_Get_SasToken shall return a copy of the device_sas_token. ] */ + if (handle->device_sas_token != NULL) + { + if (mallocAndStrcpy_s(&result, handle->device_sas_token) != 0) + { + LogError("failure allocating sas token"); + result = NULL; + } + } + else + { + LogError("failure device sas token is NULL"); + result = NULL; + } + } + else if (handle->cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY) + { + /* Codes_SRS_IoTHub_Authorization_07_009: [ if handle or scope are NULL, IoTHubClient_Auth_Get_SasToken shall return NULL. ] */ + if (scope == NULL) + { + LogError("Invalid Parameter scope: %p", scope); + result = NULL; + } + else + { + STRING_HANDLE sas_token; + size_t sec_since_epoch; + + /* Codes_SRS_IoTHub_Authorization_07_010: [ IoTHubClient_Auth_Get_SasToken` shall construct the expiration time using the expiry_time_relative_seconds added to epoch time. ] */ + if (get_seconds_since_epoch(&sec_since_epoch) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_020: [ If any error is encountered IoTHubClient_Auth_Get_ConnString shall return NULL. ] */ + LogError("failure getting seconds from epoch"); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_011: [ IoTHubClient_Auth_Get_ConnString shall call SASToken_CreateString to construct the sas token. ] */ + size_t expiry_time = sec_since_epoch+expiry_time_relative_seconds; + if ( (sas_token = SASToken_CreateString(handle->device_key, scope, key_name, expiry_time)) == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_020: [ If any error is encountered IoTHubClient_Auth_Get_ConnString shall return NULL. ] */ + LogError("Failed creating sas_token"); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_012: [ On success IoTHubClient_Auth_Get_ConnString shall allocate and return the sas token in a char*. ] */ + if (mallocAndStrcpy_s(&result, STRING_c_str(sas_token) ) != 0) + { + /* Codes_SRS_IoTHub_Authorization_07_020: [ If any error is encountered IoTHubClient_Auth_Get_ConnString shall return NULL. ] */ + LogError("Failed copying result"); + result = NULL; + } + STRING_delete(sas_token); + } + } + } + } + else + { + LogError("Failed getting sas token invalid credential type"); + result = NULL; + } + } + return result; +} + +const char* IoTHubClient_Auth_Get_DeviceId(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + const char* result; + if (handle == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_013: [ if handle is NULL, IoTHubClient_Auth_Get_DeviceId shall return NULL. ] */ + LogError("Invalid Parameter handle: %p", handle); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_014: [ IoTHubClient_Auth_Get_DeviceId shall return the device_id specified upon creation. ] */ + result = handle->device_id; + } + return result; +} + +const char* IoTHubClient_Auth_Get_ModuleId(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + const char* result; + if (handle == NULL) + { + /* Codes_SRS_IoTHub_Authorization_31_025: [ if handle is NULL, IoTHubClient_Auth_Get_ModuleId shall return NULL. ] */ + LogError("Invalid Parameter handle: %p", handle); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_31_026: [ IoTHubClient_Auth_Get_ModuleId shall return the module_id specified upon creation. ] */ + result = handle->module_id; + } + return result; +} + +const char* IoTHubClient_Auth_Get_DeviceKey(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + const char* result; + if (handle == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_022: [ if handle is NULL, IoTHubClient_Auth_Get_DeviceKey shall return NULL. ] */ + LogError("Invalid Parameter handle: %p", handle); + result = NULL; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_023: [ IoTHubClient_Auth_Get_DeviceKey shall return the device_key specified upon creation. ] */ + result = handle->device_key; + } + return result; +} + +SAS_TOKEN_STATUS IoTHubClient_Auth_Is_SasToken_Valid(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + SAS_TOKEN_STATUS result; + if (handle == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_015: [ if handle is NULL, IoTHubClient_Auth_Is_SasToken_Valid shall return false. ] */ + LogError("Invalid Parameter handle: %p", handle); + result = SAS_TOKEN_STATUS_FAILED; + } + else + { + if (handle->cred_type == IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN) + { + if (handle->device_sas_token == NULL) + { + /* Codes_SRS_IoTHub_Authorization_07_017: [ If the sas_token is NULL IoTHubClient_Auth_Is_SasToken_Valid shall return false. ] */ + LogError("Failure: device_sas_toke is NULL"); + result = SAS_TOKEN_STATUS_FAILED; + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_018: [ otherwise IoTHubClient_Auth_Is_SasToken_Valid shall return the value returned by SASToken_Validate. ] */ + STRING_HANDLE strSasToken = STRING_construct(handle->device_sas_token); + if (strSasToken != NULL) + { + if (!SASToken_Validate(strSasToken)) + { + result = SAS_TOKEN_STATUS_INVALID; + } + else + { + result = SAS_TOKEN_STATUS_VALID; + } + STRING_delete(strSasToken); + } + else + { + LogError("Failure constructing SAS Token"); + result = SAS_TOKEN_STATUS_FAILED; + } + } + } + else + { + /* Codes_SRS_IoTHub_Authorization_07_016: [ if credential type is not IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN IoTHubClient_Auth_Is_SasToken_Valid shall return SAS_TOKEN_STATUS_VALID. ] */ + result = SAS_TOKEN_STATUS_VALID; + } + } + return result; +} + + +#ifdef USE_EDGE_MODULES +char* IoTHubClient_Auth_Get_TrustBundle(IOTHUB_AUTHORIZATION_HANDLE handle) +{ + char* result; + if (handle == NULL) + { + LogError("Security Handle is NULL"); + result = NULL; + } + else + { + result = iothub_device_auth_get_trust_bundle(handle->device_auth_handle); + } + return result; +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_core.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2440 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/gballoc.h" + +#include <signal.h> +#include <stddef.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "iothub_client_core.h" +#include "iothub_client_core_ll.h" +#include "internal/iothubtransport.h" +#include "internal/iothub_client_private.h" +#include "internal/iothubtransport.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/vector.h" + +struct IOTHUB_QUEUE_CONTEXT_TAG; + +typedef struct IOTHUB_CLIENT_CORE_INSTANCE_TAG +{ + IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientLLHandle; + TRANSPORT_HANDLE TransportHandle; + THREAD_HANDLE ThreadHandle; + LOCK_HANDLE LockHandle; + sig_atomic_t StopThread; + SINGLYLINKEDLIST_HANDLE httpWorkerThreadInfoList; /*list containing HTTPWORKER_THREAD_INFO*/ + int created_with_transport_handle; + VECTOR_HANDLE saved_user_callback_list; + IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK desired_state_callback; + IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK event_confirm_callback; + IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reported_state_callback; + IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connection_status_callback; + IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC device_method_callback; + IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inbound_device_method_callback; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC message_callback; + struct IOTHUB_QUEUE_CONTEXT_TAG* devicetwin_user_context; + struct IOTHUB_QUEUE_CONTEXT_TAG* connection_status_user_context; + struct IOTHUB_QUEUE_CONTEXT_TAG* message_user_context; + struct IOTHUB_QUEUE_CONTEXT_TAG* method_user_context; +} IOTHUB_CLIENT_CORE_INSTANCE; + +typedef enum HTTPWORKER_THREAD_TYPE_TAG +{ + HTTPWORKER_THREAD_UPLOAD_TO_BLOB, + HTTPWORKER_THREAD_INVOKE_METHOD +} HTTPWORKER_THREAD_TYPE; + +typedef struct UPLOADTOBLOB_SAVED_DATA_TAG +{ + unsigned char* source; + size_t size; + IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback; +}UPLOADTOBLOB_SAVED_DATA; + +typedef struct UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA_TAG +{ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback; + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx; +}UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA; + +typedef struct INVOKE_METHOD_SAVED_DATA_TAG +{ + const char* deviceId; + const char* moduleId; + const char* methodName; + const char* methodPayload; + unsigned int timeout; + IOTHUB_METHOD_INVOKE_CALLBACK methodInvokeCallback; +} INVOKE_METHOD_SAVED_DATA; + +typedef struct HTTPWORKER_THREAD_INFO_TAG +{ + HTTPWORKER_THREAD_TYPE workerThreadType; + char* destinationFileName; + THREAD_HANDLE threadHandle; + LOCK_HANDLE lockGarbage; + int canBeGarbageCollected; /*flag indicating that the structure can be freed because the thread deadling with it finished*/ + IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle; + void* context; + UPLOADTOBLOB_SAVED_DATA uploadBlobSavedData; + INVOKE_METHOD_SAVED_DATA invokeMethodSavedData; + UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA uploadBlobMultiblockSavedData; +}HTTPWORKER_THREAD_INFO; + +#define USER_CALLBACK_TYPE_VALUES \ + CALLBACK_TYPE_DEVICE_TWIN, \ + CALLBACK_TYPE_EVENT_CONFIRM, \ + CALLBACK_TYPE_REPORTED_STATE, \ + CALLBACK_TYPE_CONNECTION_STATUS, \ + CALLBACK_TYPE_DEVICE_METHOD, \ + CALLBACK_TYPE_INBOUD_DEVICE_METHOD, \ + CALLBACK_TYPE_MESSAGE, \ + CALLBACK_TYPE_INPUTMESSAGE + +DEFINE_ENUM(USER_CALLBACK_TYPE, USER_CALLBACK_TYPE_VALUES) +DEFINE_ENUM_STRINGS(USER_CALLBACK_TYPE, USER_CALLBACK_TYPE_VALUES) + +typedef struct DEVICE_TWIN_CALLBACK_INFO_TAG +{ + DEVICE_TWIN_UPDATE_STATE update_state; + unsigned char* payLoad; + size_t size; +} DEVICE_TWIN_CALLBACK_INFO; + +typedef struct EVENT_CONFIRM_CALLBACK_INFO_TAG +{ + IOTHUB_CLIENT_CONFIRMATION_RESULT confirm_result; +} EVENT_CONFIRM_CALLBACK_INFO; + +typedef struct REPORTED_STATE_CALLBACK_INFO_TAG +{ + int status_code; +} REPORTED_STATE_CALLBACK_INFO; + +typedef struct CONNECTION_STATUS_CALLBACK_INFO_TAG +{ + IOTHUB_CLIENT_CONNECTION_STATUS connection_status; + IOTHUB_CLIENT_CONNECTION_STATUS_REASON status_reason; +} CONNECTION_STATUS_CALLBACK_INFO; + +typedef struct METHOD_CALLBACK_INFO_TAG +{ + STRING_HANDLE method_name; + BUFFER_HANDLE payload; + METHOD_HANDLE method_id; +} METHOD_CALLBACK_INFO; + +typedef struct INPUTMESSAGE_CALLBACK_INFO_TAG +{ + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback; + MESSAGE_CALLBACK_INFO* message_cb_info; +} INPUTMESSAGE_CALLBACK_INFO; + +typedef struct USER_CALLBACK_INFO_TAG +{ + USER_CALLBACK_TYPE type; + void* userContextCallback; + union IOTHUB_CALLBACK + { + DEVICE_TWIN_CALLBACK_INFO dev_twin_cb_info; + EVENT_CONFIRM_CALLBACK_INFO event_confirm_cb_info; + REPORTED_STATE_CALLBACK_INFO reported_state_cb_info; + CONNECTION_STATUS_CALLBACK_INFO connection_status_cb_info; + METHOD_CALLBACK_INFO method_cb_info; + MESSAGE_CALLBACK_INFO* message_cb_info; + INPUTMESSAGE_CALLBACK_INFO inputmessage_cb_info; + } iothub_callback; +} USER_CALLBACK_INFO; + +typedef struct IOTHUB_QUEUE_CONTEXT_TAG +{ + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientHandle; + void* userContextCallback; +} IOTHUB_QUEUE_CONTEXT; + +typedef struct IOTHUB_INPUTMESSAGE_CALLBACK_CONTEXT_TAG +{ + IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback; + void* userContextCallback; +} IOTHUB_INPUTMESSAGE_CALLBACK_CONTEXT; + +/*used by unittests only*/ +const size_t IoTHubClientCore_ThreadTerminationOffset = offsetof(IOTHUB_CLIENT_CORE_INSTANCE, StopThread); + +typedef enum CREATE_HUB_INSTANCE_TYPE_TAG +{ + CREATE_HUB_INSTANCE_FROM_CONNECTION_STRING, + CREATE_HUB_INSTANCE_FROM_EDGE_ENVIRONMENT, + CREATE_HUB_INSTANCE_FROM_TRANSPORT, + CREATE_HUB_INSTANCE_FROM_CLIENT_CONFIG, + CREATE_HUB_INSTANCE_FROM_DEVICE_AUTH +} CREATE_HUB_INSTANCE_TYPE; + +static void freeHttpWorkerThreadInfo(HTTPWORKER_THREAD_INFO* threadInfo) +{ + Lock_Deinit(threadInfo->lockGarbage); + if (threadInfo->workerThreadType == HTTPWORKER_THREAD_UPLOAD_TO_BLOB) + { + free(threadInfo->uploadBlobSavedData.source); + free(threadInfo->destinationFileName); + } + else if (threadInfo->workerThreadType == HTTPWORKER_THREAD_INVOKE_METHOD) + { + free((char*)threadInfo->invokeMethodSavedData.deviceId); + free((char*)threadInfo->invokeMethodSavedData.moduleId); + free((char*)threadInfo->invokeMethodSavedData.methodName); + free((char*)threadInfo->invokeMethodSavedData.methodPayload); + } + + free(threadInfo); +} + +/*this function is called from _Destroy and from ScheduleWork_Thread to join finished blobUpload threads and free that memory*/ +static void garbageCollectorImpl(IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance) +{ + /*see if any savedData structures can be disposed of*/ + /*Codes_SRS_IOTHUBCLIENT_02_072: [ All threads marked as disposable (upon completion of a file upload) shall be joined and the data structures build for them shall be freed. ]*/ + LIST_ITEM_HANDLE item = singlylinkedlist_get_head_item(iotHubClientInstance->httpWorkerThreadInfoList); + while (item != NULL) + { + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)singlylinkedlist_item_get_value(item); + LIST_ITEM_HANDLE old_item = item; + item = singlylinkedlist_get_next_item(item); + + if (Lock(threadInfo->lockGarbage) != LOCK_OK) + { + LogError("unable to Lock"); + } + else + { + if (threadInfo->canBeGarbageCollected == 1) + { + int notUsed; + if (ThreadAPI_Join(threadInfo->threadHandle, ¬Used) != THREADAPI_OK) + { + LogError("unable to ThreadAPI_Join"); + } + (void)singlylinkedlist_remove(iotHubClientInstance->httpWorkerThreadInfoList, old_item); + + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) + { + LogError("unable to unlock after locking"); + } + freeHttpWorkerThreadInfo(threadInfo); + } + else + { + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) + { + LogError("unable to unlock after locking"); + } + } + } + } +} + + +static bool iothub_ll_message_callback(MESSAGE_CALLBACK_INFO* messageData, void* userContextCallback) +{ + bool result; + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context == NULL) + { + LogError("invalid parameter userContextCallback(NULL)"); + result = false; + } + else + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_MESSAGE; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.message_cb_info = messageData; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) == 0) + { + result = true; + } + else + { + LogError("message callback vector push failed."); + result = false; + } + } + return result; +} + +static bool iothub_ll_inputmessage_callback(MESSAGE_CALLBACK_INFO* message_cb_info, void* userContextCallback) +{ + bool result; + IOTHUB_INPUTMESSAGE_CALLBACK_CONTEXT *inputMessageCallbackContext = (IOTHUB_INPUTMESSAGE_CALLBACK_CONTEXT *)userContextCallback; + if (inputMessageCallbackContext == NULL) + { + LogError("invalid parameter userContextCallback(NULL)"); + result = false; + } + else + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_INPUTMESSAGE; + queue_cb_info.userContextCallback = inputMessageCallbackContext->userContextCallback; + queue_cb_info.iothub_callback.inputmessage_cb_info.eventHandlerCallback = inputMessageCallbackContext->eventHandlerCallback; + queue_cb_info.iothub_callback.inputmessage_cb_info.message_cb_info = message_cb_info; + + if (VECTOR_push_back(inputMessageCallbackContext->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) == 0) + { + result = true; + } + else + { + LogError("message callback vector push failed."); + result = false; + } + } + + return result; +} + +static int make_method_calback_queue_context(USER_CALLBACK_INFO* queue_cb_info, const char* method_name, const unsigned char* payload, size_t size, METHOD_HANDLE method_id, IOTHUB_QUEUE_CONTEXT* queue_context) +{ + int result; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [ IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall copy the method_name and payload. ] */ + queue_cb_info->userContextCallback = queue_context->userContextCallback; + queue_cb_info->iothub_callback.method_cb_info.method_id = method_id; + if ((queue_cb_info->iothub_callback.method_cb_info.method_name = STRING_construct(method_name)) == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [ If a failure is encountered IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall return a non-NULL value. ]*/ + LogError("STRING_construct failed"); + result = __FAILURE__; + } + else + { + if ((queue_cb_info->iothub_callback.method_cb_info.payload = BUFFER_create(payload, size)) == NULL) + { + STRING_delete(queue_cb_info->iothub_callback.method_cb_info.method_name); + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [ If a failure is encountered IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall return a non-NULL value. ]*/ + LogError("BUFFER_create failed"); + result = __FAILURE__; + } + else + { + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, queue_cb_info, 1) == 0) + { + result = 0; + } + else + { + STRING_delete(queue_cb_info->iothub_callback.method_cb_info.method_name); + BUFFER_delete(queue_cb_info->iothub_callback.method_cb_info.payload); + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [ If a failure is encountered IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall return a non-NULL value. ]*/ + LogError("VECTOR_push_back failed"); + result = __FAILURE__; + } + } + } + return result; +} + +static int iothub_ll_device_method_callback(const char* method_name, const unsigned char* payload, size_t size, METHOD_HANDLE method_id, void* userContextCallback) +{ + int result; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_001: [ if userContextCallback is NULL, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall return a nonNULL value. ] */ + if (userContextCallback == NULL) + { + LogError("invalid parameter userContextCallback(NULL)"); + result = __FAILURE__; + } + else + { + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_DEVICE_METHOD; + + result = make_method_calback_queue_context(&queue_cb_info, method_name, payload, size, method_id, queue_context); + if (result != 0) + { + LogError("construction of method calback queue context failed"); + result = __FAILURE__; + } + } + return result; +} + +static int iothub_ll_inbound_device_method_callback(const char* method_name, const unsigned char* payload, size_t size, METHOD_HANDLE method_id, void* userContextCallback) +{ + int result; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_001: [ if userContextCallback is NULL, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK shall return a nonNULL value. ] */ + if (userContextCallback == NULL) + { + LogError("invalid parameter userContextCallback(NULL)"); + result = __FAILURE__; + } + else + { + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_INBOUD_DEVICE_METHOD; + + result = make_method_calback_queue_context(&queue_cb_info, method_name, payload, size, method_id, queue_context); + if (result != 0) + { + LogError("construction of method calback queue context failed"); + result = __FAILURE__; + } + } + return result; +} + +static void iothub_ll_connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_CONNECTION_STATUS; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.connection_status_cb_info.status_reason = reason; + queue_cb_info.iothub_callback.connection_status_cb_info.connection_status = result; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("connection status callback vector push failed."); + } + } +} + +static void iothub_ll_event_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_EVENT_CONFIRM; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.event_confirm_cb_info.confirm_result = result; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("event confirm callback vector push failed."); + } + free(queue_context); + } +} + +static void iothub_ll_reported_state_callback(int status_code, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_REPORTED_STATE; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.reported_state_cb_info.status_code = status_code; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("reported state callback vector push failed."); + } + free(queue_context); + } +} + +static void iothub_ll_device_twin_callback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + int push_to_vector; + + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_DEVICE_TWIN; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.dev_twin_cb_info.update_state = update_state; + if (payLoad == NULL) + { + queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad = NULL; + queue_cb_info.iothub_callback.dev_twin_cb_info.size = 0; + push_to_vector = 0; + } + else + { + queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad = (unsigned char*)malloc(size); + if (queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad == NULL) + { + LogError("failure allocating payload in device twin callback."); + queue_cb_info.iothub_callback.dev_twin_cb_info.size = 0; + push_to_vector = __FAILURE__; + } + else + { + (void)memcpy(queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad, payLoad, size); + queue_cb_info.iothub_callback.dev_twin_cb_info.size = size; + push_to_vector = 0; + } + } + if (push_to_vector == 0) + { + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + if (queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad != NULL) + { + free(queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad); + } + LogError("device twin callback userContextCallback vector push failed."); + } + } + } + else + { + LogError("device twin callback userContextCallback NULL"); + } +} + +static void dispatch_user_callbacks(IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance, VECTOR_HANDLE call_backs) +{ + size_t callbacks_length = VECTOR_size(call_backs); + size_t index; + + IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK desired_state_callback = NULL; + IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK event_confirm_callback = NULL; + IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reported_state_callback = NULL; + IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connection_status_callback = NULL; + IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC device_method_callback = NULL; + IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inbound_device_method_callback = NULL; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC message_callback = NULL; + IOTHUB_CLIENT_CORE_HANDLE message_user_context_handle = NULL; + IOTHUB_CLIENT_CORE_HANDLE method_user_context_handle = NULL; + + // Make a local copy of these callbacks, as we don't run with a lock held and iotHubClientInstance may change mid-run. + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("failed locking for dispatch_user_callbacks"); + } + else + { + desired_state_callback = iotHubClientInstance->desired_state_callback; + event_confirm_callback = iotHubClientInstance->event_confirm_callback; + reported_state_callback = iotHubClientInstance->reported_state_callback; + connection_status_callback = iotHubClientInstance->connection_status_callback; + device_method_callback = iotHubClientInstance->device_method_callback; + inbound_device_method_callback = iotHubClientInstance->inbound_device_method_callback; + message_callback = iotHubClientInstance->message_callback; + if (iotHubClientInstance->method_user_context) + { + method_user_context_handle = iotHubClientInstance->method_user_context->iotHubClientHandle; + } + if (iotHubClientInstance->message_user_context) + { + message_user_context_handle = iotHubClientInstance->message_user_context->iotHubClientHandle; + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + + + for (index = 0; index < callbacks_length; index++) + { + USER_CALLBACK_INFO* queued_cb = (USER_CALLBACK_INFO*)VECTOR_element(call_backs, index); + if (queued_cb == NULL) + { + LogError("VECTOR_element at index %zd is NULL.", index); + } + else + { + switch (queued_cb->type) + { + case CALLBACK_TYPE_DEVICE_TWIN: + { + if (desired_state_callback) + { + desired_state_callback(queued_cb->iothub_callback.dev_twin_cb_info.update_state, queued_cb->iothub_callback.dev_twin_cb_info.payLoad, queued_cb->iothub_callback.dev_twin_cb_info.size, queued_cb->userContextCallback); + } + + if (queued_cb->iothub_callback.dev_twin_cb_info.payLoad) + { + free(queued_cb->iothub_callback.dev_twin_cb_info.payLoad); + } + break; + } + case CALLBACK_TYPE_EVENT_CONFIRM: + if (event_confirm_callback) + { + event_confirm_callback(queued_cb->iothub_callback.event_confirm_cb_info.confirm_result, queued_cb->userContextCallback); + } + break; + case CALLBACK_TYPE_REPORTED_STATE: + if (reported_state_callback) + { + reported_state_callback(queued_cb->iothub_callback.reported_state_cb_info.status_code, queued_cb->userContextCallback); + } + break; + case CALLBACK_TYPE_CONNECTION_STATUS: + if (connection_status_callback) + { + connection_status_callback(queued_cb->iothub_callback.connection_status_cb_info.connection_status, queued_cb->iothub_callback.connection_status_cb_info.status_reason, queued_cb->userContextCallback); + } + break; + case CALLBACK_TYPE_DEVICE_METHOD: + if (device_method_callback) + { + const char* method_name = STRING_c_str(queued_cb->iothub_callback.method_cb_info.method_name); + const unsigned char* payload = BUFFER_u_char(queued_cb->iothub_callback.method_cb_info.payload); + size_t payload_len = BUFFER_length(queued_cb->iothub_callback.method_cb_info.payload); + + unsigned char* payload_resp = NULL; + size_t response_size = 0; + int status = device_method_callback(method_name, payload, payload_len, &payload_resp, &response_size, queued_cb->userContextCallback); + + if (payload_resp && (response_size > 0)) + { + IOTHUB_CLIENT_RESULT result = IoTHubClientCore_DeviceMethodResponse(method_user_context_handle, queued_cb->iothub_callback.method_cb_info.method_id, (const unsigned char*)payload_resp, response_size, status); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_DeviceMethodResponse failed"); + } + } + + BUFFER_delete(queued_cb->iothub_callback.method_cb_info.payload); + STRING_delete(queued_cb->iothub_callback.method_cb_info.method_name); + + if (payload_resp) + { + free(payload_resp); + } + } + break; + case CALLBACK_TYPE_INBOUD_DEVICE_METHOD: + if (inbound_device_method_callback) + { + const char* method_name = STRING_c_str(queued_cb->iothub_callback.method_cb_info.method_name); + const unsigned char* payload = BUFFER_u_char(queued_cb->iothub_callback.method_cb_info.payload); + size_t payload_len = BUFFER_length(queued_cb->iothub_callback.method_cb_info.payload); + + inbound_device_method_callback(method_name, payload, payload_len, queued_cb->iothub_callback.method_cb_info.method_id, queued_cb->userContextCallback); + + BUFFER_delete(queued_cb->iothub_callback.method_cb_info.payload); + STRING_delete(queued_cb->iothub_callback.method_cb_info.method_name); + } + break; + case CALLBACK_TYPE_MESSAGE: + if (message_callback) + { + IOTHUBMESSAGE_DISPOSITION_RESULT disposition = message_callback(queued_cb->iothub_callback.message_cb_info->messageHandle, queued_cb->userContextCallback); + + if (Lock(message_user_context_handle->LockHandle) == LOCK_OK) + { + IOTHUB_CLIENT_RESULT result = IoTHubClientCore_LL_SendMessageDisposition(message_user_context_handle->IoTHubClientLLHandle, queued_cb->iothub_callback.message_cb_info, disposition); + (void)Unlock(message_user_context_handle->LockHandle); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SendMessageDisposition failed"); + } + } + else + { + LogError("Lock failed"); + } + } + break; + + case CALLBACK_TYPE_INPUTMESSAGE: + { + const INPUTMESSAGE_CALLBACK_INFO *inputmessage_cb_info = &queued_cb->iothub_callback.inputmessage_cb_info; + IOTHUBMESSAGE_DISPOSITION_RESULT disposition = inputmessage_cb_info->eventHandlerCallback(inputmessage_cb_info->message_cb_info->messageHandle, queued_cb->userContextCallback); + + if (Lock(iotHubClientInstance->LockHandle) == LOCK_OK) + { + IOTHUB_CLIENT_RESULT result = IoTHubClientCore_LL_SendMessageDisposition(iotHubClientInstance->IoTHubClientLLHandle, inputmessage_cb_info->message_cb_info, disposition); + (void)Unlock(iotHubClientInstance->LockHandle); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClient_LL_SendMessageDisposition failed"); + } + } + else + { + LogError("Lock failed"); + } + } + break; + + default: + LogError("Invalid callback type '%s'", ENUM_TO_STRING(USER_CALLBACK_TYPE, queued_cb->type)); + break; + } + } + } + VECTOR_destroy(call_backs); +} + +static void ScheduleWork_Thread_ForMultiplexing(void* iotHubClientHandle) +{ + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + garbageCollectorImpl(iotHubClientInstance); + if (Lock(iotHubClientInstance->LockHandle) == LOCK_OK) + { + VECTOR_HANDLE call_backs = VECTOR_move(iotHubClientInstance->saved_user_callback_list); + (void)Unlock(iotHubClientInstance->LockHandle); + + if (call_backs == NULL) + { + LogError("Failed moving user callbacks"); + } + else + { + dispatch_user_callbacks(iotHubClientInstance, call_backs); + } + } + else + { + LogError("failed locking for ScheduleWork_Thread_ForMultiplexing"); + } +} + +static int ScheduleWork_Thread(void* threadArgument) +{ + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)threadArgument; + + while (1) + { + if (Lock(iotHubClientInstance->LockHandle) == LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_01_038: [ The thread shall exit when IoTHubClient_Destroy is called. ]*/ + if (iotHubClientInstance->StopThread) + { + (void)Unlock(iotHubClientInstance->LockHandle); + break; /*gets out of the thread*/ + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_037: [The thread created by IoTHubClient_SendEvent or IoTHubClient_SetMessageCallback shall call IoTHubClientCore_LL_DoWork every 1 ms.] */ + /* Codes_SRS_IOTHUBCLIENT_01_039: [All calls to IoTHubClientCore_LL_DoWork shall be protected by the lock created in IotHubClient_Create.] */ + IoTHubClientCore_LL_DoWork(iotHubClientInstance->IoTHubClientLLHandle); + + garbageCollectorImpl(iotHubClientInstance); + VECTOR_HANDLE call_backs = VECTOR_move(iotHubClientInstance->saved_user_callback_list); + (void)Unlock(iotHubClientInstance->LockHandle); + if (call_backs == NULL) + { + LogError("VECTOR_move failed"); + } + else + { + dispatch_user_callbacks(iotHubClientInstance, call_backs); + } + } + } + else + { + /*Codes_SRS_IOTHUBCLIENT_01_040: [If acquiring the lock fails, IoTHubClientCore_LL_DoWork shall not be called.]*/ + /*no code, shall retry*/ + } + (void)ThreadAPI_Sleep(1); + } + + ThreadAPI_Exit(0); + return 0; +} + +static IOTHUB_CLIENT_RESULT StartWorkerThreadIfNeeded(IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubClientInstance->TransportHandle == NULL) + { + if (iotHubClientInstance->ThreadHandle == NULL) + { + iotHubClientInstance->StopThread = 0; + if (ThreadAPI_Create(&iotHubClientInstance->ThreadHandle, ScheduleWork_Thread, iotHubClientInstance) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + iotHubClientInstance->ThreadHandle = NULL; + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else + { + /*Codes_SRS_IOTHUBCLIENT_17_012: [ If the transport connection is shared, the thread shall be started by calling IoTHubTransport_StartWorkerThread. ]*/ + /*Codes_SRS_IOTHUBCLIENT_17_011: [ If the transport connection is shared, the thread shall be started by calling IoTHubTransport_StartWorkerThread*/ + result = IoTHubTransport_StartWorkerThread(iotHubClientInstance->TransportHandle, iotHubClientInstance, ScheduleWork_Thread_ForMultiplexing); + } + return result; +} + +static IOTHUB_CLIENT_CORE_INSTANCE* create_iothub_instance(CREATE_HUB_INSTANCE_TYPE create_hub_instance_type, const IOTHUB_CLIENT_CONFIG* config, TRANSPORT_HANDLE transportHandle, const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol, const char* iothub_uri, const char* device_id) +{ + /* Codes_SRS_IOTHUBCLIENT_12_020: [** `IoTHubClient_CreateFromDeviceAuth` shall allocate a new `IoTHubClient` instance. **] */ + IOTHUB_CLIENT_CORE_INSTANCE* result = (IOTHUB_CLIENT_CORE_INSTANCE*)malloc(sizeof(IOTHUB_CLIENT_CORE_INSTANCE)); + (void)create_hub_instance_type; + + /* Codes_SRS_IOTHUBCLIENT_12_021: [** If allocating memory for the new `IoTHubClient` instance fails, then `IoTHubClient_CreateFromDeviceAuth` shall return `NULL`. **] */ + /* Codes_SRS_IOTHUBCLIENT_01_004: [If allocating memory for the new IoTHubClient instance fails, then IoTHubClient_Create shall return NULL.] */ + if (result != NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_029: [IoTHubClient_Create shall create a lock object to be used later for serializing IoTHubClient calls.] */ + if ((result->saved_user_callback_list = VECTOR_create(sizeof(USER_CALLBACK_INFO))) == NULL) + { + LogError("Failed creating VECTOR"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_02_060: [ IoTHubClient_Create shall create a SINGLYLINKEDLIST_HANDLE containing THREAD_HANDLE (created by future calls to IoTHubClient_UploadToBlobAsync). ]*/ + if ((result->httpWorkerThreadInfoList = singlylinkedlist_create()) == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_02_061: [ If creating the SINGLYLINKEDLIST_HANDLE fails then IoTHubClient_Create shall fail and return NULL. ]*/ + LogError("unable to singlylinkedlist_create"); + VECTOR_destroy(result->saved_user_callback_list); + free(result); + result = NULL; + } + else + { + result->TransportHandle = transportHandle; + result->created_with_transport_handle = 0; + if (config != NULL) + { + if (transportHandle != NULL) + { + /*Codes_SRS_IOTHUBCLIENT_17_005: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLock to get the transport lock to be used later for serializing IoTHubClient calls. ]*/ + result->LockHandle = IoTHubTransport_GetLock(transportHandle); + if (result->LockHandle == NULL) + { + LogError("unable to IoTHubTransport_GetLock"); + result->IoTHubClientLLHandle = NULL; + } + else + { + IOTHUB_CLIENT_DEVICE_CONFIG deviceConfig; + deviceConfig.deviceId = config->deviceId; + deviceConfig.deviceKey = config->deviceKey; + deviceConfig.protocol = config->protocol; + deviceConfig.deviceSasToken = config->deviceSasToken; + + /*Codes_SRS_IOTHUBCLIENT_17_003: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLLTransport on transportHandle to get lower layer transport. ]*/ + deviceConfig.transportHandle = IoTHubTransport_GetLLTransport(transportHandle); + if (deviceConfig.transportHandle == NULL) + { + LogError("unable to IoTHubTransport_GetLLTransport"); + result->IoTHubClientLLHandle = NULL; + } + else + { + if (Lock(result->LockHandle) != LOCK_OK) + { + LogError("unable to Lock"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_17_007: [ IoTHubClient_CreateWithTransport shall instantiate a new IoTHubClientCore_LL instance by calling IoTHubClientCore_LL_CreateWithTransport and passing the lower layer transport and config argument. ]*/ + result->IoTHubClientLLHandle = IoTHubClientCore_LL_CreateWithTransport(&deviceConfig); + result->created_with_transport_handle = 1; + if (Unlock(result->LockHandle) != LOCK_OK) + { + LogError("unable to Unlock"); + result->IoTHubClientLLHandle = NULL; + } + } + } + } + } + else + { + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_002: [IoTHubClient_Create shall instantiate a new IoTHubClientCore_LL instance by calling IoTHubClientCore_LL_Create and passing the config argument.] */ + result->IoTHubClientLLHandle = IoTHubClientCore_LL_Create(config); + } + } + } + else if (iothub_uri != NULL) + { +#ifdef USE_PROV_MODULE + /* Codes_SRS_IOTHUBCLIENT_12_022: [** `IoTHubClient_CreateFromDeviceAuth` shall create a lock object to be used later for serializing IoTHubClient calls. **] */ + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_12_023: [** If creating the lock fails, then IoTHubClient_CreateFromDeviceAuth shall return NULL. **] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_12_025: [** `IoTHubClient_CreateFromDeviceAuth` shall instantiate a new `IoTHubClientCore_LL` instance by calling `IoTHubClientCore_LL_CreateFromDeviceAuth` and passing iothub_uri, device_id and protocol argument. **] */ + result->IoTHubClientLLHandle = IoTHubClientCore_LL_CreateFromDeviceAuth(iothub_uri, device_id, protocol); + } +#else + (void)device_id; + LogError("Provisioning is not enabled for the build"); + result->IoTHubClientLLHandle = NULL; +#endif + } +#ifdef USE_EDGE_MODULES + else if (create_hub_instance_type == CREATE_HUB_INSTANCE_FROM_EDGE_ENVIRONMENT) + { + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + result->IoTHubClientLLHandle = IoTHubClientCore_LL_CreateFromEnvironment(protocol); + } + } +#endif + else + { + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + result->IoTHubClientLLHandle = IoTHubClientCore_LL_CreateFromConnectionString(connectionString, protocol); + } + } + + if (result->IoTHubClientLLHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_003: [If IoTHubClientCore_LL_Create fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + /* Codes_SRS_IOTHUBCLIENT_17_006: [ If IoTHubTransport_GetLock fails, then IoTHubClient_CreateWithTransport shall return NULL. ]*/ + if (transportHandle == NULL) + { + Lock_Deinit(result->LockHandle); + } + singlylinkedlist_destroy(result->httpWorkerThreadInfoList); + LogError("Failure creating iothub handle"); + VECTOR_destroy(result->saved_user_callback_list); + free(result); + result = NULL; + } + else + { + result->ThreadHandle = NULL; + result->desired_state_callback = NULL; + result->event_confirm_callback = NULL; + result->reported_state_callback = NULL; + result->devicetwin_user_context = NULL; + result->connection_status_callback = NULL; + result->connection_status_user_context = NULL; + result->message_callback = NULL; + result->message_user_context = NULL; + result->method_user_context = NULL; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_CORE_HANDLE IoTHubClientCore_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_CORE_INSTANCE* result; + + if (connectionString == NULL) + { + LogError("Input parameter is NULL: connectionString"); + result = NULL; + } + else if (protocol == NULL) + { + LogError("Input parameter is NULL: protocol"); + result = NULL; + } + else + { + result = create_iothub_instance(CREATE_HUB_INSTANCE_FROM_CONNECTION_STRING, NULL, NULL, connectionString, protocol, NULL, NULL); + } + return result; +} + +IOTHUB_CLIENT_CORE_HANDLE IoTHubClientCore_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + IOTHUB_CLIENT_CORE_INSTANCE* result; + if (config == NULL) + { + LogError("Input parameter is NULL: IOTHUB_CLIENT_CONFIG"); + result = NULL; + } + else + { + result = create_iothub_instance(CREATE_HUB_INSTANCE_FROM_CLIENT_CONFIG, config, NULL, NULL, NULL, NULL, NULL); + } + return result; +} + +IOTHUB_CLIENT_CORE_HANDLE IoTHubClientCore_CreateWithTransport(TRANSPORT_HANDLE transportHandle, const IOTHUB_CLIENT_CONFIG* config) +{ + IOTHUB_CLIENT_CORE_INSTANCE* result; + /*Codes_SRS_IOTHUBCLIENT_17_013: [ IoTHubClient_CreateWithTransport shall return NULL if transportHandle is NULL. ]*/ + /*Codes_SRS_IOTHUBCLIENT_17_014: [ IoTHubClient_CreateWithTransport shall return NULL if config is NULL. ]*/ + if (transportHandle == NULL || config == NULL) + { + LogError("invalid parameter TRANSPORT_HANDLE transportHandle=%p, const IOTHUB_CLIENT_CONFIG* config=%p", transportHandle, config); + result = NULL; + } + else + { + result = create_iothub_instance(CREATE_HUB_INSTANCE_FROM_TRANSPORT, config, transportHandle, NULL, NULL, NULL, NULL); + } + return result; +} + +IOTHUB_CLIENT_CORE_HANDLE IoTHubClientCore_CreateFromDeviceAuth(const char* iothub_uri, const char* device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_CORE_INSTANCE* result; + + /* Codes_SRS_IOTHUBCLIENT_12_019: [** `IoTHubClient_CreateFromDeviceAuth` shall verify the input parameters and if any of them `NULL` then return `NULL`. **] */ + if (iothub_uri == NULL) + { + LogError("Input parameter is NULL: iothub_uri"); + result = NULL; + } + else if (device_id == NULL) + { + LogError("Input parameter is NULL: device_id"); + result = NULL; + } + else if (protocol == NULL) + { + LogError("Input parameter is NULL: protocol"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_12_020: [** `IoTHubClient_CreateFromDeviceAuth` shall allocate a new `IoTHubClient` instance. **] */ + /* Codes_SRS_IOTHUBCLIENT_12_021: [** If allocating memory for the new `IoTHubClient` instance fails, then `IoTHubClient_CreateFromDeviceAuth` shall return `NULL`. **] */ + /* Codes_SRS_IOTHUBCLIENT_12_022: [** `IoTHubClient_CreateFromDeviceAuth` shall create a lock object to be used later for serializing IoTHubClient calls. **] */ + /* Codes_SRS_IOTHUBCLIENT_12_023: [** If creating the lock fails, then IoTHubClient_CreateFromDeviceAuth shall return NULL. **] */ + /* Codes_SRS_IOTHUBCLIENT_12_024: [** If IoTHubClient_CreateFromDeviceAuth fails, all resources allocated by it shall be freed. **] */ + /* Codes_SRS_IOTHUBCLIENT_12_025: [** `IoTHubClient_CreateFromDeviceAuth` shall instantiate a new `IoTHubClientCore_LL` instance by calling `IoTHubClientCore_LL_CreateFromDeviceAuth` and passing iothub_uri, device_id and protocol argument. **] */ + result = create_iothub_instance(CREATE_HUB_INSTANCE_FROM_DEVICE_AUTH, NULL, NULL, NULL, protocol, iothub_uri, device_id); + } + return result; +} + +#ifdef USE_EDGE_MODULES +IOTHUB_CLIENT_CORE_HANDLE IoTHubClientCore_CreateFromEnvironment(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return create_iothub_instance(CREATE_HUB_INSTANCE_FROM_EDGE_ENVIRONMENT, NULL, NULL, NULL, protocol, NULL, NULL); +} +#endif + + +/* Codes_SRS_IOTHUBCLIENT_01_005: [IoTHubClient_Destroy shall free all resources associated with the iotHubClientHandle instance.] */ +void IoTHubClientCore_Destroy(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle) +{ + /* Codes_SRS_IOTHUBCLIENT_01_008: [IoTHubClient_Destroy shall do nothing if parameter iotHubClientHandle is NULL.] */ + if (iotHubClientHandle != NULL) + { + bool joinClientThread; + bool joinTransportThread; + size_t vector_size; + + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + if (iotHubClientInstance->TransportHandle != NULL) + { + /*Codes_SRS_IOTHUBCLIENT_01_007: [ The thread created as part of executing IoTHubClient_SendEventAsync or IoTHubClient_SetNotificationMessageCallback shall be joined. ]*/ + joinTransportThread = IoTHubTransport_SignalEndWorkerThread(iotHubClientInstance->TransportHandle, iotHubClientHandle); + } + else + { + joinTransportThread = false; + } + + /*Codes_SRS_IOTHUBCLIENT_02_043: [ IoTHubClient_Destroy shall lock the serializing lock and signal the worker thread (if any) to end ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("unable to Lock - - will still proceed to try to end the thread without locking"); + } + + if (iotHubClientInstance->ThreadHandle != NULL) + { + iotHubClientInstance->StopThread = 1; + joinClientThread = true; + } + else + { + joinClientThread = false; + } + + /*Codes_SRS_IOTHUBCLIENT_02_045: [ IoTHubClient_Destroy shall unlock the serializing lock. ]*/ + if (Unlock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("unable to Unlock"); + } + + if (joinClientThread == true) + { + int res; + /*Codes_SRS_IOTHUBCLIENT_01_007: [ The thread created as part of executing IoTHubClient_SendEventAsync or IoTHubClient_SetNotificationMessageCallback shall be joined. ]*/ + if (ThreadAPI_Join(iotHubClientInstance->ThreadHandle, &res) != THREADAPI_OK) + { + LogError("ThreadAPI_Join failed"); + } + } + + if (joinTransportThread == true) + { + /*Codes_SRS_IOTHUBCLIENT_01_007: [ The thread created as part of executing IoTHubClient_SendEventAsync or IoTHubClient_SetNotificationMessageCallback shall be joined. ]*/ + IoTHubTransport_JoinWorkerThread(iotHubClientInstance->TransportHandle, iotHubClientHandle); + } + + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("unable to Lock - - will still proceed to try to end the thread without locking"); + } + + /*Codes_SRS_IOTHUBCLIENT_02_069: [ IoTHubClient_Destroy shall free all data created by IoTHubClient_UploadToBlobAsync ]*/ + /*wait for all uploading threads to finish*/ + while (singlylinkedlist_get_head_item(iotHubClientInstance->httpWorkerThreadInfoList) != NULL) + { + garbageCollectorImpl(iotHubClientInstance); + } + + if (iotHubClientInstance->httpWorkerThreadInfoList != NULL) + { + singlylinkedlist_destroy(iotHubClientInstance->httpWorkerThreadInfoList); + } + + /* Codes_SRS_IOTHUBCLIENT_01_006: [That includes destroying the IoTHubClientCore_LL instance by calling IoTHubClientCore_LL_Destroy.] */ + IoTHubClientCore_LL_Destroy(iotHubClientInstance->IoTHubClientLLHandle); + + if (Unlock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("unable to Unlock"); + } + + + vector_size = VECTOR_size(iotHubClientInstance->saved_user_callback_list); + size_t index = 0; + for (index = 0; index < vector_size; index++) + { + USER_CALLBACK_INFO* queue_cb_info = (USER_CALLBACK_INFO*)VECTOR_element(iotHubClientInstance->saved_user_callback_list, index); + if (queue_cb_info != NULL) + { + if ((queue_cb_info->type == CALLBACK_TYPE_DEVICE_METHOD) || (queue_cb_info->type == CALLBACK_TYPE_INBOUD_DEVICE_METHOD)) + { + STRING_delete(queue_cb_info->iothub_callback.method_cb_info.method_name); + BUFFER_delete(queue_cb_info->iothub_callback.method_cb_info.payload); + } + else if (queue_cb_info->type == CALLBACK_TYPE_DEVICE_TWIN) + { + if (queue_cb_info->iothub_callback.dev_twin_cb_info.payLoad != NULL) + { + free(queue_cb_info->iothub_callback.dev_twin_cb_info.payLoad); + } + } + else if (queue_cb_info->type == CALLBACK_TYPE_EVENT_CONFIRM) + { + if (iotHubClientInstance->event_confirm_callback) + { + iotHubClientInstance->event_confirm_callback(queue_cb_info->iothub_callback.event_confirm_cb_info.confirm_result, queue_cb_info->userContextCallback); + } + } + } + } + VECTOR_destroy(iotHubClientInstance->saved_user_callback_list); + + if (iotHubClientInstance->TransportHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_032: [If the lock was allocated in IoTHubClient_Create, it shall be also freed..] */ + Lock_Deinit(iotHubClientInstance->LockHandle); + } + if (iotHubClientInstance->devicetwin_user_context != NULL) + { + free(iotHubClientInstance->devicetwin_user_context); + } + if (iotHubClientInstance->connection_status_user_context != NULL) + { + free(iotHubClientInstance->connection_status_user_context); + } + if (iotHubClientInstance->message_user_context != NULL) + { + free(iotHubClientInstance->message_user_context); + } + if (iotHubClientInstance->method_user_context != NULL) + { + free(iotHubClientInstance->method_user_context); + } + free(iotHubClientInstance); + } +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SendEventAsync(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_011: [If iotHubClientHandle is NULL, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_INVALID_ARG.] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_01_009: [IoTHubClient_SendEventAsync shall start the worker thread if it was not previously started.] */ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_010: [If starting the thread fails, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_025: [IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_026: [If acquiring the lock fails, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->event_confirm_callback = eventConfirmationCallback; + } + + if (iotHubClientInstance->created_with_transport_handle != 0 || eventConfirmationCallback == NULL) + { + result = IoTHubClientCore_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_07_001: [ IoTHubClient_SendEventAsync shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClientCore_LL_SendEventAsync function as a user context. ] */ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (queue_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + /* Codes_SRS_IOTHUBCLIENT_01_012: [IoTHubClient_SendEventAsync shall call IoTHubClientCore_LL_SendEventAsync, while passing the IoTHubClientCore_LL handle created by IoTHubClient_Create and the parameters eventMessageHandle, eventConfirmationCallback and userContextCallback.] */ + /* Codes_SRS_IOTHUBCLIENT_01_013: [When IoTHubClientCore_LL_SendEventAsync is called, IoTHubClient_SendEventAsync shall return the result of IoTHubClientCore_LL_SendEventAsync.] */ + result = IoTHubClientCore_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, iothub_ll_event_confirm_callback, queue_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SendEventAsync failed"); + free(queue_context); + } + } + } + + /* Codes_SRS_IOTHUBCLIENT_01_025: [IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_GetSendStatus(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_023: [If iotHubClientHandle is NULL, IoTHubClient_ GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG.] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_01_033: [IoTHubClient_GetSendStatus shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_034: [If acquiring the lock fails, IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_022: [IoTHubClient_GetSendStatus shall call IoTHubClientCore_LL_GetSendStatus, while passing the IoTHubClientCore_LL handle created by IoTHubClient_Create and the parameter iotHubClientStatus.] */ + /* Codes_SRS_IOTHUBCLIENT_01_024: [Otherwise, IoTHubClient_GetSendStatus shall return the result of IoTHubClientCore_LL_GetSendStatus.] */ + result = IoTHubClientCore_LL_GetSendStatus(iotHubClientInstance->IoTHubClientLLHandle, iotHubClientStatus); + + /* Codes_SRS_IOTHUBCLIENT_01_033: [IoTHubClient_GetSendStatus shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetMessageCallback(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_016: [If iotHubClientHandle is NULL, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_INVALID_ARG.] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_01_014: [IoTHubClient_SetMessageCallback shall start the worker thread if it was not previously started.] */ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_015: [If starting the thread fails, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_028: [If acquiring the lock fails, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->message_callback = messageCallback; + } + if (iotHubClientInstance->message_user_context != NULL) + { + free(iotHubClientInstance->message_user_context); + iotHubClientInstance->message_user_context = NULL; + } + if (messageCallback == NULL) + { + result = IoTHubClientCore_LL_SetMessageCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, NULL, iotHubClientInstance->message_user_context); + } + else if (iotHubClientInstance->created_with_transport_handle != 0) + { + result = IoTHubClientCore_LL_SetMessageCallback(iotHubClientInstance->IoTHubClientLLHandle, messageCallback, userContextCallback); + } + else + { + iotHubClientInstance->message_user_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->message_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + iotHubClientInstance->message_user_context->iotHubClientHandle = iotHubClientHandle; + iotHubClientInstance->message_user_context->userContextCallback = userContextCallback; + + /* Codes_SRS_IOTHUBCLIENT_01_017: [IoTHubClient_SetMessageCallback shall call IoTHubClientCore_LL_SetMessageCallback_Ex, while passing the IoTHubClientCore_LL handle created by IoTHubClient_Create and the local iothub_ll_message_callback wrapper of messageCallback and userContextCallback.] */ + /* Codes_SRS_IOTHUBCLIENT_01_018: [When IoTHubClientCore_LL_SetMessageCallback_Ex is called, IoTHubClient_SetMessageCallback shall return the result of IoTHubClientCore_LL_SetMessageCallback_Ex.] */ + result = IoTHubClientCore_LL_SetMessageCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_message_callback, iotHubClientInstance->message_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetMessageCallback failed"); + free(iotHubClientInstance->message_user_context); + iotHubClientInstance->message_user_context = NULL; + } + } + } + + /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetConnectionStatusCallback(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_25_076: [** If `iotHubClientHandle` is `NULL`, `IoTHubClient_SetRetryPolicy` shall return `IOTHUB_CLIENT_INVALID_ARG`. ] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_25_081: [ `IoTHubClient_SetConnectionStatusCallback` shall start the worker thread if it was not previously started. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_083: [ If starting the thread fails, `IoTHubClient_SetConnectionStatusCallback` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_25_087: [ `IoTHubClient_SetConnectionStatusCallback` shall be made thread-safe by using the lock created in `IoTHubClient_Create`. ] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_088: [ If acquiring the lock fails, `IoTHubClient_SetConnectionStatusCallback` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->connection_status_callback = connectionStatusCallback; + } + + if (iotHubClientInstance->created_with_transport_handle != 0 || connectionStatusCallback == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_25_085: [ `IoTHubClient_SetConnectionStatusCallback` shall call `IoTHubClientCore_LL_SetConnectionStatusCallback`, while passing the `IoTHubClientCore_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`. ]*/ + result = IoTHubClientCore_LL_SetConnectionStatusCallback(iotHubClientInstance->IoTHubClientLLHandle, connectionStatusCallback, userContextCallback); + } + else + { + if (iotHubClientInstance->connection_status_user_context != NULL) + { + free(iotHubClientInstance->connection_status_user_context); + } + iotHubClientInstance->connection_status_user_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->connection_status_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + iotHubClientInstance->connection_status_user_context->iotHubClientHandle = iotHubClientInstance; + iotHubClientInstance->connection_status_user_context->userContextCallback = userContextCallback; + + /* Codes_SRS_IOTHUBCLIENT_25_085: [ `IoTHubClient_SetConnectionStatusCallback` shall call `IoTHubClientCore_LL_SetConnectionStatusCallback`, while passing the `IoTHubClientCore_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`. ]*/ + result = IoTHubClientCore_LL_SetConnectionStatusCallback(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_connection_status_callback, iotHubClientInstance->connection_status_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetConnectionStatusCallback failed"); + free(iotHubClientInstance->connection_status_user_context); + iotHubClientInstance->connection_status_user_context = NULL; + } + } + } + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetRetryPolicy(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_25_076: [** If `iotHubClientHandle` is `NULL`, `IoTHubClient_SetRetryPolicy` shall return `IOTHUB_CLIENT_INVALID_ARG`. ] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_25_073: [ `IoTHubClient_SetRetryPolicy` shall start the worker thread if it was not previously started. ] */ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_075: [ If starting the thread fails, `IoTHubClient_SetRetryPolicy` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_25_079: [ `IoTHubClient_SetRetryPolicy` shall be made thread-safe by using the lock created in `IoTHubClient_Create`.] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_080: [ If acquiring the lock fails, `IoTHubClient_SetRetryPolicy` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_25_077: [ `IoTHubClient_SetRetryPolicy` shall call `IoTHubClientCore_LL_SetRetryPolicy`, while passing the `IoTHubClientCore_LL` handle created by `IoTHubClient_Create` and the parameters `retryPolicy` and `retryTimeoutLimitinSeconds`.]*/ + result = IoTHubClientCore_LL_SetRetryPolicy(iotHubClientInstance->IoTHubClientLLHandle, retryPolicy, retryTimeoutLimitInSeconds); + (void)Unlock(iotHubClientInstance->LockHandle); + } + + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_GetRetryPolicy(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_25_092: [ If `iotHubClientHandle` is `NULL`, `IoTHubClient_GetRetryPolicy` shall return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_25_089: [ `IoTHubClient_GetRetryPolicy` shall start the worker thread if it was not previously started.]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_091: [ If starting the thread fails, `IoTHubClient_GetRetryPolicy` shall return `IOTHUB_CLIENT_ERROR`.]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_25_095: [ `IoTHubClient_GetRetryPolicy` shall be made thread-safe by using the lock created in `IoTHubClient_Create`. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_25_096: [ If acquiring the lock fails, `IoTHubClient_GetRetryPolicy` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_25_093: [ `IoTHubClient_GetRetryPolicy` shall call `IoTHubClientCore_LL_GetRetryPolicy`, while passing the `IoTHubClientCore_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`.]*/ + result = IoTHubClientCore_LL_GetRetryPolicy(iotHubClientInstance->IoTHubClientLLHandle, retryPolicy, retryTimeoutLimitInSeconds); + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_GetLastMessageReceiveTime(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_020: [If iotHubClientHandle is NULL, IoTHubClient_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG.] */ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_01_035: [IoTHubClient_GetLastMessageReceiveTime shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_036: [If acquiring the lock fails, IoTHubClient_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_019: [IoTHubClient_GetLastMessageReceiveTime shall call IoTHubClientCore_LL_GetLastMessageReceiveTime, while passing the IoTHubClientCore_LL handle created by IoTHubClient_Create and the parameter lastMessageReceiveTime.] */ + /* Codes_SRS_IOTHUBCLIENT_01_021: [Otherwise, IoTHubClient_GetLastMessageReceiveTime shall return the result of IoTHubClientCore_LL_GetLastMessageReceiveTime.] */ + result = IoTHubClientCore_LL_GetLastMessageReceiveTime(iotHubClientInstance->IoTHubClientLLHandle, lastMessageReceiveTime); + + /* Codes_SRS_IOTHUBCLIENT_01_035: [IoTHubClient_GetLastMessageReceiveTime shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetOption(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* optionName, const void* value) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_02_034: [If parameter iotHubClientHandle is NULL then IoTHubClient_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + /*Codes_SRS_IOTHUBCLIENT_02_035: [ If parameter optionName is NULL then IoTHubClient_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_02_036: [ If parameter value is NULL then IoTHubClient_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if ( + (iotHubClientHandle == NULL) || + (optionName == NULL) || + (value == NULL) + ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_01_041: [ IoTHubClient_SetOption shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /* Codes_SRS_IOTHUBCLIENT_01_042: [ If acquiring the lock fails, IoTHubClient_SetOption shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_02_038: [If optionName doesn't match one of the options handled by this module then IoTHubClient_SetOption shall call IoTHubClientCore_LL_SetOption passing the same parameters and return what IoTHubClientCore_LL_SetOption returns.] */ + result = IoTHubClientCore_LL_SetOption(iotHubClientInstance->IoTHubClientLLHandle, optionName, value); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetOption failed"); + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetDeviceTwinCallback(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_10_001: [** `IoTHubClient_SetDeviceTwinCallback` shall fail and return `IOTHUB_CLIENT_INVALID_ARG` if parameter `iotHubClientHandle` is `NULL`. ]*/ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_10_003: [** If the transport connection is shared, the thread shall be started by calling `IoTHubTransport_StartWorkerThread`. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_10_004: [** If starting the thread fails, `IoTHubClient_SetDeviceTwinCallback` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_10_020: [** `IoTHubClient_SetDeviceTwinCallback` shall be made thread - safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_10_002: [** If acquiring the lock fails, `IoTHubClient_SetDeviceTwinCallback` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->desired_state_callback = deviceTwinCallback; + } + + if (iotHubClientInstance->created_with_transport_handle != 0 || deviceTwinCallback == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_10_005: [** `IoTHubClientCore_LL_SetDeviceTwinCallback` shall call `IoTHubClientCore_LL_SetDeviceTwinCallback`, while passing the `IoTHubClientCore_LL handle` created by `IoTHubClientCore_LL_Create` along with the parameters `reportedStateCallback` and `userContextCallback`. ]*/ + result = IoTHubClientCore_LL_SetDeviceTwinCallback(iotHubClientInstance->IoTHubClientLLHandle, deviceTwinCallback, userContextCallback); + } + else + { + if (iotHubClientInstance->devicetwin_user_context != NULL) + { + free(iotHubClientInstance->devicetwin_user_context); + } + + /*Codes_SRS_IOTHUBCLIENT_07_002: [ IoTHubClient_SetDeviceTwinCallback shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClientCore_LL_SetDeviceTwinCallback function as a user context. ]*/ + iotHubClientInstance->devicetwin_user_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->devicetwin_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_10_005: [** `IoTHubClientCore_LL_SetDeviceTwinCallback` shall call `IoTHubClientCore_LL_SetDeviceTwinCallback`, while passing the `IoTHubClientCore_LL handle` created by `IoTHubClientCore_LL_Create` along with the parameters `iothub_ll_device_twin_callback` and IOTHUB_QUEUE_CONTEXT variable. ]*/ + iotHubClientInstance->devicetwin_user_context->iotHubClientHandle = iotHubClientInstance; + iotHubClientInstance->devicetwin_user_context->userContextCallback = userContextCallback; + result = IoTHubClientCore_LL_SetDeviceTwinCallback(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_device_twin_callback, iotHubClientInstance->devicetwin_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetDeviceTwinCallback failed"); + free(iotHubClientInstance->devicetwin_user_context); + iotHubClientInstance->devicetwin_user_context = NULL; + } + } + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SendReportedState(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_10_013: [** If `iotHubClientHandle` is `NULL`, `IoTHubClient_SendReportedState` shall return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_10_015: [** If the transport connection is shared, the thread shall be started by calling `IoTHubTransport_StartWorkerThread`. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_10_016: [** If starting the thread fails, `IoTHubClient_SendReportedState` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_10_021: [** `IoTHubClient_SendReportedState` shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_10_014: [** If acquiring the lock fails, `IoTHubClient_SendReportedState` shall return `IOTHUB_CLIENT_ERROR`. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->reported_state_callback = reportedStateCallback; + } + + if (iotHubClientInstance->created_with_transport_handle != 0 || reportedStateCallback == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_10_017: [** `IoTHubClient_SendReportedState` shall call `IoTHubClientCore_LL_SendReportedState`, while passing the `IoTHubClientCore_LL handle` created by `IoTHubClientCore_LL_Create` along with the parameters `reportedState`, `size`, `reportedStateCallback`, and `userContextCallback`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_10_018: [** When `IoTHubClientCore_LL_SendReportedState` is called, `IoTHubClient_SendReportedState` shall return the result of `IoTHubClientCore_LL_SendReportedState`. **]*/ + result = IoTHubClientCore_LL_SendReportedState(iotHubClientInstance->IoTHubClientLLHandle, reportedState, size, reportedStateCallback, userContextCallback); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_07_003: [ IoTHubClient_SendReportedState shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClientCore_LL_SendReportedState function as a user context. ] */ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (queue_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + /*Codes_SRS_IOTHUBCLIENT_10_017: [** `IoTHubClient_SendReportedState` shall call `IoTHubClientCore_LL_SendReportedState`, while passing the `IoTHubClientCore_LL handle` created by `IoTHubClientCore_LL_Create` along with the parameters `reportedState`, `size`, `iothub_ll_reported_state_callback` and IOTHUB_QUEUE_CONTEXT variable. ]*/ + /*Codes_SRS_IOTHUBCLIENT_10_018: [** When `IoTHubClientCore_LL_SendReportedState` is called, `IoTHubClient_SendReportedState` shall return the result of `IoTHubClientCore_LL_SendReportedState`. **]*/ + result = IoTHubClientCore_LL_SendReportedState(iotHubClientInstance->IoTHubClientLLHandle, reportedState, size, iothub_ll_reported_state_callback, queue_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SendReportedState failed"); + free(queue_context); + } + } + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetDeviceMethodCallback(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_12_012: [ If iotHubClientHandle is NULL, IoTHubClient_SetDeviceMethodCallback shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_12_014: [ If the transport handle is null and the worker thread is not initialized, the thread shall be started by calling IoTHubTransport_StartWorkerThread. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_12_015: [ If starting the thread fails, IoTHubClient_SetDeviceMethodCallback shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_12_018: [ IoTHubClient_SetDeviceMethodCallback shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_12_013: [ If acquiring the lock fails, IoTHubClient_SetDeviceMethodCallback shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->device_method_callback = deviceMethodCallback; + } + + if (iotHubClientInstance->method_user_context) + { + free(iotHubClientInstance->method_user_context); + iotHubClientInstance->method_user_context = NULL; + } + if (deviceMethodCallback == NULL) + { + result = IoTHubClientCore_LL_SetDeviceMethodCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, NULL, NULL); + } + else + { + iotHubClientInstance->method_user_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->method_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + iotHubClientInstance->method_user_context->iotHubClientHandle = iotHubClientHandle; + iotHubClientInstance->method_user_context->userContextCallback = userContextCallback; + + /*Codes_SRS_IOTHUBCLIENT_12_016: [ IoTHubClient_SetDeviceMethodCallback shall call IoTHubClientCore_LL_SetDeviceMethodCallback, while passing the IoTHubClientCore_LL_handle created by IoTHubClientCore_LL_Create along with the parameters deviceMethodCallback and userContextCallback. ]*/ + /*Codes_SRS_IOTHUBCLIENT_12_017: [ When IoTHubClientCore_LL_SetDeviceMethodCallback is called, IoTHubClient_SetDeviceMethodCallback shall return the result of IoTHubClientCore_LL_SetDeviceMethodCallback. ]*/ + result = IoTHubClientCore_LL_SetDeviceMethodCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_device_method_callback, iotHubClientInstance->method_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetDeviceMethodCallback_Ex failed"); + free(iotHubClientInstance->method_user_context); + iotHubClientInstance->method_user_context = NULL; + } + else + { + iotHubClientInstance->device_method_callback = deviceMethodCallback; + } + } + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetDeviceMethodCallback_Ex(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inboundDeviceMethodCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_07_001: [ If iotHubClientHandle is NULL, IoTHubClient_SetDeviceMethodCallback_Ex shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_07_003: [ If the transport handle is NULL and the worker thread is not initialized, the thread shall be started by calling IoTHubTransport_StartWorkerThread. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_07_004: [ If starting the thread fails, IoTHubClient_SetDeviceMethodCallback_Ex shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_07_007: [ IoTHubClient_SetDeviceMethodCallback_Ex shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_07_002: [ If acquiring the lock fails, IoTHubClient_SetDeviceMethodCallback_Ex shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->inbound_device_method_callback = inboundDeviceMethodCallback; + } + + if (iotHubClientInstance->method_user_context) + { + free(iotHubClientInstance->method_user_context); + iotHubClientInstance->method_user_context = NULL; + } + if (inboundDeviceMethodCallback == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_07_008: [ If inboundDeviceMethodCallback is NULL, IoTHubClient_SetDeviceMethodCallback_Ex shall call IoTHubClientCore_LL_SetDeviceMethodCallback_Ex, passing NULL for the iothub_ll_inbound_device_method_callback. ] */ + result = IoTHubClientCore_LL_SetDeviceMethodCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, NULL, NULL); + } + else + { + iotHubClientInstance->method_user_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->method_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_07_005: [ IoTHubClient_SetDeviceMethodCallback_Ex shall call IoTHubClientCore_LL_SetDeviceMethodCallback_Ex, while passing the IoTHubClientCore_LL_handle created by IoTHubClientCore_LL_Create along with the parameters iothub_ll_inbound_device_method_callback and IOTHUB_QUEUE_CONTEXT. ]*/ + iotHubClientInstance->method_user_context->iotHubClientHandle = iotHubClientHandle; + iotHubClientInstance->method_user_context->userContextCallback = userContextCallback; + + /* Codes_SRS_IOTHUBCLIENT_07_006: [ When IoTHubClientCore_LL_SetDeviceMethodCallback_Ex is called, IoTHubClient_SetDeviceMethodCallback_Ex shall return the result of IoTHubClientCore_LL_SetDeviceMethodCallback_Ex. ] */ + result = IoTHubClientCore_LL_SetDeviceMethodCallback_Ex(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_inbound_device_method_callback, iotHubClientInstance->method_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetDeviceMethodCallback_Ex failed"); + free(iotHubClientInstance->method_user_context); + iotHubClientInstance->method_user_context = NULL; + } + else + { + iotHubClientInstance->inbound_device_method_callback = inboundDeviceMethodCallback; + } + } + } + + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_DeviceMethodResponse(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, METHOD_HANDLE methodId, const unsigned char* response, size_t respSize, int statusCode) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_12_012: [ If iotHubClientHandle is NULL, IoTHubClient_SetDeviceMethodCallback shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid arg (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_12_018: [ IoTHubClient_SetDeviceMethodCallback shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBCLIENT_12_013: [ If acquiring the lock fails, IoTHubClient_SetDeviceMethodCallback shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + result = IoTHubClientCore_LL_DeviceMethodResponse(iotHubClientInstance->IoTHubClientLLHandle, methodId, response, respSize, statusCode); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_DeviceMethodResponse failed"); + } + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + return result; +} + +#if !defined(DONT_USE_UPLOADTOBLOB) || defined(USE_EDGE_MODULES) +static IOTHUB_CLIENT_RESULT startHttpWorkerThread(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, HTTPWORKER_THREAD_INFO* threadInfo, THREAD_START_FUNC httpWorkerThreadFunc) +{ + IOTHUB_CLIENT_RESULT result; + + LIST_ITEM_HANDLE item; + + // StartWorkerThreadIfNeeded creates the "main" worker thread used for transports. Though its not used + // for these HTTP based worker threads (see ThreadAPI_Create call below) the main one is needed for garbage collection. + if ((result = StartWorkerThreadIfNeeded(iotHubClientHandle)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("Could not start worker thread"); + } + else if (Lock(threadInfo->iotHubClientHandle->LockHandle) != LOCK_OK) + { + LogError("Lock failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if ((item = singlylinkedlist_add(threadInfo->iotHubClientHandle->httpWorkerThreadInfoList, threadInfo)) == NULL) + { + LogError("Adding item to list failed"); + result = IOTHUB_CLIENT_ERROR; + } + else if (ThreadAPI_Create(&threadInfo->threadHandle, httpWorkerThreadFunc, threadInfo) != THREADAPI_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to ThreadAPI_Create"); + // Remove the item from linked list here, while we're still under lock. Final garbage collector also does it under lock. + (void)singlylinkedlist_remove(threadInfo->iotHubClientHandle->httpWorkerThreadInfoList, item); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + (void)Unlock(threadInfo->iotHubClientHandle->LockHandle); + } + + return result; +} + +static int markThreadReadyToBeGarbageCollected(HTTPWORKER_THREAD_INFO* threadInfo) +{ + /*Codes_SRS_IOTHUBCLIENT_02_071: [ The thread shall mark itself as disposable. ]*/ + if (Lock(threadInfo->lockGarbage) != LOCK_OK) + { + LogError("unable to Lock - trying anyway"); + threadInfo->canBeGarbageCollected = 1; + } + else + { + threadInfo->canBeGarbageCollected = 1; + + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) + { + LogError("unable to Unlock after locking"); + } + } + + ThreadAPI_Exit(0); + return 0; +} + +#endif // !defined(DONT_USE_UPLOADTOBLOB) || defined(USE_EDGE_MODULES) + +#if !defined(DONT_USE_UPLOADTOBLOB) +static HTTPWORKER_THREAD_INFO* allocateUploadToBlob(const char* destinationFileName, IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, void* context) +{ + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)malloc(sizeof(HTTPWORKER_THREAD_INFO)); + if (threadInfo == NULL) + { + LogError("unable to allocate thread object"); + } + else + { + memset(threadInfo, 0, sizeof(HTTPWORKER_THREAD_INFO)); + threadInfo->workerThreadType = HTTPWORKER_THREAD_UPLOAD_TO_BLOB; + threadInfo->iotHubClientHandle = iotHubClientHandle; + threadInfo->context = context; + + if (mallocAndStrcpy_s(&threadInfo->destinationFileName, destinationFileName) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + freeHttpWorkerThreadInfo(threadInfo); + threadInfo = NULL; + } + else if ((threadInfo->lockGarbage = Lock_Init()) == NULL) + { + LogError("unable to allocate a lock"); + freeHttpWorkerThreadInfo(threadInfo); + threadInfo = NULL; + } + } + + return threadInfo; +} + + +static IOTHUB_CLIENT_RESULT initializeUploadToBlobData(HTTPWORKER_THREAD_INFO* threadInfo, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback) +{ + IOTHUB_CLIENT_RESULT result; + + threadInfo->uploadBlobSavedData.size = size; + threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback = iotHubClientFileUploadCallback; + + if (size != 0) + { + if ((threadInfo->uploadBlobSavedData.source = (unsigned char*)malloc(size)) == NULL) + { + LogError("Cannot allocate source field"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + memcpy(threadInfo->uploadBlobSavedData.source, source, size); + result = IOTHUB_CLIENT_OK; + } + } + else + { + result = IOTHUB_CLIENT_OK; + } + + return result; +} + + +static int uploadingThread(void *data) +{ + IOTHUB_CLIENT_FILE_UPLOAD_RESULT upload_result; + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)data; + + /*it so happens that IoTHubClientCore_LL_UploadToBlob is thread-safe because there's no saved state in the handle and there are no globals, so no need to protect it*/ + /*not having it protected means multiple simultaneous uploads can happen*/ + /*Codes_SRS_IOTHUBCLIENT_02_054: [ The thread shall call IoTHubClientCore_LL_UploadToBlob passing the information packed in the structure. ]*/ + if (IoTHubClientCore_LL_UploadToBlob(threadInfo->iotHubClientHandle->IoTHubClientLLHandle, threadInfo->destinationFileName, threadInfo->uploadBlobSavedData.source, threadInfo->uploadBlobSavedData.size) == IOTHUB_CLIENT_OK) + { + upload_result = FILE_UPLOAD_OK; + } + else + { + LogError("unable to IoTHubClientCore_LL_UploadToBlob"); + upload_result = FILE_UPLOAD_ERROR; + } + + if (threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback != NULL) + { + /*Codes_SRS_IOTHUBCLIENT_02_055: [ If IoTHubClientCore_LL_UploadToBlob fails then the thread shall call iotHubClientFileUploadCallbackInternal passing as result FILE_UPLOAD_ERROR and as context the structure from SRS IOTHUBCLIENT 02 051. ]*/ + threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback(upload_result, threadInfo->context); + } + + return markThreadReadyToBeGarbageCollected(threadInfo); +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_UploadToBlobAsync(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback, void* context) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_02_047: [ If iotHubClientHandle is NULL then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_02_048: [ If destinationFileName is NULL then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_02_049: [ If source is NULL and size is greated than 0 then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + ((source == NULL) && (size > 0)) + ) + { + LogError("invalid parameters IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle = %p , const char* destinationFileName = %s, const unsigned char* source= %p, size_t size = %lu, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback = %p, void* context = %p", + iotHubClientHandle, + destinationFileName, + source, + size, + iotHubClientFileUploadCallback, + context + ); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_02_051: [IoTHubClient_UploadToBlobAsync shall copy the souce, size, iotHubClientFileUploadCallback, context into a structure.]*/ + HTTPWORKER_THREAD_INFO *threadInfo = allocateUploadToBlob(destinationFileName, iotHubClientHandle, context); + if (threadInfo == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to create upload thread info"); + result = IOTHUB_CLIENT_ERROR; + } + else if ((result = initializeUploadToBlobData(threadInfo, source, size, iotHubClientFileUploadCallback)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to initialize upload blob info"); + result = IOTHUB_CLIENT_ERROR; + } + /*Codes_SRS_IOTHUBCLIENT_02_052: [ IoTHubClient_UploadToBlobAsync shall spawn a thread passing the structure build in SRS IOTHUBCLIENT 02 051 as thread data.]*/ + else if ((result = startHttpWorkerThread(iotHubClientHandle, threadInfo, uploadingThread)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to start upload thread"); + freeHttpWorkerThreadInfo(threadInfo); + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + + return result; +} + +static int uploadMultipleBlock_thread(void* data) +{ + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)data; + IOTHUB_CLIENT_CORE_LL_HANDLE llHandle = threadInfo->iotHubClientHandle->IoTHubClientLLHandle; + + /*Codes_SRS_IOTHUBCLIENT_99_078: [ The thread shall call `IoTHubClientCore_LL_UploadMultipleBlocksToBlob` or `IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx` passing the information packed in the structure. ]*/ + IOTHUB_CLIENT_RESULT result; + + if (threadInfo->uploadBlobMultiblockSavedData.getDataCallback != NULL) + { + result = IoTHubClientCore_LL_UploadMultipleBlocksToBlob(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallback, threadInfo->context); + } + else + { + result = IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx, threadInfo->context); + } + (void)markThreadReadyToBeGarbageCollected(threadInfo); + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_UploadMultipleBlocksToBlobAsync(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_99_072: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_99_073: [ If `destinationFileName` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_99_074: [ If `getDataCallback` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + ((getDataCallback == NULL) && (getDataCallbackEx == NULL)) + ) + { + LogError("invalid parameters iotHubClientHandle = %p , destinationFileName = %p, getDataCallback = %p, getDataCallbackEx = %p", + iotHubClientHandle, + destinationFileName, + getDataCallback, + getDataCallbackEx + ); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ + HTTPWORKER_THREAD_INFO *threadInfo = allocateUploadToBlob(destinationFileName, iotHubClientHandle, context); + if (threadInfo == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to create upload thread info"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ + threadInfo->uploadBlobMultiblockSavedData.getDataCallback = getDataCallback; + threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx = getDataCallbackEx; + + if ((result = startHttpWorkerThread(iotHubClientHandle, threadInfo, uploadMultipleBlock_thread)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to start upload thread"); + freeHttpWorkerThreadInfo(threadInfo); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure and spawning the thread succeeds, then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall return `IOTHUB_CLIENT_OK`. ]*/ + result = IOTHUB_CLIENT_OK; + } + } + } + return result; +} + +#endif /*DONT_USE_UPLOADTOBLOB*/ + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SendEventToOutputAsync(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, const char* outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if ((iotHubClientHandle == NULL) || (outputName == NULL) || (eventMessageHandle == NULL)) + { + // Codes_SRS_IOTHUBCLIENT_31_100: [ If `iotHubClientHandle`, `outputName`, or `eventConfirmationCallback` is `NULL`, `IoTHubClient_SendEventToOutputAsync` shall return `IOTHUB_CLIENT_INVALID_ARG`. ] + LogError("Invalid argument (iotHubClientHandle=%p, outputName=%p, eventMessageHandle=%p)", iotHubClientHandle, outputName, eventMessageHandle); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + // Codes_SRS_IOTHUBCLIENT_31_101: [ `IoTHubClient_SendEventToOutputAsync` shall set the outputName of the message to send. ] + if (IoTHubMessage_SetOutputName(eventMessageHandle, outputName) != IOTHUB_MESSAGE_OK) + { + LogError("IoTHubMessage_SetOutputName failed"); + result = IOTHUB_CLIENT_ERROR; + } + // Codes_SRS_IOTHUBCLIENT_31_102: [ `IoTHubClient_SendEventToOutputAsync` shall invoke `IoTHubClient_SendEventAsync` to send the message. ] + else if ((result = IoTHubClientCore_SendEventAsync(iotHubClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("Call into IoTHubClient_SendEventAsync failed, result=%d", result); + } + } + + return result; +} + + +IOTHUB_CLIENT_RESULT IoTHubClientCore_SetInputMessageCallback(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("NULL iothubClientHandle"); + } + else + { + IOTHUB_CLIENT_CORE_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_CORE_INSTANCE*)iotHubClientHandle; + + // Codes_SRS_IOTHUBCLIENT_31_098: [ `IoTHubClient_SetMessageCallback` shall start the worker thread if it was not previously started. ] + if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Could not start worker thread"); + } + else + { + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Could not acquire lock"); + } + else + { + // Codes_SRS_IOTHUBCLIENT_31_099: [ `IoTHubClient_SetMessageCallback` shall call `IoTHubClient_LL_SetInputMessageCallback`, passing its input arguments ] + IOTHUB_INPUTMESSAGE_CALLBACK_CONTEXT inputMessageCallbackContext; + inputMessageCallbackContext.iotHubClientHandle = iotHubClientHandle; + inputMessageCallbackContext.eventHandlerCallback = eventHandlerCallback; + inputMessageCallbackContext.userContextCallback = userContextCallback; + + result = IoTHubClientCore_LL_SetInputMessageCallbackEx(iotHubClientInstance->IoTHubClientLLHandle, inputName, iothub_ll_inputmessage_callback, (void*)&inputMessageCallbackContext, sizeof(inputMessageCallbackContext)); + (void)Unlock(iotHubClientInstance->LockHandle); + } + } + } + + return result; +} + +#ifdef USE_EDGE_MODULES + +HTTPWORKER_THREAD_INFO * allocateMethodInvoke(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, IOTHUB_METHOD_INVOKE_CALLBACK methodInvokeCallback, void* context) +{ + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)malloc(sizeof(HTTPWORKER_THREAD_INFO)); + if (threadInfo == NULL) + { + LogError("unable to allocate thread object"); + } + else + { + memset(threadInfo, 0, sizeof(HTTPWORKER_THREAD_INFO)); + threadInfo->workerThreadType = HTTPWORKER_THREAD_INVOKE_METHOD; + threadInfo->iotHubClientHandle = iotHubClientHandle; + threadInfo->context = context; + + threadInfo->invokeMethodSavedData.timeout = timeout; + threadInfo->invokeMethodSavedData.methodInvokeCallback = methodInvokeCallback; + + if ((mallocAndStrcpy_s((char**)&threadInfo->invokeMethodSavedData.deviceId, deviceId) != 0) || + ((moduleId != NULL) && mallocAndStrcpy_s((char**)&threadInfo->invokeMethodSavedData.moduleId, moduleId) != 0) || + (mallocAndStrcpy_s((char**)&threadInfo->invokeMethodSavedData.methodName, methodName) != 0) || + (mallocAndStrcpy_s((char**)&threadInfo->invokeMethodSavedData.methodPayload, methodPayload) != 0)) + { + LogError("Allocating resources failed"); + freeHttpWorkerThreadInfo(threadInfo); + threadInfo = NULL; + } + else if ((threadInfo->lockGarbage = Lock_Init()) == NULL) + { + LogError("unable to allocate a lock"); + freeHttpWorkerThreadInfo(threadInfo); + threadInfo = NULL; + } + } + + return threadInfo; +} + +static int uploadMethodInvoke_thread(void* data) +{ + IOTHUB_CLIENT_RESULT result; + + HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)data; + + int responseStatus; + unsigned char* responsePayload = NULL; + size_t responsePayloadSize; + + result = IoTHubClientCore_LL_GenericMethodInvoke(threadInfo->iotHubClientHandle->IoTHubClientLLHandle, + threadInfo->invokeMethodSavedData.deviceId, + threadInfo->invokeMethodSavedData.moduleId, + threadInfo->invokeMethodSavedData.methodName, + threadInfo->invokeMethodSavedData.methodPayload, + threadInfo->invokeMethodSavedData.timeout, + &responseStatus, + &responsePayload, + &responsePayloadSize); + + if (threadInfo->invokeMethodSavedData.methodInvokeCallback != NULL) + { + threadInfo->invokeMethodSavedData.methodInvokeCallback(result, responseStatus, responsePayload, responsePayloadSize, threadInfo->context); + } + + if (responsePayload != NULL) + { + free(responsePayload); + } + + (void)markThreadReadyToBeGarbageCollected(threadInfo); + return result; +} + + +IOTHUB_CLIENT_RESULT IoTHubClientCore_GenericMethodInvoke(IOTHUB_CLIENT_CORE_HANDLE iotHubClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, IOTHUB_METHOD_INVOKE_CALLBACK methodInvokeCallback, void* context) +{ + IOTHUB_CLIENT_RESULT result; + HTTPWORKER_THREAD_INFO *threadInfo; + + if ((iotHubClientHandle == NULL) || (deviceId == NULL) || (methodName == NULL) || (methodPayload == NULL)) + { + LogError("Invalid argument (iotHubClientHandle=%p, deviceId=%p, methodName=%p, methodPayload=%p)", iotHubClientHandle, deviceId, methodName, methodPayload); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else if ((threadInfo = allocateMethodInvoke(iotHubClientHandle, deviceId, moduleId, methodName, methodPayload, timeout, methodInvokeCallback, context)) == NULL) + { + LogError("failed allocating method invoke thread info"); + result = IOTHUB_CLIENT_ERROR; + } + else if ((result = startHttpWorkerThread(iotHubClientHandle, threadInfo, uploadMethodInvoke_thread)) != IOTHUB_CLIENT_OK) + { + LogError("unable to start method invoke thread"); + freeHttpWorkerThreadInfo(threadInfo); + } + else + { + result = IOTHUB_CLIENT_OK; + } + return result; +} +#endif /* USE_EDGE_MODULES */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_core_ll.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2840 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/agenttime.h" + +#include "iothub_client_core_ll.h" +#include "internal/iothub_client_authorization.h" +#include "iothub_transport_ll.h" +#include "internal/iothub_client_private.h" +#include "iothub_client_options.h" +#include "iothub_client_version.h" +#include "internal/iothub_client_diagnostic.h" +#include <stdint.h> +#include "internal/iothubtransport.h" + +#ifndef DONT_USE_UPLOADTOBLOB +#include "internal/iothub_client_ll_uploadtoblob.h" +#endif + +#ifdef USE_EDGE_MODULES +#include "azure_c_shared_utility/envvariable.h" +// #include "azure_prov_client/iothub_security_factory.h" +#include "internal/iothub_client_edge.h" +#endif + +#define LOG_ERROR_RESULT LogError("result = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result)); +#define INDEFINITE_TIME ((time_t)(-1)) + +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_RETRY_POLICY, IOTHUB_CLIENT_RETRY_POLICY_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_STATUS, IOTHUB_CLIENT_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_IDENTITY_TYPE, IOTHUB_IDENTITY_TYPE_VALUE); +DEFINE_ENUM_STRINGS(IOTHUB_PROCESS_ITEM_RESULT, IOTHUB_PROCESS_ITEM_RESULT_VALUE); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_IOTHUB_METHOD_STATUS, IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONNECTION_STATUS, IOTHUB_CLIENT_CONNECTION_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, IOTHUB_CLIENT_CONNECTION_STATUS_REASON_VALUES); +DEFINE_ENUM_STRINGS(TRANSPORT_TYPE, TRANSPORT_TYPE_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_TWIN_UPDATE_STATE, DEVICE_TWIN_UPDATE_STATE_VALUES); +#ifndef DONT_USE_UPLOADTOBLOB +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES); +#endif // DONT_USE_UPLOADTOBLOB + +#define CALLBACK_TYPE_VALUES \ + CALLBACK_TYPE_NONE, \ + CALLBACK_TYPE_SYNC, \ + CALLBACK_TYPE_ASYNC + +DEFINE_ENUM(CALLBACK_TYPE, CALLBACK_TYPE_VALUES) +DEFINE_ENUM_STRINGS(CALLBACK_TYPE, CALLBACK_TYPE_VALUES) + +typedef struct IOTHUB_METHOD_CALLBACK_DATA_TAG +{ + CALLBACK_TYPE type; + IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC callbackSync; + IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK callbackAsync; + void* userContextCallback; +}IOTHUB_METHOD_CALLBACK_DATA; + +typedef struct IOTHUB_EVENT_CALLBACK_TAG +{ + STRING_HANDLE inputName; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC callbackAsync; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX callbackAsyncEx; + void* userContextCallback; + void* userContextCallbackEx; +}IOTHUB_EVENT_CALLBACK; + +typedef struct IOTHUB_MESSAGE_CALLBACK_DATA_TAG +{ + CALLBACK_TYPE type; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC callbackSync; + IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX callbackAsync; + void* userContextCallback; +}IOTHUB_MESSAGE_CALLBACK_DATA; + +typedef struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA_TAG +{ + DLIST_ENTRY waitingToSend; + DLIST_ENTRY iot_msg_queue; + DLIST_ENTRY iot_ack_queue; + TRANSPORT_LL_HANDLE transportHandle; + bool isSharedTransport; + IOTHUB_DEVICE_HANDLE deviceHandle; + TRANSPORT_PROVIDER_FIELDS; + IOTHUB_MESSAGE_CALLBACK_DATA messageCallback; + IOTHUB_METHOD_CALLBACK_DATA methodCallback; + IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK conStatusCallback; + void* conStatusUserContextCallback; + time_t lastMessageReceiveTime; + TICK_COUNTER_HANDLE tickCounter; /*shared tickcounter used to track message timeouts in waitingToSend list*/ + tickcounter_ms_t currentMessageTimeout; + uint64_t current_device_twin_timeout; + IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback; + void* deviceTwinContextCallback; + IOTHUB_CLIENT_RETRY_POLICY retryPolicy; + size_t retryTimeoutLimitInSeconds; +#ifndef DONT_USE_UPLOADTOBLOB + IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE uploadToBlobHandle; +#endif +#ifdef USE_EDGE_MODULES + IOTHUB_CLIENT_EDGE_HANDLE methodHandle; +#endif + uint32_t data_msg_id; + bool complete_twin_update_encountered; + IOTHUB_AUTHORIZATION_HANDLE authorization_module; + STRING_HANDLE product_info; + IOTHUB_DIAGNOSTIC_SETTING_DATA diagnostic_setting; + SINGLYLINKEDLIST_HANDLE event_callbacks; // List of IOTHUB_EVENT_CALLBACK's +}IOTHUB_CLIENT_CORE_LL_HANDLE_DATA; + +static const char HOSTNAME_TOKEN[] = "HostName"; +static const char DEVICEID_TOKEN[] = "DeviceId"; +static const char X509_TOKEN[] = "x509"; +static const char X509_TOKEN_ONLY_ACCEPTABLE_VALUE[] = "true"; +static const char DEVICEKEY_TOKEN[] = "SharedAccessKey"; +static const char DEVICESAS_TOKEN[] = "SharedAccessSignature"; +static const char PROTOCOL_GATEWAY_HOST_TOKEN[] = "GatewayHostName"; +static const char MODULE_ID_TOKEN[] = "ModuleId"; +static const char PROVISIONING_TOKEN[] = "UseProvisioning"; +static const char PROVISIONING_ACCEPTABLE_VALUE[] = "true"; + + +#ifdef USE_EDGE_MODULES +/*The following section should be moved to iothub_module_client_ll.c during impending refactor*/ + +static const char* ENVIRONMENT_VAR_EDGEHUBCONNECTIONSTRING = "EdgeHubConnectionString"; +static const char* ENVIRONMENT_VAR_EDGEAUTHSCHEME = "IOTEDGE_AUTHSCHEME"; +static const char* ENVIRONMENT_VAR_EDGEDEVICEID = "IOTEDGE_DEVICEID"; +static const char* ENVIRONMENT_VAR_EDGEMODULEID = "IOTEDGE_MODULEID"; +static const char* ENVIRONMENT_VAR_EDGEHUBHOSTNAME = "IOTEDGE_IOTHUBHOSTNAME"; +static const char* ENVIRONMENT_VAR_EDGEGATEWAYHOST = "IOTEDGE_GATEWAYHOSTNAME"; +static const char* SAS_TOKEN_AUTH = "sasToken"; + + +typedef struct EDGE_ENVIRONMENT_VARIABLES_TAG +{ + const char* connection_string; + const char* auth_scheme; + const char* device_id; + const char* iothub_name; + const char* iothub_suffix; + const char* gatewayhostname; + const char* module_id; + char* iothub_buffer; +} EDGE_ENVIRONMENT_VARIABLES; + +static int retrieve_edge_environment_variabes(EDGE_ENVIRONMENT_VARIABLES *edge_environment_variables) +{ + int result; + const char* edgehubhostname; + char* edgehubhostname_separator; + + if ((edge_environment_variables->connection_string = environment_get_variable(ENVIRONMENT_VAR_EDGEHUBCONNECTIONSTRING)) != NULL) + { + // If a connection string is set, we use it and ignore all other environment variables. + result = 0; + } + else + { + if ((edge_environment_variables->auth_scheme = environment_get_variable(ENVIRONMENT_VAR_EDGEAUTHSCHEME)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEAUTHSCHEME); + result = __FAILURE__; + } + else if (strcmp(edge_environment_variables->auth_scheme, SAS_TOKEN_AUTH) != 0) + { + LogError("Environment %s was set to %s, but only support for %s", ENVIRONMENT_VAR_EDGEAUTHSCHEME, edge_environment_variables->auth_scheme, SAS_TOKEN_AUTH); + result = __FAILURE__; + } + else if ((edge_environment_variables->device_id = environment_get_variable(ENVIRONMENT_VAR_EDGEDEVICEID)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEDEVICEID); + result = __FAILURE__; + } + else if ((edgehubhostname = environment_get_variable(ENVIRONMENT_VAR_EDGEHUBHOSTNAME)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEHUBHOSTNAME); + result = __FAILURE__; + } + else if ((edge_environment_variables->gatewayhostname = environment_get_variable(ENVIRONMENT_VAR_EDGEGATEWAYHOST)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEGATEWAYHOST); + result = __FAILURE__; + } + else if ((edge_environment_variables->module_id = environment_get_variable(ENVIRONMENT_VAR_EDGEMODULEID)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEMODULEID); + result = __FAILURE__; + } + // Make a copy of just ENVIRONMENT_VAR_EDGEHUBHOSTNAME. We need to make changes in place (namely inserting a '\0') + // and can't do this with system environment variable safely. + else if (mallocAndStrcpy_s(&edge_environment_variables->iothub_buffer, edgehubhostname) != 0) + { + LogError("Unable to copy buffer"); + result = __FAILURE__; + } + else if ((edgehubhostname_separator = strchr(edge_environment_variables->iothub_buffer, '.')) == NULL) + { + LogError("Environment edgehub %s invalid, requires '.' separator", edge_environment_variables->iothub_buffer); + result = __FAILURE__; + } + else if (*(edgehubhostname_separator + 1) == 0) + { + LogError("Environment edgehub %s invalid, no content after '.' separator", edge_environment_variables->iothub_buffer); + result = __FAILURE__; + } + else + { + edge_environment_variables->iothub_name = edge_environment_variables->iothub_buffer; + *edgehubhostname_separator = 0; + edge_environment_variables->iothub_suffix = edgehubhostname_separator + 1; + result = 0; + } + } + + return result; +} + +IOTHUB_CLIENT_EDGE_HANDLE IoTHubClientCore_LL_GetEdgeHandle(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + IOTHUB_CLIENT_EDGE_HANDLE result; + if (iotHubClientHandle != NULL) + { + result = iotHubClientHandle->methodHandle; + } + else + { + result = NULL; + } + + return result; +} +#endif /* USE_EDGE_MODULES */ + +static void setTransportProtocol(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, TRANSPORT_PROVIDER* protocol) +{ + handleData->IoTHubTransport_SendMessageDisposition = protocol->IoTHubTransport_SendMessageDisposition; + handleData->IoTHubTransport_GetHostname = protocol->IoTHubTransport_GetHostname; + handleData->IoTHubTransport_SetOption = protocol->IoTHubTransport_SetOption; + handleData->IoTHubTransport_Create = protocol->IoTHubTransport_Create; + handleData->IoTHubTransport_Destroy = protocol->IoTHubTransport_Destroy; + handleData->IoTHubTransport_Register = protocol->IoTHubTransport_Register; + handleData->IoTHubTransport_Unregister = protocol->IoTHubTransport_Unregister; + handleData->IoTHubTransport_Subscribe = protocol->IoTHubTransport_Subscribe; + handleData->IoTHubTransport_Unsubscribe = protocol->IoTHubTransport_Unsubscribe; + handleData->IoTHubTransport_DoWork = protocol->IoTHubTransport_DoWork; + handleData->IoTHubTransport_SetRetryPolicy = protocol->IoTHubTransport_SetRetryPolicy; + handleData->IoTHubTransport_GetSendStatus = protocol->IoTHubTransport_GetSendStatus; + handleData->IoTHubTransport_ProcessItem = protocol->IoTHubTransport_ProcessItem; + handleData->IoTHubTransport_Subscribe_DeviceTwin = protocol->IoTHubTransport_Subscribe_DeviceTwin; + handleData->IoTHubTransport_Unsubscribe_DeviceTwin = protocol->IoTHubTransport_Unsubscribe_DeviceTwin; + handleData->IoTHubTransport_Subscribe_DeviceMethod = protocol->IoTHubTransport_Subscribe_DeviceMethod; + handleData->IoTHubTransport_Unsubscribe_DeviceMethod = protocol->IoTHubTransport_Unsubscribe_DeviceMethod; + handleData->IoTHubTransport_DeviceMethod_Response = protocol->IoTHubTransport_DeviceMethod_Response; + handleData->IoTHubTransport_Subscribe_InputQueue = protocol->IoTHubTransport_Subscribe_InputQueue; + handleData->IoTHubTransport_Unsubscribe_InputQueue = protocol->IoTHubTransport_Unsubscribe_InputQueue; +} + +static void device_twin_data_destroy(IOTHUB_DEVICE_TWIN* client_item) +{ + CONSTBUFFER_Destroy(client_item->report_data_handle); + free(client_item); +} + +static int create_edge_handle(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data, const IOTHUB_CLIENT_CONFIG* config, const char* module_id) +{ + int result; + (void)config; + (void)module_id; +#ifdef USE_EDGE_MODULES + /* There is no way to currently distinguish a regular module from a edge module, so this handle is created regardless of if appropriate. + However, as a gateway hostname is required in order to create an Edge Handle, we need to at least make sure that exists + in order to prevent errors. + + The end result is that all edge modules will have an EdgeHandle, but only some non-edge modules will have it. + Regardless, non-edge modules will never be able to use the handle. + */ + if (config->protocolGatewayHostName != NULL) + { + handle_data->methodHandle = IoTHubClient_EdgeHandle_Create(config, handle_data->authorization_module, module_id); + + if (handle_data->methodHandle == NULL) + { + LogError("Unable to IoTHubModuleClient_LL_MethodHandle_Create"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + +#else + (void)handle_data; + result = 0; +#endif + return result; +} + +static int create_blob_upload_module(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data, const IOTHUB_CLIENT_CONFIG* config) +{ + int result; + (void)handle_data; + (void)config; +#ifndef DONT_USE_UPLOADTOBLOB + handle_data->uploadToBlobHandle = IoTHubClient_LL_UploadToBlob_Create(config); + if (handle_data->uploadToBlobHandle == NULL) + { + LogError("unable to IoTHubClientCore_LL_UploadToBlob_Create"); + result = __FAILURE__; + } + else + { + result = 0; + } +#else + result = 0; +#endif + return result; +} + +static void destroy_blob_upload_module(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data) +{ + (void)handle_data; +#ifndef DONT_USE_UPLOADTOBLOB + /*Codes_SRS_IOTHUBCLIENT_LL_02_046: [ If creating the TICK_COUNTER_HANDLE fails then IoTHubClientCore_LL_Create shall fail and return NULL. ]*/ + IoTHubClient_LL_UploadToBlob_Destroy(handle_data->uploadToBlobHandle); +#endif +} + +static void destroy_module_method_module(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data) +{ + (void)handle_data; +#ifdef USE_EDGE_MODULES + IoTHubClient_EdgeHandle_Destroy(handle_data->methodHandle); +#endif +} + +/*Codes_SRS_IOTHUBCLIENT_LL_10_032: ["product_info" - takes a char string as an argument to specify the product information(e.g. `"ProductName/ProductVersion"`). ]*/ +/*Codes_SRS_IOTHUBCLIENT_LL_10_034: ["product_info" - shall store the given string concatenated with the sdk information and the platform information in the form(ProductInfo DeviceSDKName / DeviceSDKVersion(OSName OSVersion; Architecture). ]*/ +static STRING_HANDLE make_product_info(const char* product) +{ + STRING_HANDLE result; + STRING_HANDLE pfi = platform_get_platform_info(); + if (pfi == NULL) + { + result = NULL; + } + else + { + if (product == NULL) + { + result = STRING_construct_sprintf("%s %s", CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION, STRING_c_str(pfi)); + } + else + { + result = STRING_construct_sprintf("%s %s %s", product, CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION, STRING_c_str(pfi)); + } + STRING_delete(pfi); + } + return result; +} + +static IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* initialize_iothub_client(const IOTHUB_CLIENT_CONFIG* client_config, const IOTHUB_CLIENT_DEVICE_CONFIG* device_config, bool use_dev_auth, const char* module_id) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* result; + srand((unsigned int)time(NULL)); + STRING_HANDLE product_info = make_product_info(NULL); + if (product_info == NULL) + { + LogError("failed to initialize product info"); + result = NULL; + } + else + { + result = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)malloc(sizeof(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA)); + if (result == NULL) + { + LogError("failure allocating IOTHUB_CLIENT_CORE_LL_HANDLE_DATA"); + STRING_delete(product_info); + } + else + { + IOTHUB_CLIENT_CONFIG actual_config; + const IOTHUB_CLIENT_CONFIG* config = NULL; + char* IoTHubName = NULL; + char* IoTHubSuffix = NULL; + + memset(result, 0, sizeof(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA)); + if (use_dev_auth) + { + if ((result->authorization_module = IoTHubClient_Auth_CreateFromDeviceAuth(client_config->deviceId, module_id)) == NULL) + { + LogError("Failed create authorization module"); + free(result); + STRING_delete(product_info); + result = NULL; + } + } + else + { + const char* device_key; + const char* device_id; + const char* sas_token; + if (device_config == NULL) + { + device_key = client_config->deviceKey; + device_id = client_config->deviceId; + sas_token = client_config->deviceSasToken; + } + else + { + device_key = device_config->deviceKey; + device_id = device_config->deviceId; + sas_token = device_config->deviceSasToken; + } + + /* Codes_SRS_IOTHUBCLIENT_LL_07_029: [ IoTHubClientCore_LL_Create shall create the Auth module with the device_key, device_id, and/or deviceSasToken values ] */ + if ((result->authorization_module = IoTHubClient_Auth_Create(device_key, device_id, sas_token, module_id)) == NULL) + { + LogError("Failed create authorization module"); + free(result); + STRING_delete(product_info); + result = NULL; + } + } + + if (result != NULL) + { + if (client_config != NULL) + { + IOTHUBTRANSPORT_CONFIG lowerLayerConfig; + memset(&lowerLayerConfig, 0, sizeof(IOTHUBTRANSPORT_CONFIG)); + /*Codes_SRS_IOTHUBCLIENT_LL_02_006: [IoTHubClientCore_LL_Create shall populate a structure of type IOTHUBTRANSPORT_CONFIG with the information from config parameter and the previous DLIST and shall pass that to the underlying layer _Create function.]*/ + lowerLayerConfig.upperConfig = client_config; + lowerLayerConfig.waitingToSend = &(result->waitingToSend); + lowerLayerConfig.auth_module_handle = result->authorization_module; + lowerLayerConfig.moduleId = module_id; + + setTransportProtocol(result, (TRANSPORT_PROVIDER*)client_config->protocol()); + if ((result->transportHandle = result->IoTHubTransport_Create(&lowerLayerConfig)) == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_007: [If the underlaying layer _Create function fails them IoTHubClientCore_LL_Create shall fail and return NULL.] */ + LogError("underlying transport failed"); + destroy_blob_upload_module(result); + destroy_module_method_module(result); + tickcounter_destroy(result->tickCounter); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_008: [Otherwise, IoTHubClientCore_LL_Create shall succeed and return a non-NULL handle.] */ + result->isSharedTransport = false; + config = client_config; + } + } + else + { + STRING_HANDLE transport_hostname = NULL; + + result->transportHandle = device_config->transportHandle; + setTransportProtocol(result, (TRANSPORT_PROVIDER*)device_config->protocol()); + + transport_hostname = result->IoTHubTransport_GetHostname(result->transportHandle); + if (transport_hostname == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClientCore_LL_CreateWithTransport shall fail and return NULL. ]*/ + LogError("unable to determine the transport IoTHub name"); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + const char* hostname = STRING_c_str(transport_hostname); + /*Codes_SRS_IOTHUBCLIENT_LL_02_096: [ IoTHubClientCore_LL_CreateWithTransport shall create the data structures needed to instantiate a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE. ]*/ + /*the first '.' says where the iothubname finishes*/ + const char* whereIsDot = strchr(hostname, '.'); + if (whereIsDot == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClientCore_LL_CreateWithTransport shall fail and return NULL. ]*/ + LogError("unable to determine the IoTHub name"); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + size_t suffix_len = strlen(whereIsDot); + /*Codes_SRS_IOTHUBCLIENT_LL_02_096: [ IoTHubClientCore_LL_CreateWithTransport shall create the data structures needed to instantiate a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE. ]*/ + IoTHubName = (char*)malloc(whereIsDot - hostname + 1); + if (IoTHubName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClientCore_LL_CreateWithTransport shall fail and return NULL. ]*/ + LogError("unable to malloc"); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else if ((IoTHubSuffix = (char*)malloc(suffix_len + 1)) == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClientCore_LL_CreateWithTransport shall fail and return NULL. ]*/ + LogError("unable to malloc"); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + memset(IoTHubName, 0, whereIsDot - hostname + 1); + (void)strncpy(IoTHubName, hostname, whereIsDot - hostname); + (void)strcpy(IoTHubSuffix, whereIsDot+1); + + actual_config.deviceId = device_config->deviceId; + actual_config.deviceKey = device_config->deviceKey; + actual_config.deviceSasToken = device_config->deviceSasToken; + actual_config.iotHubName = IoTHubName; + actual_config.iotHubSuffix = IoTHubSuffix; + actual_config.protocol = NULL; /*irrelevant to IoTHubClientCore_LL_UploadToBlob*/ + actual_config.protocolGatewayHostName = NULL; /*irrelevant to IoTHubClientCore_LL_UploadToBlob*/ + + config = &actual_config; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_008: [Otherwise, IoTHubClientCore_LL_Create shall succeed and return a non-NULL handle.] */ + result->isSharedTransport = true; + } + } + } + STRING_delete(transport_hostname); + } + } + if (result != NULL) + { + if (create_blob_upload_module(result, config) != 0) + { + LogError("unable to create blob upload"); + // Codes_SRS_IOTHUBCLIENT_LL_09_010: [ If any failure occurs `IoTHubClientCore_LL_Create` shall destroy the `transportHandle` only if it has created it ] + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else if ((module_id != NULL) && create_edge_handle(result, config, module_id) != 0) + { + LogError("unable to create module method handle"); + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + if ((result->tickCounter = tickcounter_create()) == NULL) + { + LogError("unable to get a tickcounter"); + // Codes_SRS_IOTHUBCLIENT_LL_09_010: [ If any failure occurs `IoTHubClientCore_LL_Create` shall destroy the `transportHandle` only if it has created it ] + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + destroy_blob_upload_module(result); + destroy_module_method_module(result); + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_004: [Otherwise IoTHubClientCore_LL_Create shall initialize a new DLIST (further called "waitingToSend") containing records with fields of the following types: IOTHUB_MESSAGE_HANDLE, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, void*.]*/ + DList_InitializeListHead(&(result->waitingToSend)); + DList_InitializeListHead(&(result->iot_msg_queue)); + DList_InitializeListHead(&(result->iot_ack_queue)); + result->messageCallback.type = CALLBACK_TYPE_NONE; + result->lastMessageReceiveTime = INDEFINITE_TIME; + result->data_msg_id = 1; + result->product_info = product_info; + + IOTHUB_DEVICE_CONFIG deviceConfig; + deviceConfig.deviceId = config->deviceId; + deviceConfig.deviceKey = config->deviceKey; + deviceConfig.deviceSasToken = config->deviceSasToken; + deviceConfig.authorization_module = result->authorization_module; + deviceConfig.moduleId = module_id; + + /*Codes_SRS_IOTHUBCLIENT_LL_17_008: [IoTHubClientCore_LL_Create shall call the transport _Register function with a populated structure of type IOTHUB_DEVICE_CONFIG and waitingToSend list.] */ + if ((result->deviceHandle = result->IoTHubTransport_Register(result->transportHandle, &deviceConfig, result, &(result->waitingToSend))) == NULL) + { + LogError("Registering device in transport failed"); + IoTHubClient_Auth_Destroy(result->authorization_module); + // Codes_SRS_IOTHUBCLIENT_LL_09_010: [ If any failure occurs `IoTHubClientCore_LL_Create` shall destroy the `transportHandle` only if it has created it ] + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + destroy_blob_upload_module(result); + destroy_module_method_module(result); + tickcounter_destroy(result->tickCounter); + STRING_delete(product_info); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_042: [ By default, messages shall not timeout. ]*/ + result->currentMessageTimeout = 0; + result->current_device_twin_timeout = 0; + + result->diagnostic_setting.currentMessageNumber = 0; + result->diagnostic_setting.diagSamplingPercentage = 0; + /*Codes_SRS_IOTHUBCLIENT_LL_25_124: [ `IoTHubClientCore_LL_Create` shall set the default retry policy as Exponential backoff with jitter and if succeed and return a `non-NULL` handle. ]*/ + if (IoTHubClientCore_LL_SetRetryPolicy(result, IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, 0) != IOTHUB_CLIENT_OK) + { + LogError("Setting default retry policy in transport failed"); + result->IoTHubTransport_Unregister(result->deviceHandle); + IoTHubClient_Auth_Destroy(result->authorization_module); + // Codes_SRS_IOTHUBCLIENT_LL_09_010: [ If any failure occurs `IoTHubClientCore_LL_Create` shall destroy the `transportHandle` only if it has created it ] + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + destroy_blob_upload_module(result); + destroy_module_method_module(result); + tickcounter_destroy(result->tickCounter); + STRING_delete(product_info); + free(result); + result = NULL; + } + } + } + } + } + if (IoTHubName) + { + free(IoTHubName); + } + if (IoTHubSuffix) + { + free(IoTHubSuffix); + } + } + } + return result; +} + +static uint32_t get_next_item_id(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData) +{ + if (handleData->data_msg_id+1 >= UINT32_MAX) + { + handleData->data_msg_id = 1; + } + else + { + handleData->data_msg_id++; + } + return handleData->data_msg_id; +} + +static IOTHUB_DEVICE_TWIN* dev_twin_data_create(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, uint32_t id, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + IOTHUB_DEVICE_TWIN* result = (IOTHUB_DEVICE_TWIN*)malloc(sizeof(IOTHUB_DEVICE_TWIN) ); + if (result != NULL) + { + result->report_data_handle = CONSTBUFFER_Create(reportedState, size); + if (result->report_data_handle == NULL) + { + LogError("Failure allocating reported state data"); + free(result); + result = NULL; + } + else if (tickcounter_get_current_ms(handleData->tickCounter, &result->ms_timesOutAfter) != 0) + { + LogError("Failure getting tickcount info"); + CONSTBUFFER_Destroy(result->report_data_handle); + free(result); + result = NULL; + } + else + { + result->item_id = id; + result->ms_timesOutAfter = 0; + result->context = userContextCallback; + result->reported_state_callback = reportedStateCallback; + result->client_handle = handleData; + result->device_handle = handleData->deviceHandle; + } + } + else + { + LogError("Failure allocating device twin information"); + } + return result; +} + +static void delete_event(IOTHUB_EVENT_CALLBACK* event_callback) +{ + STRING_delete(event_callback->inputName); + free(event_callback->userContextCallbackEx); + free(event_callback); +} + +static void delete_event_callback(const void* item, const void* action_context, bool* continue_processing) +{ + (void)action_context; + delete_event((IOTHUB_EVENT_CALLBACK*)item); + *continue_processing = true; +} + +static void delete_event_callback_list(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData) +{ + if (handleData->event_callbacks != NULL) + { + singlylinkedlist_foreach(handleData->event_callbacks, delete_event_callback, NULL); + singlylinkedlist_destroy(handleData->event_callbacks); + handleData->event_callbacks = NULL; + } +} + + +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateFromDeviceAuth(const char* iothub_uri, const char* device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE result; + if (iothub_uri == NULL || protocol == NULL || device_id == NULL) + { + LogError("Input parameter is NULL: iothub_uri: %p protocol: %p device_id: %p", iothub_uri, protocol, device_id); + result = NULL; + } + else + { +#ifdef USE_PROV_MODULE + IOTHUB_CLIENT_CONFIG* config = (IOTHUB_CLIENT_CONFIG*)malloc(sizeof(IOTHUB_CLIENT_CONFIG)); + if (config == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_012: [If the allocation failed IoTHubClientCore_LL_CreateFromConnectionString returns NULL] */ + LogError("Malloc failed"); + result = NULL; + } + else + { + const char* iterator; + const char* initial; + char* iothub_name = NULL; + char* iothub_suffix = NULL; + + memset(config, 0, sizeof(IOTHUB_CLIENT_CONFIG)); + config->protocol = protocol; + config->deviceId = device_id; + + // Find the iothub suffix + initial = iterator = iothub_uri; + while (iterator != NULL && *iterator != '\0') + { + if (*iterator == '.') + { + size_t length = iterator - initial; + iothub_name = (char*)malloc(length + 1); + if (iothub_name != NULL) + { + memset(iothub_name, 0, length + 1); + memcpy(iothub_name, initial, length); + config->iotHubName = iothub_name; + + length = strlen(initial) - length - 1; + iothub_suffix = (char*)malloc(length + 1); + if (iothub_suffix != NULL) + { + memset(iothub_suffix, 0, length + 1); + memcpy(iothub_suffix, iterator + 1, length); + config->iotHubSuffix = iothub_suffix; + break; + } + else + { + LogError("Failed to allocate iothub suffix"); + free(iothub_name); + iothub_name = NULL; + result = NULL; + } + } + else + { + LogError("Failed to allocate iothub name"); + result = NULL; + } + } + iterator++; + } + + if (config->iotHubName == NULL || config->iotHubSuffix == NULL) + { + LogError("initialize iothub client"); + result = NULL; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = initialize_iothub_client(config, NULL, true, NULL); + if (handleData == NULL) + { + LogError("initialize iothub client"); + result = NULL; + } + else + { + result = handleData; + } + } + + free(iothub_name); + free(iothub_suffix); + free(config); + } +#else + LogError("HSM module is not included"); + result = NULL; +#endif + } + return result; +} + +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE result; + + /* Codes_SRS_IOTHUBCLIENT_LL_12_003: [IoTHubClientCore_LL_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL] */ + if (connectionString == NULL) + { + LogError("Input parameter is NULL: connectionString"); + result = NULL; + } + else if (protocol == NULL) + { + LogError("Input parameter is NULL: protocol"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_004: [IoTHubClientCore_LL_CreateFromConnectionString shall allocate IOTHUB_CLIENT_CONFIG structure] */ + IOTHUB_CLIENT_CONFIG* config = (IOTHUB_CLIENT_CONFIG*) malloc(sizeof(IOTHUB_CLIENT_CONFIG)); + if (config == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_012: [If the allocation failed IoTHubClientCore_LL_CreateFromConnectionString returns NULL] */ + LogError("Malloc failed"); + result = NULL; + } + else + { + STRING_TOKENIZER_HANDLE tokenizer1 = NULL; + STRING_HANDLE connString = NULL; + STRING_HANDLE tokenString = NULL; + STRING_HANDLE valueString = NULL; + STRING_HANDLE hostNameString = NULL; + STRING_HANDLE hostSuffixString = NULL; + STRING_HANDLE deviceIdString = NULL; + STRING_HANDLE deviceKeyString = NULL; + STRING_HANDLE deviceSasTokenString = NULL; + STRING_HANDLE protocolGateway = NULL; + STRING_HANDLE moduleId = NULL; + + memset(config, 0, sizeof(*config)); + config->protocol = protocol; + + /* Codes_SRS_IOTHUBCLIENT_LL_04_002: [If it does not, it shall pass the protocolGatewayHostName NULL.] */ + config->protocolGatewayHostName = NULL; + + if ((connString = STRING_construct(connectionString)) == NULL) + { + LogError("Error constructing connectiong String"); + result = NULL; + } + else if ((tokenizer1 = STRING_TOKENIZER_create(connString)) == NULL) + { + LogError("Error creating Tokenizer"); + result = NULL; + } + else if ((tokenString = STRING_new()) == NULL) + { + LogError("Error creating Token String"); + result = NULL; + } + else if ((valueString = STRING_new()) == NULL) + { + LogError("Error creating Value String"); + result = NULL; + } + else if ((hostNameString = STRING_new()) == NULL) + { + LogError("Error creating HostName String"); + result = NULL; + } + else if ((hostSuffixString = STRING_new()) == NULL) + { + LogError("Error creating HostSuffix String"); + result = NULL; + } + /* Codes_SRS_IOTHUBCLIENT_LL_12_005: [IoTHubClientCore_LL_CreateFromConnectionString shall try to parse the connectionString input parameter for the following structure: "Key1=value1;key2=value2;key3=value3..."] */ + /* Codes_SRS_IOTHUBCLIENT_LL_12_006: [IoTHubClientCore_LL_CreateFromConnectionString shall verify the existence of the following Key/Value pairs in the connection string: HostName, DeviceId, SharedAccessKey, SharedAccessSignature or x509] */ + else + { + int isx509found = 0; + bool use_provisioning = false; + while ((STRING_TOKENIZER_get_next_token(tokenizer1, tokenString, "=") == 0)) + { + if (STRING_TOKENIZER_get_next_token(tokenizer1, valueString, ";") != 0) + { + LogError("Tokenizer error"); + break; + } + else + { + if (tokenString != NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_010: [IoTHubClientCore_LL_CreateFromConnectionString shall fill up the IOTHUB_CLIENT_CONFIG structure using the following mapping: iotHubName = Name, iotHubSuffix = Suffix, deviceId = DeviceId, deviceKey = SharedAccessKey or deviceSasToken = SharedAccessSignature] */ + const char* s_token = STRING_c_str(tokenString); + if (strcmp(s_token, HOSTNAME_TOKEN) == 0) + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_009: [IoTHubClientCore_LL_CreateFromConnectionString shall split the value of HostName to Name and Suffix using the first "." as a separator] */ + STRING_TOKENIZER_HANDLE tokenizer2 = NULL; + if ((tokenizer2 = STRING_TOKENIZER_create(valueString)) == NULL) + { + LogError("Error creating Tokenizer"); + break; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_015: [If the string split failed, IoTHubClientCore_LL_CreateFromConnectionString returns NULL ] */ + if (STRING_TOKENIZER_get_next_token(tokenizer2, hostNameString, ".") != 0) + { + LogError("Tokenizer error"); + STRING_TOKENIZER_destroy(tokenizer2); + break; + } + else + { + config->iotHubName = STRING_c_str(hostNameString); + if (STRING_TOKENIZER_get_next_token(tokenizer2, hostSuffixString, ";") != 0) + { + LogError("Tokenizer error"); + STRING_TOKENIZER_destroy(tokenizer2); + break; + } + else + { + config->iotHubSuffix = STRING_c_str(hostSuffixString); + } + } + STRING_TOKENIZER_destroy(tokenizer2); + } + } + else if (strcmp(s_token, DEVICEID_TOKEN) == 0) + { + deviceIdString = STRING_clone(valueString); + if (deviceIdString != NULL) + { + config->deviceId = STRING_c_str(deviceIdString); + } + else + { + LogError("Failure cloning device id string"); + break; + } + } + else if (strcmp(s_token, DEVICEKEY_TOKEN) == 0) + { + deviceKeyString = STRING_clone(valueString); + if (deviceKeyString != NULL) + { + config->deviceKey = STRING_c_str(deviceKeyString); + } + else + { + LogError("Failure cloning device key string"); + break; + } + } + else if (strcmp(s_token, DEVICESAS_TOKEN) == 0) + { + deviceSasTokenString = STRING_clone(valueString); + if (deviceSasTokenString != NULL) + { + config->deviceSasToken = STRING_c_str(deviceSasTokenString); + } + else + { + LogError("Failure cloning device sasToken string"); + break; + } + } + else if (strcmp(s_token, X509_TOKEN) == 0) + { + if (strcmp(STRING_c_str(valueString), X509_TOKEN_ONLY_ACCEPTABLE_VALUE) != 0) + { + LogError("x509 option has wrong value, the only acceptable one is \"true\""); + break; + } + else + { + isx509found = 1; + } + } + else if (strcmp(s_token, PROVISIONING_TOKEN) == 0) + { + if (strcmp(STRING_c_str(valueString), PROVISIONING_ACCEPTABLE_VALUE) != 0) + { + LogError("provisioning option has wrong value, the only acceptable one is \"true\""); + break; + } + else + { + use_provisioning = 1; + } + } + + /* Codes_SRS_IOTHUBCLIENT_LL_04_001: [IoTHubClientCore_LL_CreateFromConnectionString shall verify the existence of key/value pair GatewayHostName. If it does exist it shall pass the value to IoTHubClientCore_LL_Create API.] */ + else if (strcmp(s_token, PROTOCOL_GATEWAY_HOST_TOKEN) == 0) + { + protocolGateway = STRING_clone(valueString); + if (protocolGateway != NULL) + { + config->protocolGatewayHostName = STRING_c_str(protocolGateway); + } + else + { + LogError("Failure cloning protocol Gateway Name"); + break; + } + } + /*Codes_SRS_IOTHUBCLIENT_LL_31_126: [IoTHubClient_LL_CreateFromConnectionString shall optionally parse ModuleId, if present.] */ + else if (strcmp(s_token, MODULE_ID_TOKEN) == 0) + { + moduleId = STRING_clone(valueString); + if (moduleId == NULL) + { + LogError("Failure cloning moduleId string"); + break; + } + } + } + } + } + /* parsing is done - check the result */ + if (config->iotHubName == NULL) + { + LogError("iotHubName is not found"); + result = NULL; + } + else if (config->iotHubSuffix == NULL) + { + LogError("iotHubSuffix is not found"); + result = NULL; + } + else if (config->deviceId == NULL) + { + LogError("deviceId is not found"); + result = NULL; + } + else if (!( + ((!use_provisioning && !isx509found) && (config->deviceSasToken == NULL) ^ (config->deviceKey == NULL)) || + ((use_provisioning || isx509found) && (config->deviceSasToken == NULL) && (config->deviceKey == NULL)) + )) + { + LogError("invalid combination of x509, provisioning, deviceSasToken and deviceKey"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_12_011: [IoTHubClientCore_LL_CreateFromConnectionString shall call into the IoTHubClientCore_LL_Create API with the current structure and returns with the return value of it] */ + result = initialize_iothub_client(config, NULL, use_provisioning, STRING_c_str(moduleId)); + if (result == NULL) + { + LogError("IoTHubClientCore_LL_Create failed"); + } + else + { + /*return as is*/ + } + } + } + if (deviceSasTokenString != NULL) + STRING_delete(deviceSasTokenString); + if (deviceKeyString != NULL) + STRING_delete(deviceKeyString); + if (deviceIdString != NULL) + STRING_delete(deviceIdString); + if (hostSuffixString != NULL) + STRING_delete(hostSuffixString); + if (hostNameString != NULL) + STRING_delete(hostNameString); + if (valueString != NULL) + STRING_delete(valueString); + if (tokenString != NULL) + STRING_delete(tokenString); + if (connString != NULL) + STRING_delete(connString); + if (protocolGateway != NULL) + STRING_delete(protocolGateway); + if (moduleId != NULL) + STRING_delete(moduleId); + + if (tokenizer1 != NULL) + STRING_TOKENIZER_destroy(tokenizer1); + + free(config); + } + } + return result; +} + +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateImpl(const IOTHUB_CLIENT_CONFIG* config, const char* module_id, bool use_dev_auth) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_001: [IoTHubClientCore_LL_Create shall return NULL if config parameter is NULL or protocol field is NULL.]*/ + if( + (config == NULL) || + (config->protocol == NULL) + ) + { + result = NULL; + LogError("invalid configuration (NULL detected)"); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = initialize_iothub_client(config, NULL, use_dev_auth, module_id); + if (handleData == NULL) + { + LogError("initialize iothub client"); + result = NULL; + } + else + { + result = handleData; + } + } + + return result; +} + +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + return IoTHubClientCore_LL_CreateImpl(config, NULL, false); +} + +#ifdef USE_EDGE_MODULES +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateFromEnvironment(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* result; + EDGE_ENVIRONMENT_VARIABLES edge_environment_variables; + + memset(&edge_environment_variables, 0, sizeof(edge_environment_variables)); + + if (retrieve_edge_environment_variabes(&edge_environment_variables) != 0) + { + LogError("retrieve_edge_environment_variabes failed"); + result = NULL; + } + // The presence of a connection string environment variable means we use it, ignoring other settings + else if (edge_environment_variables.connection_string != NULL) + { + result = IoTHubClientCore_LL_CreateFromConnectionString(edge_environment_variables.connection_string, protocol); + } + else if (iothub_security_init(IOTHUB_SECURITY_TYPE_HTTP_EDGE) != 0) + { + LogError("iothub_security_init failed"); + result = NULL; + } + else + { + IOTHUB_CLIENT_CONFIG client_config; + + memset(&client_config, 0, sizeof(client_config)); + client_config.protocol = protocol; + client_config.deviceId = edge_environment_variables.device_id; + client_config.iotHubName = edge_environment_variables.iothub_name; + client_config.iotHubSuffix = edge_environment_variables.iothub_suffix; + client_config.protocolGatewayHostName = edge_environment_variables.gatewayhostname; + + if ((result = IoTHubClientCore_LL_CreateImpl(&client_config, edge_environment_variables.module_id, true)) != NULL) + { + // Because the Edge Hub almost always use self-signed certificates, we need + // to query it for the the certificate its using so we can trust it. + char* trustedCertificate = IoTHubClient_Auth_Get_TrustBundle(result->authorization_module); + IOTHUB_CLIENT_RESULT setTrustResult; + + if (trustedCertificate == NULL) + { + LogError("IoTHubClient_Auth_Get_TrustBundle failed"); + IoTHubClientCore_LL_Destroy(result); + result = NULL; + } + else if ((setTrustResult = IoTHubClientCore_LL_SetOption(result, OPTION_TRUSTED_CERT, trustedCertificate)) != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetOption failed, err = %d", setTrustResult); + IoTHubClientCore_LL_Destroy(result); + result = NULL; + } + + free(trustedCertificate); + } + } + + free(edge_environment_variables.iothub_buffer); + return result; +} +#endif + + +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateWithTransport(const IOTHUB_CLIENT_DEVICE_CONFIG * config) +{ + IOTHUB_CLIENT_CORE_LL_HANDLE result; + /*Codes_SRS_IOTHUBCLIENT_LL_17_001: [IoTHubClientCore_LL_CreateWithTransport shall return NULL if config parameter is NULL, or protocol field is NULL or transportHandle is NULL.]*/ + if ( + (config == NULL) || + (config->protocol == NULL) || + (config->transportHandle == NULL) || + /*Codes_SRS_IOTHUBCLIENT_LL_02_098: [ IoTHubClientCore_LL_CreateWithTransport shall fail and return NULL if both config->deviceKey AND config->deviceSasToken are NULL. ]*/ + ((config->deviceKey == NULL) && (config->deviceSasToken == NULL)) + ) + { + result = NULL; + LogError("invalid configuration (NULL detected)"); + } + else + { + result = initialize_iothub_client(NULL, config, false, NULL); + } + return result; +} + +void IoTHubClientCore_LL_Destroy(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /*Codes_SRS_IOTHUBCLIENT_LL_02_009: [IoTHubClientCore_LL_Destroy shall do nothing if parameter iotHubClientHandle is NULL.]*/ + if (iotHubClientHandle != NULL) + { + PDLIST_ENTRY unsend; + /*Codes_SRS_IOTHUBCLIENT_LL_17_010: [IoTHubClientCore_LL_Destroy shall call the underlaying layer's _Unregister function] */ + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + handleData->IoTHubTransport_Unregister(handleData->deviceHandle); + if (handleData->isSharedTransport == false) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_010: [If iotHubClientHandle was not created by IoTHubClientCore_LL_CreateWithTransport, IoTHubClientCore_LL_Destroy shall call the underlaying layer's _Destroy function.] */ + handleData->IoTHubTransport_Destroy(handleData->transportHandle); + } + /*if any, remove the items currently not send*/ + while ((unsend = DList_RemoveHeadList(&(handleData->waitingToSend))) != &(handleData->waitingToSend)) + { + IOTHUB_MESSAGE_LIST* temp = containingRecord(unsend, IOTHUB_MESSAGE_LIST, entry); + /*Codes_SRS_IOTHUBCLIENT_LL_02_033: [Otherwise, IoTHubClientCore_LL_Destroy shall complete all the event message callbacks that are in the waitingToSend list with the result IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY.] */ + if (temp->callback != NULL) + { + temp->callback(IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY, temp->context); + } + IoTHubMessage_Destroy(temp->messageHandle); + free(temp); + } + + /* Codes_SRS_IOTHUBCLIENT_LL_07_007: [ IoTHubClientCore_LL_Destroy shall iterate the device twin queues and destroy any remaining items. ] */ + while ((unsend = DList_RemoveHeadList(&(handleData->iot_msg_queue))) != &(handleData->iot_msg_queue)) + { + IOTHUB_DEVICE_TWIN* temp = containingRecord(unsend, IOTHUB_DEVICE_TWIN, entry); + device_twin_data_destroy(temp); + } + while ((unsend = DList_RemoveHeadList(&(handleData->iot_ack_queue))) != &(handleData->iot_ack_queue)) + { + IOTHUB_DEVICE_TWIN* temp = containingRecord(unsend, IOTHUB_DEVICE_TWIN, entry); + device_twin_data_destroy(temp); + } + + /* Codes_SRS_IOTHUBCLIENT_LL_31_141: [ IoTHubClient_LL_Destroy shall iterate registered callbacks for input queues and destroy any remaining items. ] */ + delete_event_callback_list(handleData); + + /*Codes_SRS_IOTHUBCLIENT_LL_17_011: [IoTHubClientCore_LL_Destroy shall free the resources allocated by IoTHubClient (if any).] */ + IoTHubClient_Auth_Destroy(handleData->authorization_module); + tickcounter_destroy(handleData->tickCounter); +#ifndef DONT_USE_UPLOADTOBLOB + IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle); +#endif +#ifdef USE_EDGE_MODULES + IoTHubClient_EdgeHandle_Destroy(handleData->methodHandle); +#endif + STRING_delete(handleData->product_info); + free(handleData); + } +} + +/*Codes_SRS_IOTHUBCLIENT_LL_02_044: [ Messages already delivered to IoTHubClientCore_LL shall not have their timeouts modified by a new call to IoTHubClientCore_LL_SetOption. ]*/ +/*returns 0 on success, any other value is error*/ +static int attach_ms_timesOutAfter(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, IOTHUB_MESSAGE_LIST *newEntry) +{ + int result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_043: [ Calling IoTHubClientCore_LL_SetOption with value set to "0" shall disable the timeout mechanism for all new messages. ]*/ + if (handleData->currentMessageTimeout == 0) + { + newEntry->ms_timesOutAfter = 0; /*do not timeout*/ + result = 0; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_039: [ "messageTimeout" - once IoTHubClientCore_LL_SendEventAsync is called the message shall timeout after value miliseconds. Value is a pointer to a tickcounter_ms_t. ]*/ + if (tickcounter_get_current_ms(handleData->tickCounter, &newEntry->ms_timesOutAfter) != 0) + { + result = __FAILURE__; + LogError("unable to get the current relative tickcount"); + } + else + { + newEntry->ms_timesOutAfter += handleData->currentMessageTimeout; + result = 0; + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SendEventAsync(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_011: [IoTHubClientCore_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle or eventMessageHandle is NULL.]*/ + if ( + (iotHubClientHandle == NULL) || + (eventMessageHandle == NULL) || + /*Codes_SRS_IOTHUBCLIENT_LL_02_012: [IoTHubClientCore_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter eventConfirmationCallback is NULL and userContextCallback is not NULL.] */ + ((eventConfirmationCallback == NULL) && (userContextCallback != NULL)) + ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_MESSAGE_LIST *newEntry = (IOTHUB_MESSAGE_LIST*)malloc(sizeof(IOTHUB_MESSAGE_LIST)); + if (newEntry == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + if (attach_ms_timesOutAfter(handleData, newEntry) != 0) + { + result = IOTHUB_CLIENT_ERROR; + LOG_ERROR_RESULT; + free(newEntry); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_013: [IoTHubClientCore_LL_SendEventAsync shall add the DLIST waitingToSend a new record cloning the information from eventMessageHandle, eventConfirmationCallback, userContextCallback.]*/ + if ((newEntry->messageHandle = IoTHubMessage_Clone(eventMessageHandle)) == NULL) + { + result = IOTHUB_CLIENT_ERROR; + free(newEntry); + LOG_ERROR_RESULT; + } + else if (IoTHubClient_Diagnostic_AddIfNecessary(&handleData->diagnostic_setting, newEntry->messageHandle) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_014: [If cloning and/or adding the information/diagnostic fails for any reason, IoTHubClientCore_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + IoTHubMessage_Destroy(newEntry->messageHandle); + free(newEntry); + LOG_ERROR_RESULT; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_013: [IoTHubClientCore_LL_SendEventAsync shall add the DLIST waitingToSend a new record cloning the information from eventMessageHandle, eventConfirmationCallback, userContextCallback.]*/ + newEntry->callback = eventConfirmationCallback; + newEntry->context = userContextCallback; + DList_InsertTailList(&(iotHubClientHandle->waitingToSend), &(newEntry->entry)); + /*Codes_SRS_IOTHUBCLIENT_LL_02_015: [Otherwise IoTHubClientCore_LL_SendEventAsync shall succeed and return IOTHUB_CLIENT_OK.] */ + result = IOTHUB_CLIENT_OK; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetMessageCallback(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubClientHandle == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_016: [IoTHubClientCore_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.] */ + LogError("Invalid argument - iotHubClientHandle is NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if (messageCallback == NULL) + { + if (handleData->messageCallback.type == CALLBACK_TYPE_NONE) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_010: [If parameter messageCallback is NULL and the _SetMessageCallback had not been called to subscribe for messages, then IoTHubClientCore_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("not currently set to accept or process incoming messages."); + result = IOTHUB_CLIENT_ERROR; + } + else if (handleData->messageCallback.type == CALLBACK_TYPE_ASYNC) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_010: [If parameter messageCallback is NULL and the _SetMessageCallback had not been called to subscribe for messages, then IoTHubClientCore_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetMessageCallback_Ex function."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_019: [If parameter messageCallback is NULL then IoTHubClientCore_LL_SetMessageCallback shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */ + handleData->IoTHubTransport_Unsubscribe(handleData->deviceHandle); + handleData->messageCallback.type = CALLBACK_TYPE_NONE; + handleData->messageCallback.callbackSync = NULL; + handleData->messageCallback.callbackAsync = NULL; + handleData->messageCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_OK; + } + } + else + { + if (handleData->messageCallback.type == CALLBACK_TYPE_ASYNC) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_011: [If parameter messageCallback is non-NULL and the _SetMessageCallback_Ex had been used to susbscribe for messages, then IoTHubClientCore_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetMessageCallback_Ex function before subscribing with MessageCallback."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (handleData->IoTHubTransport_Subscribe(handleData->deviceHandle) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_017: [If parameter messageCallback is non-NULL then IoTHubClientCore_LL_SetMessageCallback shall call the underlying layer's _Subscribe function.]*/ + handleData->messageCallback.type = CALLBACK_TYPE_SYNC; + handleData->messageCallback.callbackSync = messageCallback; + handleData->messageCallback.userContextCallback = userContextCallback; + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_018: [If the underlying layer's _Subscribe function fails, then IoTHubClientCore_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_ERROR. Otherwise IoTHubClientCore_LL_SetMessageCallback shall succeed and return IOTHUB_CLIENT_OK.]*/ + LogError("IoTHubTransport_Subscribe failed"); + handleData->messageCallback.type = CALLBACK_TYPE_NONE; + handleData->messageCallback.callbackSync = NULL; + handleData->messageCallback.callbackAsync = NULL; + handleData->messageCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_ERROR; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetMessageCallback_Ex(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX messageCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubClientHandle == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_021: [IoTHubClientCore_LL_SetMessageCallback_Ex shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.]*/ + LogError("Invalid argument - iotHubClientHandle is NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if (messageCallback == NULL) + { + if (handleData->messageCallback.type == CALLBACK_TYPE_NONE) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_018: [If parameter messageCallback is NULL and IoTHubClientCore_LL_SetMessageCallback_Ex had not been used to subscribe for messages, then IoTHubClientCore_LL_SetMessageCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("not currently set to accept or process incoming messages."); + result = IOTHUB_CLIENT_ERROR; + } + else if (handleData->messageCallback.type == CALLBACK_TYPE_SYNC) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_019: [If parameter messageCallback is NULL and IoTHubClientCore_LL_SetMessageCallback had been used to subscribe for messages, then IoTHubClientCore_LL_SetMessageCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetMessageCallback function."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_023: [If parameter messageCallback is NULL then IoTHubClientCore_LL_SetMessageCallback_Ex shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */ + handleData->IoTHubTransport_Unsubscribe(handleData->deviceHandle); + handleData->messageCallback.type = CALLBACK_TYPE_NONE; + handleData->messageCallback.callbackSync = NULL; + handleData->messageCallback.callbackAsync = NULL; + handleData->messageCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_OK; + } + } + else + { + if (handleData->messageCallback.type == CALLBACK_TYPE_SYNC) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_020: [If parameter messageCallback is non-NULL, and IoTHubClientCore_LL_SetMessageCallback had been used to subscribe for messages, then IoTHubClientCore_LL_SetMessageCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_MessageCallbackEx function before subscribing with MessageCallback."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (handleData->IoTHubTransport_Subscribe(handleData->deviceHandle) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_024: [If parameter messageCallback is non-NULL then IoTHubClientCore_LL_SetMessageCallback_Ex shall call the underlying layer's _Subscribe function.]*/ + handleData->messageCallback.type = CALLBACK_TYPE_ASYNC; + handleData->messageCallback.callbackAsync = messageCallback; + handleData->messageCallback.userContextCallback = userContextCallback; + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_025: [If the underlying layer's _Subscribe function fails, then IoTHubClientCore_LL_SetMessageCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR. Otherwise IoTHubClientCore_LL_SetMessageCallback_Ex shall succeed and return IOTHUB_CLIENT_OK.] */ + LogError("IoTHubTransport_Subscribe failed"); + handleData->messageCallback.type = CALLBACK_TYPE_NONE; + handleData->messageCallback.callbackSync = NULL; + handleData->messageCallback.callbackAsync = NULL; + handleData->messageCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_ERROR; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SendMessageDisposition(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + IOTHUB_CLIENT_RESULT result; + if ((iotHubClientHandle == NULL) || (message_data == NULL)) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_026: [IoTHubClientCore_LL_SendMessageDisposition shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.]*/ + LogError("Invalid argument handle=%p, message=%p", iotHubClientHandle, message_data); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + /*Codes_SRS_IOTHUBCLIENT_LL_10_027: [IoTHubClientCore_LL_SendMessageDisposition shall return the result from calling the underlying layer's _Send_Message_Disposition.]*/ + result = handleData->IoTHubTransport_SendMessageDisposition(message_data, disposition); + } + return result; +} + +static void DoTimeouts(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData) +{ + tickcounter_ms_t nowTick; + if (tickcounter_get_current_ms(handleData->tickCounter, &nowTick) != 0) + { + LogError("unable to get the current ms, timeouts will not be processed"); + } + else + { + DLIST_ENTRY* currentItemInWaitingToSend = handleData->waitingToSend.Flink; + while (currentItemInWaitingToSend != &(handleData->waitingToSend)) /*while we are not at the end of the list*/ + { + IOTHUB_MESSAGE_LIST* fullEntry = containingRecord(currentItemInWaitingToSend, IOTHUB_MESSAGE_LIST, entry); + /*Codes_SRS_IOTHUBCLIENT_LL_02_041: [ If more than value miliseconds have passed since the call to IoTHubClientCore_LL_SendEventAsync then the message callback shall be called with a status code of IOTHUB_CLIENT_CONFIRMATION_TIMEOUT. ]*/ + if ((fullEntry->ms_timesOutAfter != 0) && (fullEntry->ms_timesOutAfter < nowTick)) + { + PDLIST_ENTRY theNext = currentItemInWaitingToSend->Flink; /*need to save the next item, because the below operations are destructive*/ + DList_RemoveEntryList(currentItemInWaitingToSend); + if (fullEntry->callback != NULL) + { + fullEntry->callback(IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT, fullEntry->context); + } + IoTHubMessage_Destroy(fullEntry->messageHandle); /*because it has been cloned*/ + free(fullEntry); + currentItemInWaitingToSend = theNext; + } + else + { + currentItemInWaitingToSend = currentItemInWaitingToSend->Flink; + } + } + } +} + +void IoTHubClientCore_LL_DoWork(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /*Codes_SRS_IOTHUBCLIENT_LL_02_020: [If parameter iotHubClientHandle is NULL then IoTHubClientCore_LL_DoWork shall not perform any action.] */ + if (iotHubClientHandle != NULL) + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + DoTimeouts(handleData); + + /*Codes_SRS_IOTHUBCLIENT_LL_07_008: [ IoTHubClientCore_LL_DoWork shall iterate the message queue and execute the underlying transports IoTHubTransport_ProcessItem function for each item. ] */ + DLIST_ENTRY* client_item = handleData->iot_msg_queue.Flink; + while (client_item != &(handleData->iot_msg_queue)) /*while we are not at the end of the list*/ + { + PDLIST_ENTRY next_item = client_item->Flink; + + IOTHUB_DEVICE_TWIN* queue_data = containingRecord(client_item, IOTHUB_DEVICE_TWIN, entry); + IOTHUB_IDENTITY_INFO identity_info; + identity_info.device_twin = queue_data; + IOTHUB_PROCESS_ITEM_RESULT process_results = handleData->IoTHubTransport_ProcessItem(handleData->transportHandle, IOTHUB_TYPE_DEVICE_TWIN, &identity_info); + if (process_results == IOTHUB_PROCESS_CONTINUE || process_results == IOTHUB_PROCESS_NOT_CONNECTED) + { + /*Codes_SRS_IOTHUBCLIENT_LL_07_010: [ If 'IoTHubTransport_ProcessItem' returns IOTHUB_PROCESS_CONTINUE or IOTHUB_PROCESS_NOT_CONNECTED IoTHubClientCore_LL_DoWork shall continue on to call the underlaying layer's _DoWork function. ]*/ + break; + } + else + { + DList_RemoveEntryList(client_item); + if (process_results == IOTHUB_PROCESS_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_07_011: [ If 'IoTHubTransport_ProcessItem' returns IOTHUB_PROCESS_OK IoTHubClientCore_LL_DoWork shall add the IOTHUB_DEVICE_TWIN to the ack queue. ]*/ + DList_InsertTailList(&(iotHubClientHandle->iot_ack_queue), &(queue_data->entry)); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_07_012: [ If 'IoTHubTransport_ProcessItem' returns any other value IoTHubClientCore_LL_DoWork shall destroy the IOTHUB_DEVICE_TWIN item. ]*/ + LogError("Failure queue processing item"); + device_twin_data_destroy(queue_data); + } + } + // Move along to the next item + client_item = next_item; + } + + /*Codes_SRS_IOTHUBCLIENT_LL_02_021: [Otherwise, IoTHubClientCore_LL_DoWork shall invoke the underlaying layer's _DoWork function.]*/ + handleData->IoTHubTransport_DoWork(handleData->transportHandle, iotHubClientHandle); + } +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GetSendStatus(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + + /* Codes_SRS_IOTHUBCLIENT_09_007: [IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter] */ + if (iotHubClientHandle == NULL || iotHubClientStatus == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_09_008: [IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there is currently no items to be sent] */ + /* Codes_SRS_IOTHUBCLIENT_09_009: [IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently items to be sent] */ + result = handleData->IoTHubTransport_GetSendStatus(handleData->deviceHandle, iotHubClientStatus); + } + + return result; +} + +void IoTHubClientCore_LL_SendComplete(IOTHUB_CLIENT_CORE_LL_HANDLE handle, PDLIST_ENTRY completed, IOTHUB_CLIENT_CONFIRMATION_RESULT result) +{ + /*Codes_SRS_IOTHUBCLIENT_LL_02_022: [If parameter completed is NULL, or parameter handle is NULL then IoTHubClientCore_LL_SendBatch shall return.]*/ + if ( + (handle == NULL) || + (completed == NULL) + ) + { + /*"shall return"*/ + LogError("invalid arg"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_027: [If parameter result is IOTHUB_CLIENT_CONFIRMATION_ERROR then IoTHubClientCore_LL_SendComplete shall call all the non-NULL callbacks with the result parameter set to IOTHUB_CLIENT_CONFIRMATION_ERROR and the context set to the context passed originally in the SendEventAsync call.] */ + /*Codes_SRS_IOTHUBCLIENT_LL_02_025: [If parameter result is IOTHUB_CLIENT_CONFIRMATION_OK then IoTHubClientCore_LL_SendComplete shall call all the non-NULL callbacks with the result parameter set to IOTHUB_CLIENT_CONFIRMATION_OK and the context set to the context passed originally in the SendEventAsync call.]*/ + PDLIST_ENTRY oldest; + while ((oldest = DList_RemoveHeadList(completed)) != completed) + { + IOTHUB_MESSAGE_LIST* messageList = (IOTHUB_MESSAGE_LIST*)containingRecord(oldest, IOTHUB_MESSAGE_LIST, entry); + /*Codes_SRS_IOTHUBCLIENT_LL_02_026: [If any callback is NULL then there shall not be a callback call.]*/ + if (messageList->callback != NULL) + { + messageList->callback(result, messageList->context); + } + IoTHubMessage_Destroy(messageList->messageHandle); + free(messageList); + } + } +} + +int IoTHubClientCore_LL_DeviceMethodComplete(IOTHUB_CLIENT_CORE_LL_HANDLE handle, const char* method_name, const unsigned char* payLoad, size_t size, METHOD_HANDLE response_id) +{ + int result; + if (handle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_017: [ If handle or response is NULL then IoTHubClientCore_LL_DeviceMethodComplete shall return 500. ] */ + LogError("Invalid argument handle=%p", handle); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_018: [ If deviceMethodCallback is not NULL IoTHubClientCore_LL_DeviceMethodComplete shall execute deviceMethodCallback and return the status. ] */ + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + switch (handleData->methodCallback.type) + { + case CALLBACK_TYPE_SYNC: + { + unsigned char* payload_resp = NULL; + size_t response_size = 0; + result = handleData->methodCallback.callbackSync(method_name, payLoad, size, &payload_resp, &response_size, handleData->methodCallback.userContextCallback); + /* Codes_SRS_IOTHUBCLIENT_LL_07_020: [ deviceMethodCallback shall build the BUFFER_HANDLE with the response payload from the IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC callback. ] */ + if (payload_resp != NULL && response_size > 0) + { + result = handleData->IoTHubTransport_DeviceMethod_Response(handleData->deviceHandle, response_id, payload_resp, response_size, result); + } + else + { + result = __FAILURE__; + } + if (payload_resp != NULL) + { + free(payload_resp); + } + break; + } + case CALLBACK_TYPE_ASYNC: + result = handleData->methodCallback.callbackAsync(method_name, payLoad, size, response_id, handleData->methodCallback.userContextCallback); + break; + default: + /* Codes_SRS_IOTHUBCLIENT_LL_07_019: [ If deviceMethodCallback is NULL IoTHubClientCore_LL_DeviceMethodComplete shall return 404. ] */ + result = 0; + break; + } + } + return result; +} + +void IoTHubClientCore_LL_RetrievePropertyComplete(IOTHUB_CLIENT_CORE_LL_HANDLE handle, DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size) +{ + if (handle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_013: [ If handle is NULL then IoTHubClientCore_LL_RetrievePropertyComplete shall do nothing.] */ + LogError("Invalid argument handle=%p", handle); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + /* Codes_SRS_IOTHUBCLIENT_LL_07_014: [ If deviceTwinCallback is NULL then IoTHubClientCore_LL_RetrievePropertyComplete shall do nothing.] */ + if (handleData->deviceTwinCallback) + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_015: [ If the the update_state parameter is DEVICE_TWIN_UPDATE_PARTIAL and a DEVICE_TWIN_UPDATE_COMPLETE has not been previously recieved then IoTHubClientCore_LL_RetrievePropertyComplete shall do nothing.] */ + if (update_state == DEVICE_TWIN_UPDATE_COMPLETE) + { + handleData->complete_twin_update_encountered = true; + } + if (handleData->complete_twin_update_encountered) + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_016: [ If deviceTwinCallback is set and DEVICE_TWIN_UPDATE_COMPLETE has been encountered then IoTHubClientCore_LL_RetrievePropertyComplete shall call deviceTwinCallback.] */ + handleData->deviceTwinCallback(update_state, payLoad, size, handleData->deviceTwinContextCallback); + } + } + } +} + +void IoTHubClientCore_LL_ReportedStateComplete(IOTHUB_CLIENT_CORE_LL_HANDLE handle, uint32_t item_id, int status_code) +{ + /* Codes_SRS_IOTHUBCLIENT_LL_07_002: [ if handle or queue_handle are NULL then IoTHubClientCore_LL_ReportedStateComplete shall do nothing. ] */ + if (handle == NULL) + { + /*"shall return"*/ + LogError("Invalid argument handle=%p", handle); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + + /* Codes_SRS_IOTHUBCLIENT_LL_07_003: [ IoTHubClientCore_LL_ReportedStateComplete shall enumerate through the IOTHUB_DEVICE_TWIN structures in queue_handle. ]*/ + DLIST_ENTRY* client_item = handleData->iot_ack_queue.Flink; + while (client_item != &(handleData->iot_ack_queue)) /*while we are not at the end of the list*/ + { + PDLIST_ENTRY next_item = client_item->Flink; + IOTHUB_DEVICE_TWIN* queue_data = containingRecord(client_item, IOTHUB_DEVICE_TWIN, entry); + if (queue_data->item_id == item_id) + { + if (queue_data->reported_state_callback != NULL) + { + queue_data->reported_state_callback(status_code, queue_data->context); + } + /*Codes_SRS_IOTHUBCLIENT_LL_07_009: [ IoTHubClientCore_LL_ReportedStateComplete shall remove the IOTHUB_DEVICE_TWIN item from the ack queue.]*/ + DList_RemoveEntryList(client_item); + device_twin_data_destroy(queue_data); + break; + } + client_item = next_item; + } + } +} + +static bool invoke_message_callback(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, MESSAGE_CALLBACK_INFO* messageData) +{ + bool result; + /* Codes_SRS_IOTHUBCLIENT_LL_09_004: [IoTHubClient_LL_GetLastMessageReceiveTime shall return lastMessageReceiveTime in localtime] */ + handleData->lastMessageReceiveTime = get_time(NULL); + + switch (handleData->messageCallback.type) + { + case CALLBACK_TYPE_NONE: + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_032: [If the client is not subscribed to receive messages then IoTHubClient_LL_MessageCallback shall return false.] */ + LogError("Invalid workflow - not currently set up to accept messages"); + result = false; + break; + } + case CALLBACK_TYPE_SYNC: + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_030: [If messageCallbackType is LEGACY then IoTHubClient_LL_MessageCallback shall invoke the last callback function (the parameter messageCallback to IoTHubClient_LL_SetMessageCallback) passing the message and the passed userContextCallback.]*/ + IOTHUBMESSAGE_DISPOSITION_RESULT cb_result = handleData->messageCallback.callbackSync(messageData->messageHandle, handleData->messageCallback.userContextCallback); + + /*Codes_SRS_IOTHUBCLIENT_LL_10_007: [If messageCallbackType is LEGACY then IoTHubClient_LL_MessageCallback shall send the message disposition as returned by the client to the underlying layer.] */ + if (handleData->IoTHubTransport_SendMessageDisposition(messageData, cb_result) != IOTHUB_CLIENT_OK) + { + LogError("IoTHubTransport_SendMessageDisposition failed"); + } + result = true; + break; + } + case CALLBACK_TYPE_ASYNC: + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_009: [If messageCallbackType is ASYNC then IoTHubClient_LL_MessageCallback shall return what messageCallbacEx returns.] */ + result = handleData->messageCallback.callbackAsync(messageData, handleData->messageCallback.userContextCallback); + if (!result) + { + LogError("messageCallbackEx failed"); + } + break; + } + default: + { + LogError("Invalid state"); + result = false; + break; + } + } + + return result; +} + +bool IoTHubClientCore_LL_MessageCallback(IOTHUB_CLIENT_CORE_LL_HANDLE handle, MESSAGE_CALLBACK_INFO* messageData) +{ + bool result; + if ((handle == NULL) || messageData == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_029: [If parameter handle is NULL then IoTHubClient_LL_MessageCallback shall return IOTHUBMESSAGE_ABANDONED.] */ + LogError("invalid argument: handle(%p), messageData(%p)", handle, messageData); + result = false; + } + else if (messageData->messageHandle == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_004: [If messageHandle field of paramger messageData is NULL then IoTHubClient_LL_MessageCallback shall return IOTHUBMESSAGE_ABANDONED.] */ + LogError("invalid argument messageData->messageHandle(NULL)"); + result = false; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + return invoke_message_callback(handleData, messageData); + } + return result; +} + +static bool is_event_equal(IOTHUB_EVENT_CALLBACK *event_callback, const char *input_name) +{ + bool result; + + if (event_callback != NULL) + { + const char* event_input_name = STRING_c_str(event_callback->inputName); + if ((event_input_name != NULL) && (input_name != NULL)) + { + // Matched the input queue name of a named handler + result = (strcmp(event_input_name, input_name) == 0); + } + else if ((input_name == NULL) && (event_input_name == NULL)) + { + // Matched the default handler + result = true; + } + else + { + result = false; + } + } + else + { + result = false; + } + return result; +} + +static bool is_event_equal_for_match(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + return is_event_equal((IOTHUB_EVENT_CALLBACK*)singlylinkedlist_item_get_value(list_item), (const char*)match_context); +} + +bool IoTHubClientCore_LL_MessageCallbackFromInput(IOTHUB_CLIENT_CORE_LL_HANDLE handle, MESSAGE_CALLBACK_INFO* messageData) +{ + bool result; + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + + if ((handle == NULL) || messageData == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_137: [ If either parameter `handle` or `messageData` is `NULL` then `IoTHubClient_LL_MessageCallbackFromInput` shall return `false`.** ] + LogError("invalid argument: handle(%p), messageData(%p)", handle, messageData); + result = false; + } + else if (messageData->messageHandle == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_137: [ If either parameter `handle` or `messageData` is `NULL` then `IoTHubClient_LL_MessageCallbackFromInput` shall return `false`.** ] + LogError("invalid argument messageData->messageHandle(NULL)"); + result = false; + } + else if (handleData->event_callbacks == NULL) + { + LogError("Callback from input called but no input specific callbacks registered"); + result = false; + } + else + { + const char* inputName = IoTHubMessage_GetInputName(messageData->messageHandle); + + LIST_ITEM_HANDLE item_handle = NULL; + + item_handle = singlylinkedlist_find(handleData->event_callbacks, is_event_equal_for_match, (const void*)inputName); + + if (item_handle == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_138: [ If there is no registered handler for the inputName from `IoTHubMessage_GetInputName`, then `IoTHubClient_LL_MessageCallbackFromInput` shall attempt invoke the default handler handler.** ] + item_handle = singlylinkedlist_find(handleData->event_callbacks, is_event_equal_for_match, NULL); + } + + if (item_handle == NULL) + { + LogError("Could not find callback (explicit or default) for input queue %s", inputName); + result = false; + } + else + { + IOTHUB_EVENT_CALLBACK* event_callback = (IOTHUB_EVENT_CALLBACK*)singlylinkedlist_item_get_value(item_handle); + if (NULL == event_callback) + { + LogError("singlylinkedlist_item_get_value for event_callback failed"); + result = false; + } + else + { + // Codes_SRS_IOTHUBCLIENT_LL_09_004: [IoTHubClient_LL_GetLastMessageReceiveTime shall return lastMessageReceiveTime in localtime] + handleData->lastMessageReceiveTime = get_time(NULL); + + if (event_callback->callbackAsyncEx != NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_139: [ `IoTHubClient_LL_MessageCallbackFromInput` shall the callback from the given inputName queue if it has been registered.** ] + result = event_callback->callbackAsyncEx(messageData, event_callback->userContextCallbackEx); + } + else + { + // Codes_SRS_IOTHUBCLIENT_LL_31_139: [ `IoTHubClient_LL_MessageCallbackFromInput` shall the callback from the given inputName queue if it has been registered.** ] + IOTHUBMESSAGE_DISPOSITION_RESULT cb_result = event_callback->callbackAsync(messageData->messageHandle, event_callback->userContextCallback); + + // Codes_SRS_IOTHUBCLIENT_LL_31_140: [ `IoTHubClient_LL_MessageCallbackFromInput` shall send the message disposition as returned by the client to the underlying layer and return `true` if an input queue match is found.** ] + if (handleData->IoTHubTransport_SendMessageDisposition(messageData, cb_result) != IOTHUB_CLIENT_OK) + { + LogError("IoTHubTransport_SendMessageDisposition failed"); + } + result = true; + } + } + } + } + + return result; +} + + +void IoTHubClientCore_LL_ConnectionStatusCallBack(IOTHUB_CLIENT_CORE_LL_HANDLE handle, IOTHUB_CLIENT_CONNECTION_STATUS status, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason) +{ + /*Codes_SRS_IOTHUBCLIENT_LL_25_113: [If parameter connectionStatus is NULL or parameter handle is NULL then IoTHubClientCore_LL_ConnectionStatusCallBack shall return.]*/ + if (handle == NULL) + { + /*"shall return"*/ + LogError("invalid arg"); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)handle; + + /*Codes_SRS_IOTHUBCLIENT_LL_25_114: [IoTHubClientCore_LL_ConnectionStatusCallBack shall call non-callback set by the user from IoTHubClientCore_LL_SetConnectionStatusCallback passing the status, reason and the passed userContextCallback.]*/ + if (handleData->conStatusCallback != NULL) + { + handleData->conStatusCallback(status, reason, handleData->conStatusUserContextCallback); + } + } + +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetConnectionStatusCallback(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_25_111: [IoTHubClientCore_LL_SetConnectionStatusCallback shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter iotHubClientHandle**]** */ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + /*Codes_SRS_IOTHUBCLIENT_LL_25_112: [IoTHubClientCore_LL_SetConnectionStatusCallback shall return IOTHUB_CLIENT_OK and save the callback and userContext as a member of the handle.] */ + handleData->conStatusCallback = connectionStatusCallback; + handleData->conStatusUserContextCallback = userContextCallback; + result = IOTHUB_CLIENT_OK; + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetRetryPolicy(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_LL_25_116: [If iotHubClientHandle, retryPolicy or retryTimeoutLimitinSeconds is NULL, IoTHubClientCore_LL_GetRetryPolicy shall return IOTHUB_CLIENT_INVALID_ARG]*/ + if (handleData == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + if (handleData->transportHandle == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LOG_ERROR_RESULT; + } + else + { + if (handleData->IoTHubTransport_SetRetryPolicy(handleData->transportHandle, retryPolicy, retryTimeoutLimitInSeconds) != 0) + { + result = IOTHUB_CLIENT_ERROR; + LOG_ERROR_RESULT; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_25_118: [IoTHubClientCore_LL_SetRetryPolicy shall save connection retry policies specified by the user to retryPolicy in struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA] */ + /* Codes_SRS_IOTHUBCLIENT_LL_25_119: [IoTHubClientCore_LL_SetRetryPolicy shall save retryTimeoutLimitInSeconds in seconds to retryTimeout in struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA] */ + handleData->retryPolicy = retryPolicy; + handleData->retryTimeoutLimitInSeconds = retryTimeoutLimitInSeconds; + result = IOTHUB_CLIENT_OK; + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GetRetryPolicy(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + + /* Codes_SRS_IOTHUBCLIENT_LL_09_001: [IoTHubClientCore_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG if any of the arguments is NULL] */ + if (iotHubClientHandle == NULL || retryPolicy == NULL || retryTimeoutLimitInSeconds == NULL) + { + LogError("Invalid parameter IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle = %p, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy = %p, size_t* retryTimeoutLimitInSeconds = %p", iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + *retryPolicy = handleData->retryPolicy; + *retryTimeoutLimitInSeconds = handleData->retryTimeoutLimitInSeconds; + result = IOTHUB_CLIENT_OK; + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GetLastMessageReceiveTime(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) +{ + IOTHUB_CLIENT_RESULT result; + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + /* Codes_SRS_IOTHUBCLIENT_LL_09_001: [IoTHubClientCore_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG if any of the arguments is NULL] */ + if (handleData == NULL || lastMessageReceiveTime == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_09_002: [IoTHubClientCore_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INDEFINITE_TIME - and not set 'lastMessageReceiveTime' - if it is unable to provide the time for the last commands] */ + if (handleData->lastMessageReceiveTime == INDEFINITE_TIME) + { + result = IOTHUB_CLIENT_INDEFINITE_TIME; + LOG_ERROR_RESULT; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_09_003: [IoTHubClientCore_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_OK if it wrote in the lastMessageReceiveTime the time when the last command was received] */ + /* Codes_SRS_IOTHUBCLIENT_LL_09_004: [IoTHubClientCore_LL_GetLastMessageReceiveTime shall return lastMessageReceiveTime in localtime] */ + *lastMessageReceiveTime = handleData->lastMessageReceiveTime; + result = IOTHUB_CLIENT_OK; + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetOption(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* optionName, const void* value) +{ + + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_034: [If iotHubClientHandle is NULL then IoTHubClientCore_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_035: [If optionName is NULL then IoTHubClientCore_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + /*Codes_SRS_IOTHUBCLIENT_LL_02_036: [If value is NULL then IoTHubClientCore_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + if ( + (iotHubClientHandle == NULL) || + (optionName == NULL) || + (value == NULL) + ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid argument (NULL)"); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_039: [ "messageTimeout" - once IoTHubClientCore_LL_SendEventAsync is called the message shall timeout after value miliseconds. Value is a pointer to a tickcounter_ms_t. ]*/ + if (strcmp(optionName, OPTION_MESSAGE_TIMEOUT) == 0) + { + /*this is an option handled by IoTHubClientCore_LL*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_043: [ Calling IoTHubClientCore_LL_SetOption with value set to "0" shall disable the timeout mechanism for all new messages. ]*/ + handleData->currentMessageTimeout = *(const tickcounter_ms_t*)value; + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(optionName, OPTION_PRODUCT_INFO) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_033: [repeat calls with "product_info" will erase the previously set product information if applicatble. ]*/ + if (handleData->product_info != NULL) + { + STRING_delete(handleData->product_info); + handleData->product_info = NULL; + } + + /*Codes_SRS_IOTHUBCLIENT_LL_10_035: [If string concatenation fails, `IoTHubClientCore_LL_SetOption` shall return `IOTHUB_CLIENT_ERRROR`. Otherwise, `IOTHUB_CLIENT_OK` shall be returned. ]*/ + handleData->product_info = make_product_info((const char*)value); + if (handleData->product_info == NULL) + { + LogError("STRING_construct_sprintf failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else if (strcmp(optionName, OPTION_DIAGNOSTIC_SAMPLING_PERCENTAGE) == 0) + { + uint32_t percentage = *(uint32_t*)value; + if (percentage > 100) + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_036: [Calling IoTHubClientCore_LL_SetOption with value > 100 shall return `IOTHUB_CLIENT_ERRROR`. ]*/ + LogError("The value of diag_sampling_percentage is out of range [0, 100]: %u", percentage); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_10_037: [Calling IoTHubClientCore_LL_SetOption with value between [0, 100] shall return `IOTHUB_CLIENT_OK`. ]*/ + handleData->diagnostic_setting.diagSamplingPercentage = percentage; + handleData->diagnostic_setting.currentMessageNumber = 0; + result = IOTHUB_CLIENT_OK; + } + } + else if (strcmp(optionName, OPTION_BLOB_UPLOAD_TIMEOUT_SECS) == 0) + { +#ifndef DONT_USE_UPLOADTOBLOB + // This option just gets passed down into IoTHubClientCore_LL_UploadToBlob + /*Codes_SRS_IOTHUBCLIENT_LL_30_010: [ blob_xfr_timeout - IoTHubClientCore_LL_SetOption shall pass this option to IoTHubClient_UploadToBlob_SetOption and return its result. ]*/ + result = IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value); + if(result != IOTHUB_CLIENT_OK) + { + LogError("unable to IoTHubClientCore_LL_UploadToBlob_SetOption"); + } +#else + LogError("OPTION_BLOB_TRANSFER_TIMEOUT option being set with DONT_USE_UPLOADTOBLOB compiler switch"); + result = IOTHUB_CLIENT_ERROR; +#endif /*DONT_USE_UPLOADTOBLOB*/ + } + else + { + // This section is unusual for SetOption calls because it attempts to pass unhandled options + // to two downstream targets (IoTHubTransport_SetOption and IoTHubClientCore_LL_UploadToBlob_SetOption) instead of one. + + /*Codes_SRS_IOTHUBCLIENT_LL_30_011: [ IoTHubClientCore_LL_SetOption shall always pass unhandled options to Transport_SetOption. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_30_012: [ If Transport_SetOption fails, IoTHubClientCore_LL_SetOption shall return that failure code. ]*/ + result = handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value); + if(result != IOTHUB_CLIENT_OK) + { + LogError("unable to IoTHubTransport_SetOption"); + } +#ifndef DONT_USE_UPLOADTOBLOB + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_30_013: [ If the DONT_USE_UPLOADTOBLOB compiler switch is undefined, IoTHubClientCore_LL_SetOption shall pass unhandled options to IoTHubClient_UploadToBlob_SetOption and ignore the result. ]*/ + (void)IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value); + } +#endif /*DONT_USE_UPLOADTOBLOB*/ + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GetOption(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* optionName, void** value) +{ + IOTHUB_CLIENT_RESULT result; + + if ((iotHubClientHandle == NULL) || (optionName == NULL) || (value == NULL)) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid argument iotHubClientHandle(%p); optionName(%p); value(%p)", iotHubClientHandle, optionName, value); + } + else if (strcmp(optionName, OPTION_PRODUCT_INFO) == 0) + { + result = IOTHUB_CLIENT_OK; + *value = iotHubClientHandle->product_info; + } + else + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid argument (%s)", optionName); + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetDeviceTwinCallback(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /* Codes_SRS_IOTHUBCLIENT_LL_10_001: [ IoTHubClientCore_LL_SetDeviceTwinCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.] */ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Invalid argument specified iothubClientHandle=%p", iotHubClientHandle); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if (deviceTwinCallback == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_006: [ If deviceTwinCallback is NULL, then IoTHubClientCore_LL_SetDeviceTwinCallback shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */ + handleData->IoTHubTransport_Unsubscribe_DeviceTwin(handleData->transportHandle); + handleData->deviceTwinCallback = NULL; + result = IOTHUB_CLIENT_OK; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_002: [ If deviceTwinCallback is not NULL, then IoTHubClientCore_LL_SetDeviceTwinCallback shall call the underlying layer's _Subscribe function.] */ + if (handleData->IoTHubTransport_Subscribe_DeviceTwin(handleData->transportHandle) == 0) + { + handleData->deviceTwinCallback = deviceTwinCallback; + handleData->deviceTwinContextCallback = userContextCallback; + /* Codes_SRS_IOTHUBCLIENT_LL_10_005: [ Otherwise IoTHubClientCore_LL_SetDeviceTwinCallback shall succeed and return IOTHUB_CLIENT_OK.] */ + result = IOTHUB_CLIENT_OK; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_003: [ If the underlying layer's _Subscribe function fails, then IoTHubClientCore_LL_SetDeviceTwinCallback shall fail and return IOTHUB_CLIENT_ERROR.] */ + result = IOTHUB_CLIENT_ERROR; + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SendReportedState(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /* Codes_SRS_IOTHUBCLIENT_LL_10_012: [ IoTHubClientCore_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL. ] */ + /* Codes_SRS_IOTHUBCLIENT_LL_10_013: [ IoTHubClientCore_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter reportedState is NULL] */ + /* Codes_SRS_IOTHUBCLIENT_LL_07_005: [ IoTHubClientCore_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter size is equal to 0. ] */ + if (iotHubClientHandle == NULL || (reportedState == NULL || size == 0) ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Invalid argument specified iothubClientHandle=%p, reportedState=%p, size=%lu", iotHubClientHandle, reportedState, size); + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + /* Codes_SRS_IOTHUBCLIENT_LL_10_014: [IoTHubClientCore_LL_SendReportedState shall construct and queue the reported a Device_Twin structure for transmition by the underlying transport.] */ + IOTHUB_DEVICE_TWIN* client_data = dev_twin_data_create(handleData, get_next_item_id(handleData), reportedState, size, reportedStateCallback, userContextCallback); + if (client_data == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_015: [If any error is encountered IoTHubClientCore_LL_SendReportedState shall return IOTHUB_CLIENT_ERROR.] */ + LogError("Failure constructing device twin data"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (handleData->IoTHubTransport_Subscribe_DeviceTwin(handleData->transportHandle) != 0) + { + LogError("Failure adding device twin data to queue"); + device_twin_data_destroy(client_data); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_001: [ IoTHubClientCore_LL_SendReportedState shall queue the constructed reportedState data to be consumed by the targeted transport. ] */ + DList_InsertTailList(&(iotHubClientHandle->iot_msg_queue), &(client_data->entry)); + + /* Codes_SRS_IOTHUBCLIENT_LL_10_016: [ Otherwise IoTHubClientCore_LL_SendReportedState shall succeed and return IOTHUB_CLIENT_OK.] */ + result = IOTHUB_CLIENT_OK; + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetDeviceMethodCallback(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_LL_12_017: [ IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL. ] */ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if (deviceMethodCallback == NULL) + { + if (handleData->methodCallback.type == CALLBACK_TYPE_NONE) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_029: [ If deviceMethodCallback is NULL and the client is not subscribed to receive method calls, IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("not currently set to accept or process incoming messages."); + result = IOTHUB_CLIENT_ERROR; + } + else if (handleData->methodCallback.type == CALLBACK_TYPE_ASYNC) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_028: [If the user has subscribed using IoTHubClientCore_LL_SetDeviceMethodCallback_Ex, IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetDeviceMethodCallback_Ex function."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_019: [If parameter messageCallback is NULL then IoTHubClientCore_LL_SetMessageCallback shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */ + /*Codes_SRS_IOTHUBCLIENT_LL_12_018: [If deviceMethodCallback is NULL, then IoTHubClientCore_LL_SetDeviceMethodCallback shall call the underlying layer's IoTHubTransport_Unsubscribe_DeviceMethod function and return IOTHUB_CLIENT_OK. ] */ + /*Codes_SRS_IOTHUBCLIENT_LL_12_022: [ Otherwise IoTHubClientCore_LL_SetDeviceMethodCallback shall succeed and return IOTHUB_CLIENT_OK. ]*/ + handleData->IoTHubTransport_Unsubscribe_DeviceMethod(handleData->transportHandle); + handleData->methodCallback.type = CALLBACK_TYPE_NONE; + handleData->methodCallback.callbackSync = NULL; + handleData->methodCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_OK; + } + } + else + { + if (handleData->methodCallback.type == CALLBACK_TYPE_ASYNC) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_028: [If the user has subscribed using IoTHubClientCore_LL_SetDeviceMethodCallback_Ex, IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetDeviceMethodCallback_Ex function before subscribing with IoTHubClientCore_LL_SetDeviceMethodCallback."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_12_019: [ If deviceMethodCallback is not NULL, then IoTHubClientCore_LL_SetDeviceMethodCallback shall call the underlying layer's IoTHubTransport_Subscribe_DeviceMethod function. ]*/ + if (handleData->IoTHubTransport_Subscribe_DeviceMethod(handleData->deviceHandle) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_12_022: [ Otherwise IoTHubClientCore_LL_SetDeviceMethodCallback shall succeed and return IOTHUB_CLIENT_OK. ]*/ + handleData->methodCallback.type = CALLBACK_TYPE_SYNC; + handleData->methodCallback.callbackSync = deviceMethodCallback; + handleData->methodCallback.callbackAsync = NULL; + handleData->methodCallback.userContextCallback = userContextCallback; + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_12_020: [ If the underlying layer's IoTHubTransport_Subscribe_DeviceMethod function fails, then IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_12_021: [ If adding the information fails for any reason, IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("IoTHubTransport_Subscribe_DeviceMethod failed"); + handleData->methodCallback.type = CALLBACK_TYPE_NONE; + handleData->methodCallback.callbackAsync = NULL; + handleData->methodCallback.callbackSync = NULL; + handleData->methodCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_ERROR; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetDeviceMethodCallback_Ex(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inboundDeviceMethodCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /* Codes_SRS_IOTHUBCLIENT_LL_07_021: [ If handle is NULL then IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall return IOTHUB_CLIENT_INVALID_ARG.] */ + if (iotHubClientHandle == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if (inboundDeviceMethodCallback == NULL) + { + if (handleData->methodCallback.type == CALLBACK_TYPE_NONE) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_030: [ If deviceMethodCallback is NULL and the client is not subscribed to receive method calls, IoTHubClientCore_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("not currently set to accept or process incoming messages."); + result = IOTHUB_CLIENT_ERROR; + } + else if (handleData->methodCallback.type == CALLBACK_TYPE_SYNC) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_031: [If the user has subscribed using IoTHubClientCore_LL_SetDeviceMethodCallback, IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetDeviceMethodCallback function."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_022: [ If inboundDeviceMethodCallback is NULL then IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall call the underlying layer's IoTHubTransport_Unsubscribe_DeviceMethod function and return IOTHUB_CLIENT_OK.] */ + handleData->IoTHubTransport_Unsubscribe_DeviceMethod(handleData->transportHandle); + handleData->methodCallback.type = CALLBACK_TYPE_NONE; + handleData->methodCallback.callbackAsync = NULL; + handleData->methodCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_OK; + } + } + else + { + if (handleData->methodCallback.type == CALLBACK_TYPE_SYNC) + { + /* Codes_SRS_IOTHUBCLIENT_LL_10_031: [If the user has subscribed using IoTHubClientCore_LL_SetDeviceMethodCallback, IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall fail and return IOTHUB_CLIENT_ERROR. ] */ + LogError("Invalid workflow sequence. Please unsubscribe using the IoTHubClientCore_LL_SetDeviceMethodCallback function before subscribing with IoTHubClientCore_LL_SetDeviceMethodCallback_Ex."); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_023: [ If inboundDeviceMethodCallback is non-NULL then IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall call the underlying layer's IoTHubTransport_Subscribe_DeviceMethod function.]*/ + if (handleData->IoTHubTransport_Subscribe_DeviceMethod(handleData->deviceHandle) == 0) + { + handleData->methodCallback.type = CALLBACK_TYPE_ASYNC; + handleData->methodCallback.callbackAsync = inboundDeviceMethodCallback; + handleData->methodCallback.callbackSync = NULL; + handleData->methodCallback.userContextCallback = userContextCallback; + result = IOTHUB_CLIENT_OK; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_025: [ If any error is encountered then IoTHubClientCore_LL_SetDeviceMethodCallback_Ex shall return IOTHUB_CLIENT_ERROR.] */ + LogError("IoTHubTransport_Subscribe_DeviceMethod failed"); + handleData->methodCallback.type = CALLBACK_TYPE_NONE; + handleData->methodCallback.callbackAsync = NULL; + handleData->methodCallback.callbackSync = NULL; + handleData->methodCallback.userContextCallback = NULL; + result = IOTHUB_CLIENT_ERROR; + } + } + } + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_DeviceMethodResponse(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + IOTHUB_CLIENT_RESULT result; + /* Codes_SRS_IOTHUBCLIENT_LL_07_026: [ If handle or methodId is NULL then IoTHubClientCore_LL_DeviceMethodResponse shall return IOTHUB_CLIENT_INVALID_ARG.] */ + if (iotHubClientHandle == NULL || methodId == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LOG_ERROR_RESULT; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + /* Codes_SRS_IOTHUBCLIENT_LL_07_027: [ IoTHubClientCore_LL_DeviceMethodResponse shall call the IoTHubTransport_DeviceMethod_Response transport function.] */ + if (handleData->IoTHubTransport_DeviceMethod_Response(handleData->deviceHandle, methodId, response, response_size, status_response) != 0) + { + LogError("IoTHubTransport_DeviceMethod_Response failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + return result; +} + +#ifndef DONT_USE_UPLOADTOBLOB +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_UploadToBlob(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_061: [ If iotHubClientHandle is NULL then IoTHubClientCore_LL_UploadToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_062: [ If destinationFileName is NULL then IoTHubClientCore_LL_UploadToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_063: [ If `source` is `NULL` and size is greater than 0 then `IoTHubClientCore_LL_UploadToBlob` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + ((source == NULL) && (size >0)) + ) + { + LogError("invalid parameters IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle=%p, const char* destinationFileName=%s, const unsigned char* source=%p, size_t size=%lu", iotHubClientHandle, destinationFileName, source, size); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + result = IoTHubClient_LL_UploadToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, source, size); + } + return result; +} + +typedef struct UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT_TAG +{ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback; + void* context; +} UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT; + + +static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT uploadMultipleBlocksCallbackWrapper(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context) +{ + UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT* wrapperContext = (UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT*)context; + wrapperContext->getDataCallback(result, data, size, wrapperContext->context); + return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_UploadMultipleBlocksToBlob(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_99_005: [ If `iotHubClientHandle` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_006: [ If `destinationFileName` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_007: [ If `getDataCallback` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + (getDataCallback == NULL) + ) + { + LogError("invalid parameters IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle=%p, const char* destinationFileName=%p, getDataCallback=%p", iotHubClientHandle, destinationFileName, getDataCallback); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT uploadMultipleBlocksWrapperContext; + uploadMultipleBlocksWrapperContext.getDataCallback = getDataCallback; + uploadMultipleBlocksWrapperContext.context = context; + + result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, uploadMultipleBlocksCallbackWrapper, &uploadMultipleBlocksWrapperContext); + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_99_005: [ If `iotHubClientHandle` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_006: [ If `destinationFileName` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_007: [ If `getDataCallback` is `NULL` then `IoTHubClientCore_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + (getDataCallbackEx == NULL) + ) + { + LogError("invalid parameters IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle=%p, destinationFileName=%p, getDataCallbackEx=%p", iotHubClientHandle, destinationFileName, getDataCallbackEx); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, getDataCallbackEx, context); + } + return result; +} +#endif // DONT_USE_UPLOADTOBLOB + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SendEventToOutputAsync(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, const char* outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + + if ((iotHubClientHandle == NULL) || (outputName == NULL) || (eventMessageHandle == NULL) || ((eventConfirmationCallback == NULL) && (userContextCallback != NULL))) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_127: [ If `iotHubClientHandle`, `outputName`, or `eventConfirmationCallback` is `NULL`, `IoTHubClient_LL_SendEventToOutputAsync` shall return `IOTHUB_CLIENT_INVALID_ARG`. ] + LogError("Invalid argument (iotHubClientHandle=%p, outputName=%p, eventMessageHandle=%p)", iotHubClientHandle, outputName, eventMessageHandle); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + // Codes_SRS_IOTHUBCLIENT_LL_31_128: [ `IoTHubClient_LL_SendEventToOutputAsync` shall set the outputName of the message to send. ] + if (IoTHubMessage_SetOutputName(eventMessageHandle, outputName) != IOTHUB_MESSAGE_OK) + { + LogError("IoTHubMessage_SetOutputName failed"); + result = IOTHUB_CLIENT_ERROR; + } + // Codes_SRS_IOTHUBCLIENT_LL_31_129: [ `IoTHubClient_LL_SendEventToOutputAsync` shall invoke `IoTHubClient_LL_SendEventAsync` to send the message. ] + else if ((result = IoTHubClientCore_LL_SendEventAsync(iotHubClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("Call into IoTHubClient_LL_SendEventAsync failed, result=%d", result); + } + } + + return result; +} + + +static IOTHUB_CLIENT_RESULT create_event_handler_callback(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC callbackSync, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX callbackSyncEx, void* userContextCallback, void* userContextCallbackEx, size_t userContextCallbackExLength) +{ + IOTHUB_CLIENT_RESULT result = IOTHUB_CLIENT_ERROR; + bool add_to_list = false; + + if ((handleData->event_callbacks == NULL) && ((handleData->event_callbacks = singlylinkedlist_create()) == NULL)) + { + LogError("Could not allocate linked list for callbacks"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + IOTHUB_EVENT_CALLBACK* event_callback = NULL; + LIST_ITEM_HANDLE item_handle = singlylinkedlist_find(handleData->event_callbacks, is_event_equal_for_match, (const void*)inputName); + if (item_handle == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_134: [ `IoTHubClient_LL_SetInputMessageCallback` shall allocate a callback handle to associate callbacks from the transport => client if `inputName` isn't already present in the callback list. ] + event_callback = (IOTHUB_EVENT_CALLBACK*)malloc(sizeof(IOTHUB_EVENT_CALLBACK)); + if (event_callback == NULL) + { + LogError("Could not allocate IOTHUB_EVENT_CALLBACK"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + memset(event_callback, 0, sizeof(*event_callback)); + add_to_list = true; + } + } + else + { + // Codes_SRS_IOTHUBCLIENT_LL_31_135: [ `IoTHubClient_LL_SetInputMessageCallback` shall reuse the existing callback handle if `inputName` is already present in the callback list. ] + event_callback = (IOTHUB_EVENT_CALLBACK*)singlylinkedlist_item_get_value(item_handle); + if (event_callback == NULL) + { + LogError("singlylinkedlist_item_get_value failed looking up event callback"); + } + } + + if (event_callback != NULL) + { + if ((inputName != NULL) && (event_callback->inputName == NULL)) + { + event_callback->inputName = STRING_construct(inputName); + } + + if ((inputName == NULL) || (event_callback->inputName != NULL)) + { + event_callback->callbackAsync = callbackSync; + event_callback->callbackAsyncEx = callbackSyncEx; + + free(event_callback->userContextCallbackEx); + event_callback->userContextCallbackEx = NULL; + + if (userContextCallbackEx == NULL) + { + event_callback->userContextCallback = userContextCallback; + } + + if ((userContextCallbackEx != NULL) && + (NULL == (event_callback->userContextCallbackEx = malloc(userContextCallbackExLength)))) + { + LogError("Unable to allocate userContextCallback"); + delete_event(event_callback); + result = IOTHUB_CLIENT_ERROR; + } + else if ((add_to_list == true) && (NULL == singlylinkedlist_add(handleData->event_callbacks, event_callback))) + { + LogError("Unable to add eventCallback to list"); + delete_event(event_callback); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (userContextCallbackEx != NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_141: [`IoTHubClient_LL_SetInputMessageCallbackEx` shall copy the data passed in extended context. ] + memcpy(event_callback->userContextCallbackEx, userContextCallbackEx, userContextCallbackExLength); + } + result = IOTHUB_CLIENT_OK; + } + } + else + { + delete_event(event_callback); + result = IOTHUB_CLIENT_ERROR; + } + } + } + + return result; +} + +static IOTHUB_CLIENT_RESULT remove_event_unsubscribe_if_needed(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, const char* inputName) +{ + IOTHUB_CLIENT_RESULT result; + + LIST_ITEM_HANDLE item_handle = singlylinkedlist_find(handleData->event_callbacks, is_event_equal_for_match, (const void*)inputName); + if (item_handle == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_132: [ If `eventHandlerCallback` is NULL, `IoTHubClient_LL_SetInputMessageCallback` shall return `IOTHUB_CLIENT_ERROR` if the `inputName` is not present. ] + LogError("Input name %s was not present", inputName); + result = IOTHUB_CLIENT_ERROR; + } + else + { + IOTHUB_EVENT_CALLBACK* event_callback = (IOTHUB_EVENT_CALLBACK*)singlylinkedlist_item_get_value(item_handle); + if (event_callback == NULL) + { + LogError("singlylinkedlist_item_get_value failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + delete_event(event_callback); + // Codes_SRS_IOTHUBCLIENT_LL_31_131: [ If `eventHandlerCallback` is NULL, `IoTHubClient_LL_SetInputMessageCallback` shall remove the `inputName` from its callback list if present. ] + if (singlylinkedlist_remove(handleData->event_callbacks, item_handle) != 0) + { + LogError("singlylinkedlist_remove failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (singlylinkedlist_get_head_item(handleData->event_callbacks) == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_133: [ If `eventHandlerCallback` is NULL, `IoTHubClient_LL_SetInputMessageCallback` shall invoke `IoTHubTransport_Unsubscribe_InputQueue` if this was the last input callback. ] + handleData->IoTHubTransport_Unsubscribe_InputQueue(handleData); + } + result = IOTHUB_CLIENT_OK; + } + } + } + + return result; +} + + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetInputMessageCallbackImpl(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX eventHandlerCallbackEx, void *userContextCallback, void *userContextCallbackEx, size_t userContextCallbackExLength) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubClientHandle == NULL) + { + // Codes_SRS_IOTHUBCLIENT_LL_31_130: [ If `iotHubClientHandle` or `inputName` is NULL, `IoTHubClient_LL_SetInputMessageCallback` shall return IOTHUB_CLIENT_INVALID_ARG. ] + LogError("Invalid argument - iotHubClientHandle=%p, inputName=%p", iotHubClientHandle, inputName); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_CORE_LL_HANDLE_DATA*)iotHubClientHandle; + if ((eventHandlerCallback == NULL) && (eventHandlerCallbackEx == NULL)) + { + result = (IOTHUB_CLIENT_RESULT)remove_event_unsubscribe_if_needed(handleData, inputName); + } + else + { + bool registered_with_transport_handler = (handleData->event_callbacks != NULL) && (singlylinkedlist_get_head_item(handleData->event_callbacks) != NULL); + if ((result = (IOTHUB_CLIENT_RESULT)create_event_handler_callback(handleData, inputName, eventHandlerCallback, eventHandlerCallbackEx, userContextCallback, userContextCallbackEx, userContextCallbackExLength)) != IOTHUB_CLIENT_OK) + { + LogError("create_event_handler_callback call failed, error = %d", result); + } + // Codes_SRS_IOTHUBCLIENT_LL_31_136: [ `IoTHubClient_LL_SetInputMessageCallback` shall invoke `IoTHubTransport_Subscribe_InputQueue` if this is the first callback being registered. ] + else if (!registered_with_transport_handler && (handleData->IoTHubTransport_Subscribe_InputQueue(handleData->deviceHandle) != 0)) + { + LogError("IoTHubTransport_Subscribe_InputQueue failed"); + delete_event_callback_list(handleData); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + } + return result; + +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetInputMessageCallbackEx(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC_EX eventHandlerCallbackEx, void *userContextCallbackEx, size_t userContextCallbackExLength) +{ + return IoTHubClientCore_LL_SetInputMessageCallbackImpl(iotHubClientHandle, inputName, NULL, eventHandlerCallbackEx, NULL, userContextCallbackEx, userContextCallbackExLength); +} + +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_SetInputMessageCallback(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SetInputMessageCallbackImpl(iotHubClientHandle, inputName, eventHandlerCallback, NULL, userContextCallback, NULL, 0); +} + +#ifdef USE_EDGE_MODULES +/* These should be replaced during iothub_client refactor */ +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GenericMethodInvoke(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubClientHandle != NULL) + { + if (moduleId != NULL) + { + result = IoTHubClient_Edge_ModuleMethodInvoke(iotHubClientHandle->methodHandle, deviceId, moduleId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + else + { + result = IoTHubClient_Edge_DeviceMethodInvoke(iotHubClientHandle->methodHandle, deviceId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + + } + } + else + { + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} +#endif + +/*end*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_diagnostic.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <inttypes.h> +#include <math.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/buffer_.h" + +#include "internal/iothub_client_diagnostic.h" + +#define TIME_STRING_BUFFER_LEN 30 + +static const int BASE_36 = 36; + +#define INDEFINITE_TIME ((time_t)-1) + +static char* get_epoch_time(char* timeBuffer) +{ + char* result; + time_t epochTime; + int timeLen = sizeof(time_t); + + if ((epochTime = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed getting current time"); + result = NULL; + } + else if (timeLen == sizeof(int64_t)) + { + if (sprintf(timeBuffer, "%"PRIu64, (int64_t)epochTime) < 0) + { + LogError("Failed sprintf to timeBuffer with 8 bytes of time_t"); + result = NULL; + } + else + { + result = timeBuffer; + } + } + else if (timeLen == sizeof(int32_t)) + { + if (sprintf(timeBuffer, "%"PRIu32, (int32_t)epochTime) < 0) + { + LogError("Failed sprintf to timeBuffer with 4 bytes of time_t"); + result = NULL; + } + else + { + result = timeBuffer; + } + } + else + { + LogError("Unknown size of time_t"); + result = NULL; + } + + return result; +} + +static char get_base36_char(unsigned char value) +{ + return value <= 9 ? '0' + value : 'a' + value - 10; +} + +static char* generate_eight_random_characters(char *randomString) +{ + int i; + char* randomStringPos = randomString; + for (i = 0; i < 4; ++i) + { + int rawRandom = rand(); + int first = rawRandom % BASE_36; + int second = rawRandom / BASE_36 % BASE_36; + *randomStringPos++ = get_base36_char((unsigned char)first); + *randomStringPos++ = get_base36_char((unsigned char)second); + } + *randomStringPos = 0; + + return randomString; +} + +static bool should_add_diagnostic_info(IOTHUB_DIAGNOSTIC_SETTING_DATA* diagSetting) +{ + bool result = false; + if (diagSetting->diagSamplingPercentage > 0) + { + double number; + double percentage; + + if (diagSetting->currentMessageNumber == UINT32_MAX) + { + diagSetting->currentMessageNumber %= diagSetting->diagSamplingPercentage * 100; + } + ++diagSetting->currentMessageNumber; + + number = diagSetting->currentMessageNumber; + percentage = diagSetting->diagSamplingPercentage; + result = (floor((number - 2) * percentage / 100.0) < floor((number - 1) * percentage / 100.0)); + } + return result; +} + +static IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* prepare_message_diagnostic_data() +{ + IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* result = (IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA*)malloc(sizeof(IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA)); + if (result == NULL) + { + LogError("malloc for DiagnosticData failed"); + } + else + { + char* diagId = (char*)malloc(9); + if (diagId == NULL) + { + LogError("malloc for diagId failed"); + free(result); + result = NULL; + } + else + { + char* timeBuffer; + + (void)generate_eight_random_characters(diagId); + result->diagnosticId = diagId; + + timeBuffer = (char*)malloc(TIME_STRING_BUFFER_LEN); + if (timeBuffer == NULL) + { + LogError("malloc for timeBuffer failed"); + free(result->diagnosticId); + free(result); + result = NULL; + } + else if (get_epoch_time(timeBuffer) == NULL) + { + LogError("Failed getting current time"); + free(result->diagnosticId); + free(result); + result = NULL; + } + else + { + result->diagnosticCreationTimeUtc = timeBuffer; + } + } + } + return result; +} + +int IoTHubClient_Diagnostic_AddIfNecessary(IOTHUB_DIAGNOSTIC_SETTING_DATA* diagSetting, IOTHUB_MESSAGE_HANDLE messageHandle) +{ + int result; + /* Codes_SRS_IOTHUB_DIAGNOSTIC_13_001: [ IoTHubClient_Diagnostic_AddIfNecessary should return nonezero if diagSetting or messageHandle is NULL. ]*/ + if (diagSetting == NULL || messageHandle == NULL) + { + result = __FAILURE__; + } + /* Codes_SRS_IOTHUB_DIAGNOSTIC_13_003: [ If diagSamplingPercentage is equal to 0, message number should not be increased and no diagnostic properties added ]*/ + else if (should_add_diagnostic_info(diagSetting)) + { + /* Codes_SRS_IOTHUB_DIAGNOSTIC_13_004: [ If diagSamplingPercentage is equal to 100, diagnostic properties should be added to all messages]*/ + /* Codes_SRS_IOTHUB_DIAGNOSTIC_13_005: [ If diagSamplingPercentage is between(0, 100), diagnostic properties should be added based on percentage]*/ + + IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData; + if ((diagnosticData = prepare_message_diagnostic_data()) == NULL) + { + result = __FAILURE__; + } + else + { + if (IoTHubMessage_SetDiagnosticPropertyData(messageHandle, diagnosticData) != IOTHUB_MESSAGE_OK) + { + /* Codes_SRS_IOTHUB_DIAGNOSTIC_13_002: [ IoTHubClient_Diagnostic_AddIfNecessary should return nonezero if failing to add diagnostic property. ]*/ + result = __FAILURE__; + } + else + { + result = 0; + } + + free(diagnosticData->diagnosticCreationTimeUtc); + free(diagnosticData->diagnosticId); + free(diagnosticData); + diagnosticData = NULL; + } + } + else + { + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_dll.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,160 @@ +LIBRARY iothub_client_dll +EXPORTS + IoTHub_Init + IoTHub_Deinit + + IoTHubTransport_Create + IoTHubTransport_Destroy + IoTHubTransport_GetLock + IoTHubTransport_GetLLTransport + IoTHubTransport_StartWorkerThread + IoTHubTransport_SignalEndWorkerThread + IoTHubTransport_JoinWorkerThread + + IoTHubClient_GetVersionString + + IoTHubClientCore_LL_SendComplete + IoTHubClientCore_LL_ReportedStateComplete + IoTHubClientCore_LL_MessageCallback + IoTHubClientCore_LL_RetrievePropertyComplete + IoTHubClientCore_LL_DeviceMethodComplete + IoTHubClientCore_LL_ConnectionStatusCallBack + IoTHubClientCore_LL_GetOption + IoTHubClientCore_LL_MessageCallbackFromInput + + IoTHubClient_CreateFromConnectionString + IoTHubClient_Create + IoTHubClient_CreateWithTransport + IoTHubClient_CreateFromDeviceAuth + IoTHubClient_Destroy + IoTHubClient_SendEventAsync + IoTHubClient_GetSendStatus + IoTHubClient_SetMessageCallback + IoTHubClient_SetConnectionStatusCallback + IoTHubClient_SetRetryPolicy + IoTHubClient_GetRetryPolicy + IoTHubClient_GetLastMessageReceiveTime + IoTHubClient_SetOption + IoTHubClient_SetDeviceTwinCallback + IoTHubClient_SendReportedState + IoTHubClient_SetDeviceMethodCallback + + IoTHubDeviceClient_CreateFromConnectionString + IoTHubDeviceClient_Create + IoTHubDeviceClient_CreateWithTransport + IoTHubDeviceClient_CreateFromDeviceAuth + IoTHubDeviceClient_Destroy + IoTHubDeviceClient_SendEventAsync + IoTHubDeviceClient_GetSendStatus + IoTHubDeviceClient_SetMessageCallback + IoTHubDeviceClient_SetConnectionStatusCallback + IoTHubDeviceClient_SetRetryPolicy + IoTHubDeviceClient_GetRetryPolicy + IoTHubDeviceClient_GetLastMessageReceiveTime + IoTHubDeviceClient_SetOption + IoTHubDeviceClient_SetDeviceTwinCallback + IoTHubDeviceClient_SendReportedState + IoTHubDeviceClient_SetDeviceMethodCallback + IoTHubDeviceClient_DeviceMethodResponse + IoTHubDeviceClient_UploadToBlobAsync + IoTHubDeviceClient_UploadMultipleBlocksToBlobAsync + + IoTHubModuleClient_CreateFromConnectionString + IoTHubModuleClient_Destroy + IoTHubModuleClient_SendEventAsync + IoTHubModuleClient_GetSendStatus + IoTHubModuleClient_SetMessageCallback + IoTHubModuleClient_SetConnectionStatusCallback + IoTHubModuleClient_SetRetryPolicy + IoTHubModuleClient_GetRetryPolicy + IoTHubModuleClient_GetLastMessageReceiveTime + IoTHubModuleClient_SetOption + IoTHubModuleClient_SetModuleTwinCallback + IoTHubModuleClient_SendReportedState + IoTHubModuleClient_SetModuleMethodCallback + IoTHubModuleClient_SendEventToOutputAsync + IoTHubModuleClient_SetInputMessageCallback + + IoTHubClient_LL_CreateFromConnectionString + IoTHubClient_LL_Destroy + IoTHubClient_LL_DoWork + IoTHubClient_LL_SendEventAsync + IoTHubClient_LL_SetMessageCallback + IoTHubClient_LL_SetOption + + IoTHubDeviceClient_LL_CreateFromConnectionString + IoTHubDeviceClient_LL_Create + IoTHubDeviceClient_LL_CreateWithTransport + IoTHubDeviceClient_LL_CreateFromDeviceAuth + IoTHubDeviceClient_LL_Destroy + IoTHubDeviceClient_LL_SendEventAsync + IoTHubDeviceClient_LL_GetSendStatus + IoTHubDeviceClient_LL_SetMessageCallback + IoTHubDeviceClient_LL_SetConnectionStatusCallback + IoTHubDeviceClient_LL_SetRetryPolicy + IoTHubDeviceClient_LL_GetRetryPolicy + IoTHubDeviceClient_LL_GetLastMessageReceiveTime + IoTHubDeviceClient_LL_DoWork + IoTHubDeviceClient_LL_SetOption + IoTHubDeviceClient_LL_SetDeviceTwinCallback + IoTHubDeviceClient_LL_SendReportedState + IoTHubDeviceClient_LL_SetDeviceMethodCallback + IoTHubDeviceClient_LL_DeviceMethodResponse + IoTHubDeviceClient_LL_UploadToBlob + IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob + + IoTHubModuleClient_LL_CreateFromConnectionString + IoTHubModuleClient_LL_Destroy + IoTHubModuleClient_LL_SendEventAsync + IoTHubModuleClient_LL_GetSendStatus + IoTHubModuleClient_LL_SetMessageCallback + IoTHubModuleClient_LL_SetConnectionStatusCallback + IoTHubModuleClient_LL_SetRetryPolicy + IoTHubModuleClient_LL_GetRetryPolicy + IoTHubModuleClient_LL_GetLastMessageReceiveTime + IoTHubModuleClient_LL_DoWork + IoTHubModuleClient_LL_SetOption + IoTHubModuleClient_LL_SetModuleTwinCallback + IoTHubModuleClient_LL_SendReportedState + IoTHubModuleClient_LL_SetModuleMethodCallback + IoTHubModuleClient_LL_ModuleMethodResponse + IoTHubModuleClient_LL_SendEventToOutputAsync + IoTHubModuleClient_LL_SetInputMessageCallback + + IoTHubMessage_CreateFromString + IoTHubMessage_CreateFromByteArray + IoTHubMessage_Clone + IoTHubMessage_Destroy + IoTHubMessage_GetByteArray + IoTHubMessage_GetString + IoTHubMessage_GetContentType + IoTHubMessage_GetContentTypeSystemProperty + IoTHubMessage_GetContentEncodingSystemProperty + IoTHubMessage_GetCorrelationId + IoTHubMessage_GetDiagnosticPropertyData + IoTHubMessage_GetMessageId + IoTHubMessage_GetOutputName + IoTHubMessage_GetProperty + IoTHubMessage_Properties + IoTHubMessage_SetConnectionDeviceId + IoTHubMessage_SetConnectionModuleId + IoTHubMessage_SetContentTypeSystemProperty + IoTHubMessage_SetContentEncodingSystemProperty + IoTHubMessage_SetCorrelationId + IoTHubMessage_SetInputName + IoTHubMessage_SetMessageId + IoTHubMessage_SetProperty + + IOTHUB_CLIENT_CONFIRMATION_RESULTStrings + IOTHUB_CLIENT_FILE_UPLOAD_RESULTStrings + IOTHUB_CLIENT_RESULTStrings + IOTHUB_CLIENT_RETRY_POLICYStrings + IOTHUB_CLIENT_STATUSStrings + IOTHUB_IDENTITY_TYPEStrings + IOTHUB_PROCESS_ITEM_RESULTStrings + IOTHUB_CLIENT_IOTHUB_METHOD_STATUSStrings + IOTHUB_CLIENT_CONFIRMATION_RESULTStrings + IOTHUB_CLIENT_CONNECTION_STATUSStrings + IOTHUB_CLIENT_CONNECTION_STATUS_REASONStrings + TRANSPORT_TYPEStrings + DEVICE_TWIN_UPDATE_STATEStrings
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_ll.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/platform.h" + +#include "iothub_client_core_ll.h" +#include "internal/iothub_client_authorization.h" +#include "iothub_client_ll.h" +#include "iothub_transport_ll.h" +#include "iothub_device_client_ll.h" +#include "internal/iothub_client_private.h" +#include "iothub_client_options.h" +#include "iothub_client_version.h" +#include "internal/iothub_client_diagnostic.h" +#include <stdint.h> + +#ifndef DONT_USE_UPLOADTOBLOB +#include "internal/iothub_client_ll_uploadtoblob.h" +#endif + +IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_CLIENT_LL_HANDLE)IoTHubClientCore_LL_CreateFromConnectionString(connectionString, protocol); +} + +IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + return (IOTHUB_CLIENT_LL_HANDLE)IoTHubClientCore_LL_Create(config); +} + +IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateWithTransport(const IOTHUB_CLIENT_DEVICE_CONFIG * config) +{ + return (IOTHUB_CLIENT_LL_HANDLE)IoTHubClientCore_LL_CreateWithTransport(config); +} + +IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromDeviceAuth(const char* iothub_uri, const char* device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_CLIENT_LL_HANDLE)IoTHubClientCore_LL_CreateFromDeviceAuth(iothub_uri, device_id, protocol); +} + +void IoTHubClient_LL_Destroy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle) +{ + IoTHubClientCore_LL_Destroy((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SendEventAsync(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SendEventAsync((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetSendStatus(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + return IoTHubClientCore_LL_GetSendStatus((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, iotHubClientStatus); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetMessageCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SetMessageCallback((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, messageCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetConnectionStatusCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + return IoTHubClientCore_LL_SetConnectionStatusCallback((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, connectionStatusCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetRetryPolicy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_LL_SetRetryPolicy((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetRetryPolicy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_LL_GetRetryPolicy((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetLastMessageReceiveTime(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) +{ + return IoTHubClientCore_LL_GetLastMessageReceiveTime((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, lastMessageReceiveTime); +} + +void IoTHubClient_LL_DoWork(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle) +{ + IoTHubClientCore_LL_DoWork((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetDeviceTwinCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SetDeviceTwinCallback((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, deviceTwinCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetOption(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* optionName, const void* value) +{ + return IoTHubClientCore_LL_SetOption((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, optionName, value); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SendReportedState(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SendReportedState((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, reportedState, size, reportedStateCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetDeviceMethodCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SetDeviceMethodCallback((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, deviceMethodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetDeviceMethodCallback_Ex(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK inboundDeviceMethodCallback, void* userContextCallback) +{ + return IoTHubClientCore_LL_SetDeviceMethodCallback_Ex((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, inboundDeviceMethodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_DeviceMethodResponse(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + return IoTHubClientCore_LL_DeviceMethodResponse((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, methodId, response, response_size, status_response); +} + +#ifndef DONT_USE_UPLOADTOBLOB +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size) +{ + return IoTHubClientCore_LL_UploadToBlob((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, destinationFileName, source, size); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +{ + return IoTHubClientCore_LL_UploadMultipleBlocksToBlob((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, destinationFileName, getDataCallback, context); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlobEx(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + return IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx((IOTHUB_CLIENT_CORE_LL_HANDLE)iotHubClientHandle, destinationFileName, getDataCallbackEx, context); +} +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_ll_uploadtoblob.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1328 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef DONT_USE_UPLOADTOBLOB +#error "trying to compile iothub_client_ll_uploadtoblob.c while the symbol DONT_USE_UPLOADTOBLOB is #define'd" +#else + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/urlencode.h" + +#include "iothub_client_core_ll.h" +#include "iothub_client_options.h" +#include "internal/iothub_client_private.h" +#include "iothub_client_version.h" +#include "iothub_transport_ll.h" +#include "parson.h" +#include "internal/iothub_client_ll_uploadtoblob.h" +#include "internal/blob.h" + +#ifdef WINCE +#include <stdarg.h> +// Returns number of characters copied. +int snprintf(char * s, size_t n, const char * format, ...) +{ + int result; + va_list args; + va_start(args, format); + result = vsnprintf(s, n, format, args); + va_end(args); + return result; +} +#endif + + +/*Codes_SRS_IOTHUBCLIENT_LL_02_085: [ IoTHubClient_LL_UploadToBlob shall use the same authorization as step 1. to prepare and perform a HTTP request with the following parameters: ]*/ +#define FILE_UPLOAD_FAILED_BODY "{ \"isSuccess\":false, \"statusCode\":-1,\"statusDescription\" : \"client not able to connect with the server\" }" +#define FILE_UPLOAD_ABORTED_BODY "{ \"isSuccess\":false, \"statusCode\":-1,\"statusDescription\" : \"file upload aborted\" }" + +#define AUTHORIZATION_SCHEME_VALUES \ + DEVICE_KEY, \ + X509, \ + SAS_TOKEN +DEFINE_ENUM(AUTHORIZATION_SCHEME, AUTHORIZATION_SCHEME_VALUES); + +typedef struct UPLOADTOBLOB_X509_CREDENTIALS_TAG +{ + const char* x509certificate; + const char* x509privatekey; +}UPLOADTOBLOB_X509_CREDENTIALS; + +typedef struct IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA_TAG +{ + STRING_HANDLE deviceId; /*needed for file upload*/ + const char* hostname; /*needed for file upload*/ + AUTHORIZATION_SCHEME authorizationScheme; /*needed for file upload*/ + union { + STRING_HANDLE deviceKey; /*used when authorizationScheme is DEVICE_KEY*/ + STRING_HANDLE sas; /*used when authorizationScheme is SAS_TOKEN*/ + UPLOADTOBLOB_X509_CREDENTIALS x509credentials; /*assumed to be used when both deviceKey and deviceSasToken are NULL*/ + } credentials; /*needed for file upload*/ + char* certificates; /*if there are any certificates used*/ + HTTP_PROXY_OPTIONS http_proxy_options; + size_t curl_verbose; + size_t blob_upload_timeout_secs; +}IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA; + +typedef struct BLOB_UPLOAD_CONTEXT_TAG +{ + const unsigned char* blobSource; /* source to upload */ + size_t blobSourceSize; /* size of the source */ + size_t remainingSizeToUpload; /* size not yet uploaded */ +}BLOB_UPLOAD_CONTEXT; + +IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE IoTHubClient_LL_UploadToBlob_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData = malloc(sizeof(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA)); + if (handleData == NULL) + { + LogError("oom - malloc"); + /*return as is*/ + } + else + { + size_t iotHubNameLength = strlen(config->iotHubName); + size_t iotHubSuffixLength = strlen(config->iotHubSuffix); + handleData->deviceId = STRING_construct(config->deviceId); + if (handleData->deviceId == NULL) + { + LogError("unable to STRING_construct"); + free(handleData); + handleData = NULL; + } + else + { + handleData->hostname = malloc(iotHubNameLength + 1 + iotHubSuffixLength + 1); /*first +1 is because "." the second +1 is because \0*/ + if (handleData->hostname == NULL) + { + LogError("malloc failed"); + STRING_delete(handleData->deviceId); + free(handleData); + handleData = NULL; + } + else + { + char* insert_pos = (char*)handleData->hostname; + (void)memcpy((char*)insert_pos, config->iotHubName, iotHubNameLength); + insert_pos += iotHubNameLength; + *insert_pos = '.'; + insert_pos += 1; + (void)memcpy(insert_pos, config->iotHubSuffix, iotHubSuffixLength); /*+1 will copy the \0 too*/ + insert_pos += iotHubSuffixLength; + *insert_pos = '\0'; + + handleData->certificates = NULL; + memset(&(handleData->http_proxy_options), 0, sizeof(HTTP_PROXY_OPTIONS)); + handleData->curl_verbose = 0; + handleData->blob_upload_timeout_secs = 0; + + if ((config->deviceSasToken != NULL) && (config->deviceKey == NULL)) + { + handleData->authorizationScheme = SAS_TOKEN; + handleData->credentials.sas = STRING_construct(config->deviceSasToken); + if (handleData->credentials.sas == NULL) + { + LogError("unable to STRING_construct"); + free((void*)handleData->hostname); + STRING_delete(handleData->deviceId); + free(handleData); + handleData = NULL; + } + else + { + /*return as is*/ + } + } + else if ((config->deviceSasToken == NULL) && (config->deviceKey != NULL)) + { + handleData->authorizationScheme = DEVICE_KEY; + handleData->credentials.deviceKey = STRING_construct(config->deviceKey); + if (handleData->credentials.deviceKey == NULL) + { + LogError("unable to STRING_construct"); + free((void*)handleData->hostname); + STRING_delete(handleData->deviceId); + free(handleData); + handleData = NULL; + } + else + { + /*return as is*/ + } + } + else if ((config->deviceSasToken == NULL) && (config->deviceKey == NULL)) + { + handleData->authorizationScheme = X509; + handleData->credentials.x509credentials.x509certificate = NULL; + handleData->credentials.x509credentials.x509privatekey = NULL; + /*return as is*/ + } + } + } + } + return (IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE)handleData; + +} + +/*returns 0 when correlationId, sasUri contain data*/ +static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData, HTTPAPIEX_HANDLE iotHubHttpApiExHandle, HTTP_HEADERS_HANDLE requestHttpHeaders, const char* destinationFileName, + STRING_HANDLE correlationId, STRING_HANDLE sasUri) +{ + int result; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_066: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTP relative path formed from "/devices/" + deviceId + "/files/" + "?api-version=API_VERSION". ]*/ + STRING_HANDLE relativePath = STRING_construct("/devices/"); + if (relativePath == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to STRING_construct"); + result = __FAILURE__; + } + else + { + if (!( + (STRING_concat_with_STRING(relativePath, handleData->deviceId) == 0) && + (STRING_concat(relativePath, "/files") == 0) && + (STRING_concat(relativePath, API_VERSION) == 0) + )) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to concatenate STRING"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_001: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create a JSON string formed from "{ \"blobName\": \" + destinationFileName + "\" }" */ + STRING_HANDLE blobName = STRING_construct("{ \"blobName\": \""); + if (blobName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to STRING_construct"); + result = __FAILURE__; + } + else + { + if (!( + (STRING_concat(blobName, destinationFileName) == 0) && + (STRING_concat(blobName, "\" }") == 0) + )) + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to concatenate STRING"); + result = __FAILURE__; + } + else + { + size_t len = STRING_length(blobName); + BUFFER_HANDLE blobBuffer = BUFFER_create((const unsigned char *)STRING_c_str(blobName), len); + + if (blobBuffer == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to create BUFFER"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_068: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTP responseContent BUFFER_HANDLE. ]*/ + BUFFER_HANDLE responseContent = BUFFER_new(); + if (responseContent == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_069: [ If creating the HTTP response buffer handle fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + result = __FAILURE__; + LogError("unable to BUFFER_new"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_072: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall add the following name:value to request HTTP headers: ] "Content-Type": "application/json" "Accept": "application/json" "User-Agent": "iothubclient/" IOTHUB_SDK_VERSION*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_107: [ - "Authorization" header shall not be build. ]*/ + if (!( + (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "Content-Type", "application/json") == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "Accept", "application/json") == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "User-Agent", "iothubclient/" IOTHUB_SDK_VERSION) == HTTP_HEADERS_OK) && + (handleData->authorizationScheme == X509 || (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "Authorization", "") == HTTP_HEADERS_OK)) + )) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_071: [ If creating the HTTP headers fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to HTTPHeaders_AddHeaderNameValuePair"); + result = __FAILURE__; + } + else + { + int wasIoTHubRequestSuccess = 0; /*!=0 means responseContent has a buffer that should be parsed by parson after executing the below switch*/ + /* set the result to error by default */ + result = __FAILURE__; + switch (handleData->authorizationScheme) + { + default: + { + /*wasIoTHubRequestSuccess takes care of the return value*/ + LogError("Internal Error: unexpected value in handleData->authorizationScheme = %d", handleData->authorizationScheme); + result = __FAILURE__; + break; + } + case(X509): + { + unsigned int statusCode; + /*Codes_SRS_IOTHUBCLIENT_LL_32_003: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ + if (HTTPAPIEX_ExecuteRequest( + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)`*/ + HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_POST*/ + STRING_c_str(relativePath), /*const char* relativePath - the HTTP relative path*/ + requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ + blobBuffer, /*BUFFER_HANDLE requestContent - address of JSON with optional/directory/tree/filename*/ + &statusCode, /*unsigned int* statusCode - the address of an unsigned int that will contain the HTTP status code*/ + NULL, /*HTTP_HEADERS_HANDLE responseHttpHeadersHandle - NULL*/ + responseContent /*BUFFER_HANDLE responseContent - the HTTP response BUFFER_HANDLE - responseContent*/ + ) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + result = __FAILURE__; + LogError("unable to HTTPAPIEX_ExecuteRequest"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + if (statusCode >= 300) + { + result = __FAILURE__; + LogError("HTTP code was %u", statusCode); + } + else + { + wasIoTHubRequestSuccess = 1; + } + } + break; + } + case (SAS_TOKEN): + { + const char* sasToken = STRING_c_str(handleData->credentials.sas); + /*Codes_SRS_IOTHUBCLIENT_LL_02_073: [ If the credentials used to create handle have "sasToken" then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall add the following HTTP request headers: ]*/ + if (HTTPHeaders_ReplaceHeaderNameValuePair(requestHttpHeaders, "Authorization", sasToken) != HTTP_HEADERS_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_074: [ If adding "Authorization" fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR ]*/ + result = __FAILURE__; + LogError("unable to HTTPHeaders_AddHeaderNameValuePair"); + } + else + { + unsigned int statusCode; + /*Codes_SRS_IOTHUBCLIENT_LL_32_004: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ + if (HTTPAPIEX_ExecuteRequest( + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)`*/ + HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_POST*/ + STRING_c_str(relativePath), /*const char* relativePath - the HTTP relative path*/ + requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ + blobBuffer, /*BUFFER_HANDLE requestContent - address of JSON with optional/directory/tree/filename*/ + &statusCode, /*unsigned int* statusCode - the address of an unsigned int that will contain the HTTP status code*/ + NULL, /*HTTP_HEADERS_HANDLE responseHttpHeadersHandle - NULL*/ + responseContent /*BUFFER_HANDLE responseContent - the HTTP response BUFFER_HANDLE - responseContent*/ + ) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + result = __FAILURE__; + LogError("unable to HTTPAPIEX_ExecuteRequest"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + if (statusCode >= 300) + { + result = __FAILURE__; + LogError("HTTP code was %u", statusCode); + } + else + { + wasIoTHubRequestSuccess = 1; + } + } + } + break; + } + case(DEVICE_KEY): + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_078: [ If the credentials used to create handle have "deviceKey" then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTPAPIEX_SAS_HANDLE passing as arguments: ]*/ + STRING_HANDLE uriResource = STRING_construct(handleData->hostname); + if (uriResource == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + result = __FAILURE__; + LogError("unable to STRING_construct"); + } + else + { + if (!( + (STRING_concat(uriResource, "/devices/") == 0) && + (STRING_concat_with_STRING(uriResource, handleData->deviceId) == 0) + )) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to STRING_concat_with_STRING"); + result = __FAILURE__; + } + else + { + STRING_HANDLE empty = STRING_new(); + if (empty == NULL) + { + LogError("unable to STRING_new"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + HTTPAPIEX_SAS_HANDLE sasHandle = HTTPAPIEX_SAS_Create(handleData->credentials.deviceKey, uriResource, empty); + if (sasHandle == NULL) + { + LogError("unable to HTTPAPIEX_SAS_Create"); + result = __FAILURE__; + } + else + { + unsigned int statusCode; + /*Codes_SRS_IOTHUBCLIENT_LL_32_005: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall call HTTPAPIEX_SAS_ExecuteRequest passing as arguments: ]*/ + if (HTTPAPIEX_SAS_ExecuteRequest( + sasHandle, /*HTTPAPIEX_SAS_HANDLE sasHandle - the created HTTPAPIEX_SAS_HANDLE*/ + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the created HTTPAPIEX_HANDLE*/ + HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_POST*/ + STRING_c_str(relativePath), /*const char* relativePath - the HTTP relative path*/ + requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ + blobBuffer, /*BUFFER_HANDLE requestContent - address of JSON with optional/directory/tree/filename*/ + &statusCode, /*unsigned int* statusCode - the address of an unsigned int that will contain the HTTP status code*/ + NULL, /*HTTP_HEADERS_HANDLE responseHeadersHandle - NULL*/ + responseContent + ) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); + result = __FAILURE__; + } + else + { + if (statusCode >= 300) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_080: [ If status code is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + result = __FAILURE__; + LogError("HTTP code was %u", statusCode); + } + else + { + wasIoTHubRequestSuccess = 1; + } + } + HTTPAPIEX_SAS_Destroy(sasHandle); + } + STRING_delete(empty); + } + } + STRING_delete(uriResource); + } + } + } /*switch*/ + + if (wasIoTHubRequestSuccess == 0) + { + /*do nothing, shall be reported as an error*/ + } + else + { + const unsigned char*responseContent_u_char = BUFFER_u_char(responseContent); + size_t responseContent_length = BUFFER_length(responseContent); + STRING_HANDLE responseAsString = STRING_from_byte_array(responseContent_u_char, responseContent_length); + if (responseAsString == NULL) + { + result = __FAILURE__; + LogError("unable to get the response as string"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_081: [ Otherwise, IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall use parson to extract and save the following information from the response buffer: correlationID and SasUri. ]*/ + JSON_Value* allJson = json_parse_string(STRING_c_str(responseAsString)); + if (allJson == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_parse_string"); + result = __FAILURE__; + } + else + { + JSON_Object* jsonObject = json_value_get_object(allJson); + if (jsonObject == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_value_get_object"); + result = __FAILURE__; + } + else + { + const char* json_correlationId; + json_correlationId = json_object_get_string(jsonObject, "correlationId"); + if (json_correlationId == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_object_get_string(jsonObject, \"correlationId\")"); + result = __FAILURE__; + } + else + { + if (STRING_copy(correlationId, json_correlationId) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to copy json_correlationId"); + result = __FAILURE__; + } + else + { + const char* json_hostName = json_object_get_string(jsonObject, "hostName"); + if (json_hostName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_object_get_string(jsonObject, \"hostName\")"); + result = __FAILURE__; + } + else + { + const char* json_containerName = json_object_get_string(jsonObject, "containerName"); + if (json_containerName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_object_get_string(jsonObject, \"containerName\")"); + result = __FAILURE__; + } + else + { + const char* json_blobName = json_object_get_string(jsonObject, "blobName"); + if (json_blobName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_object_get_string(jsonObject, \"blobName\")"); + result = __FAILURE__; + } + else + { + const char* json_sasToken = json_object_get_string(jsonObject, "sasToken"); + if (json_sasToken == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to json_object_get_string(jsonObject, \"sasToken\")"); + result = __FAILURE__; + } + else + { + /*good JSON received from the service*/ + + if (STRING_copy(sasUri, "https://") != 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to STRING_copy"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_008: [ The returned file name shall be URL encoded before passing back to the cloud. ]*/ + STRING_HANDLE fileName = URL_EncodeString(json_blobName); + + if (fileName == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_LL_32_009: [ If URL_EncodeString fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to URL_EncodeString of filename"); + result = __FAILURE__; + } + + else + { + if (!( + (STRING_concat(sasUri, json_hostName) == 0) && + (STRING_concat(sasUri, "/") == 0) && + (STRING_concat(sasUri, json_containerName) == 0) && + (STRING_concat(sasUri, "/") == 0) && + (STRING_concat(sasUri, STRING_c_str(fileName)) == 0) && + (STRING_concat(sasUri, json_sasToken) == 0) + )) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to STRING_concat"); + result = __FAILURE__; + } + else + { + result = 0; /*success in step 1*/ + } + + STRING_delete(fileName); + } + } + } + } + } + } + } + } + } + json_value_free(allJson); + } + STRING_delete(responseAsString); + } + } + } + BUFFER_delete(responseContent); + } + BUFFER_delete(blobBuffer); + } + } + STRING_delete(blobName); + } + } + STRING_delete(relativePath); + } + return result; +} + +/*returns 0 when the IoTHub has been informed about the file upload status*/ +static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData, STRING_HANDLE correlationId, HTTPAPIEX_HANDLE iotHubHttpApiExHandle, HTTP_HEADERS_HANDLE requestHttpHeaders, BUFFER_HANDLE messageBody) +{ + int result; + /*here is step 3. depending on the outcome of step 2 it needs to inform IoTHub about the file upload status*/ + /*if step 1 failed, there's nothing that step 3 needs to report.*/ + /*this POST "tries" to happen*/ + + /*Codes_SRS_IOTHUBCLIENT_LL_02_085: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall use the same authorization as step 1. to prepare and perform a HTTP request with the following parameters: ]*/ + STRING_HANDLE uriResource = STRING_construct(handleData->hostname); + if (uriResource == NULL) + { + LogError("unable to construct URI"); + result = __FAILURE__; + } + else + { + if (!( + (STRING_concat(uriResource, "/devices/") == 0) && + (STRING_concat_with_STRING(uriResource, handleData->deviceId) == 0) && + (STRING_concat(uriResource, "/files/notifications") == 0) + )) + { + LogError("unable to STRING_concat"); + result = __FAILURE__; + } + else + { + STRING_HANDLE relativePathNotification = STRING_construct("/devices/"); + if (relativePathNotification == NULL) + { + result = __FAILURE__; + LogError("unable to STRING_construct"); + } + else + { + if (!( + (STRING_concat_with_STRING(relativePathNotification, handleData->deviceId) == 0) && + (STRING_concat(relativePathNotification, "/files/notifications/") == 0) && + (STRING_concat(relativePathNotification, STRING_c_str(correlationId)) == 0) && + (STRING_concat(relativePathNotification, API_VERSION) == 0) + )) + { + LogError("unable to STRING_concat_with_STRING"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_086: [ If performing the HTTP request fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + switch (handleData->authorizationScheme) + { + default: + { + LogError("internal error: unknown authorization Scheme"); + result = __FAILURE__; + break; + } + case (X509): + { + unsigned int notificationStatusCode; + if (HTTPAPIEX_ExecuteRequest( + iotHubHttpApiExHandle, + HTTPAPI_REQUEST_POST, + STRING_c_str(relativePathNotification), + requestHttpHeaders, + messageBody, + ¬ificationStatusCode, + NULL, + NULL) != HTTPAPIEX_OK) + { + LogError("unable to do HTTPAPIEX_ExecuteRequest"); + result = __FAILURE__; + } + else + { + if (notificationStatusCode >= 300) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ + LogError("server didn't like the notification request"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + break; + } + case (DEVICE_KEY): + { + STRING_HANDLE empty = STRING_new(); + if (empty == NULL) + { + LogError("unable to STRING_new"); + result = __FAILURE__; + } + else + { + HTTPAPIEX_SAS_HANDLE sasHandle = HTTPAPIEX_SAS_Create(handleData->credentials.deviceKey, uriResource, empty); + if (sasHandle == NULL) + { + LogError("unable to HTTPAPIEX_SAS_Create"); + result = __FAILURE__; + } + else + { + unsigned int statusCode; + if (HTTPAPIEX_SAS_ExecuteRequest( + sasHandle, /*HTTPAPIEX_SAS_HANDLE sasHandle - the created HTTPAPIEX_SAS_HANDLE*/ + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the created HTTPAPIEX_HANDLE*/ + HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_GET*/ + STRING_c_str(relativePathNotification), /*const char* relativePath - the HTTP relative path*/ + requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ + messageBody, /*BUFFER_HANDLE requestContent*/ + &statusCode, /*unsigned int* statusCode - the address of an unsigned int that will contain the HTTP status code*/ + NULL, /*HTTP_HEADERS_HANDLE responseHeadersHandle - NULL*/ + NULL + ) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); + result = __FAILURE__; + ; + } + else + { + if (statusCode >= 300) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ + result = __FAILURE__; + LogError("HTTP code was %u", statusCode); + } + else + { + result = 0; + } + } + HTTPAPIEX_SAS_Destroy(sasHandle); + } + STRING_delete(empty); + } + break; + } + case(SAS_TOKEN): + { + unsigned int notificationStatusCode; + if (HTTPAPIEX_ExecuteRequest( + iotHubHttpApiExHandle, + HTTPAPI_REQUEST_POST, + STRING_c_str(relativePathNotification), + requestHttpHeaders, + messageBody, + ¬ificationStatusCode, + NULL, + NULL) != HTTPAPIEX_OK) + { + LogError("unable to do HTTPAPIEX_ExecuteRequest"); + result = __FAILURE__; + } + else + { + if (notificationStatusCode >= 300) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ + LogError("server didn't like the notification request"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + break; + } + } /*switch authorizationScheme*/ + } + STRING_delete(relativePathNotification); + } + } + STRING_delete(uriResource); + } + return result; +} + +// this callback splits the source data into blocks to be fed to IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)_Impl +static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT FileUpload_GetData_Callback(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context) +{ + BLOB_UPLOAD_CONTEXT* uploadContext = (BLOB_UPLOAD_CONTEXT*) context; + + if (data == NULL || size == NULL) + { + // This is the last call, nothing to do + } + else if (result != FILE_UPLOAD_OK) + { + // Last call failed + *data = NULL; + *size = 0; + } + else if (uploadContext->remainingSizeToUpload == 0) + { + // Everything has been uploaded + *data = NULL; + *size = 0; + } + else + { + // Upload next block + size_t thisBlockSize = (uploadContext->remainingSizeToUpload > BLOCK_SIZE) ? BLOCK_SIZE : uploadContext->remainingSizeToUpload; + *data = (unsigned char*)uploadContext->blobSource + (uploadContext->blobSourceSize - uploadContext->remainingSizeToUpload); + *size = thisBlockSize; + uploadContext->remainingSizeToUpload -= thisBlockSize; + } + + return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK; +} + +static HTTPAPIEX_RESULT set_transfer_timeout(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData, HTTPAPIEX_HANDLE iotHubHttpApiExHandle) +{ + HTTPAPIEX_RESULT result; + if (handleData->blob_upload_timeout_secs != 0) + { + // Convert the timeout to milliseconds for curl + long http_timeout = (long)handleData->blob_upload_timeout_secs * 1000; + result = HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_HTTP_TIMEOUT, &http_timeout); + } + else + { + result = HTTPAPIEX_OK; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_061: [ If handle is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_062: [ If destinationFileName is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + + if ( + (handle == NULL) || + (destinationFileName == NULL) || + (getDataCallbackEx == NULL) + ) + { + LogError("invalid argument detected handle=%p destinationFileName=%p getDataCallbackEx=%p", handle, destinationFileName, getDataCallbackEx); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA*)handle; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_064: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTPAPIEX_HANDLE to the IoTHub hostname. ]*/ + HTTPAPIEX_HANDLE iotHubHttpApiExHandle = HTTPAPIEX_Create(handleData->hostname); + + /*Codes_SRS_IOTHUBCLIENT_LL_02_065: [ If creating the HTTPAPIEX_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + if (iotHubHttpApiExHandle == NULL) + { + LogError("unable to HTTPAPIEX_Create"); + result = IOTHUB_CLIENT_ERROR; + } + /*Codes_SRS_IOTHUBCLIENT_LL_30_020: [ If the blob_upload_timeout_secs option has been set to non-zero, IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall set the timeout on the underlying transport accordingly. ]*/ + else if (set_transfer_timeout(handleData, iotHubHttpApiExHandle) != HTTPAPIEX_OK) + { + LogError("unable to set blob transfer timeout"); + result = IOTHUB_CLIENT_ERROR; + + } + else + { + (void)HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_CURL_VERBOSE, &handleData->curl_verbose); + + if ( + (handleData->authorizationScheme == X509) && + + /*transmit the x509certificate and x509privatekey*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_106: [ - x509certificate and x509privatekey saved options shall be passed on the HTTPAPIEX_SetOption ]*/ + (!( + (HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_X509_CERT, handleData->credentials.x509credentials.x509certificate) == HTTPAPIEX_OK) && + (HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_X509_PRIVATE_KEY, handleData->credentials.x509credentials.x509privatekey) == HTTPAPIEX_OK) + )) + ) + { + LogError("unable to HTTPAPIEX_SetOption for x509"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_111: [ If certificates is non-NULL then certificates shall be passed to HTTPAPIEX_SetOption with optionName TrustedCerts. ]*/ + if ((handleData->certificates != NULL) && (HTTPAPIEX_SetOption(iotHubHttpApiExHandle, "TrustedCerts", handleData->certificates) != HTTPAPIEX_OK)) + { + LogError("unable to set TrustedCerts!"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + + if (handleData->http_proxy_options.host_address != NULL) + { + HTTP_PROXY_OPTIONS proxy_options; + proxy_options = handleData->http_proxy_options; + + if (HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_HTTP_PROXY, &proxy_options) != HTTPAPIEX_OK) + { + LogError("unable to set http proxy!"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else + { + result = IOTHUB_CLIENT_OK; + } + + if (result != IOTHUB_CLIENT_ERROR) + { + STRING_HANDLE correlationId = STRING_new(); + if (correlationId == NULL) + { + LogError("unable to STRING_new"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + STRING_HANDLE sasUri = STRING_new(); + if (sasUri == NULL) + { + LogError("unable to STRING_new"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_070: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create request HTTP headers. ]*/ + HTTP_HEADERS_HANDLE requestHttpHeaders = HTTPHeaders_Alloc(); /*these are build by step 1 and used by step 3 too*/ + if (requestHttpHeaders == NULL) + { + LogError("unable to HTTPHeaders_Alloc"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*do step 1*/ + if (IoTHubClient_LL_UploadToBlob_step1and2(handleData, iotHubHttpApiExHandle, requestHttpHeaders, destinationFileName, correlationId, sasUri) != 0) + { + LogError("error in IoTHubClient_LL_UploadToBlob_step1"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*do step 2.*/ + + unsigned int httpResponse; + BUFFER_HANDLE responseToIoTHub = BUFFER_new(); + if (responseToIoTHub == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("unable to BUFFER_new"); + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_083: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall call Blob_UploadFromSasUri and capture the HTTP return code and HTTP body. ]*/ + BLOB_RESULT uploadMultipleBlocksResult = Blob_UploadMultipleBlocksFromSasUri(STRING_c_str(sasUri), getDataCallbackEx, context, &httpResponse, responseToIoTHub, handleData->certificates, &(handleData->http_proxy_options)); + if (uploadMultipleBlocksResult == BLOB_ABORTED) + { + /*Codes_SRS_IOTHUBCLIENT_LL_99_008: [ If step 2 is aborted by the client, then the HTTP message body shall look like: ]*/ + LogInfo("Blob_UploadFromSasUri aborted file upload"); + + if (BUFFER_build(responseToIoTHub, (const unsigned char*)FILE_UPLOAD_ABORTED_BODY, sizeof(FILE_UPLOAD_ABORTED_BODY) / sizeof(FILE_UPLOAD_ABORTED_BODY[0])) == 0) + { + if (IoTHubClient_LL_UploadToBlob_step3(handleData, correlationId, iotHubHttpApiExHandle, requestHttpHeaders, responseToIoTHub) != 0) + { + LogError("IoTHubClient_LL_UploadToBlob_step3 failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_99_009: [ If step 2 is aborted by the client and if step 3 succeeds, then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall return `IOTHUB_CLIENT_OK`. ] */ + result = IOTHUB_CLIENT_OK; + } + } + else + { + LogError("Unable to BUFFER_build, can't perform IoTHubClient_LL_UploadToBlob_step3"); + result = IOTHUB_CLIENT_ERROR; + } + } + else if (uploadMultipleBlocksResult != BLOB_OK) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_084: [ If Blob_UploadFromSasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to Blob_UploadFromSasUri"); + + /*do step 3*/ /*try*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_091: [ If step 2 fails without establishing an HTTP dialogue, then the HTTP message body shall look like: ]*/ + if (BUFFER_build(responseToIoTHub, (const unsigned char*)FILE_UPLOAD_FAILED_BODY, sizeof(FILE_UPLOAD_FAILED_BODY) / sizeof(FILE_UPLOAD_FAILED_BODY[0])) == 0) + { + if (IoTHubClient_LL_UploadToBlob_step3(handleData, correlationId, iotHubHttpApiExHandle, requestHttpHeaders, responseToIoTHub) != 0) + { + LogError("IoTHubClient_LL_UploadToBlob_step3 failed"); + } + } + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*must make a json*/ + + int requiredStringLength = snprintf(NULL, 0, "{\"isSuccess\":%s, \"statusCode\":%d, \"statusDescription\":\"%s\"}", ((httpResponse < 300) ? "true" : "false"), httpResponse, BUFFER_u_char(responseToIoTHub)); + + char * requiredString = malloc(requiredStringLength + 1); + if (requiredString == 0) + { + LogError("unable to malloc"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*do again snprintf*/ + BUFFER_HANDLE toBeTransmitted = NULL; + (void)snprintf(requiredString, requiredStringLength + 1, "{\"isSuccess\":%s, \"statusCode\":%d, \"statusDescription\":\"%s\"}", ((httpResponse < 300) ? "true" : "false"), httpResponse, BUFFER_u_char(responseToIoTHub)); + toBeTransmitted = BUFFER_create((const unsigned char*)requiredString, requiredStringLength); + if (toBeTransmitted == NULL) + { + LogError("unable to BUFFER_create"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (IoTHubClient_LL_UploadToBlob_step3(handleData, correlationId, iotHubHttpApiExHandle, requestHttpHeaders, toBeTransmitted) != 0) + { + LogError("IoTHubClient_LL_UploadToBlob_step3 failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = (httpResponse < 300) ? IOTHUB_CLIENT_OK : IOTHUB_CLIENT_ERROR; + } + BUFFER_delete(toBeTransmitted); + } + free(requiredString); + } + } + BUFFER_delete(responseToIoTHub); + } + } + HTTPHeaders_Free(requestHttpHeaders); + } + STRING_delete(sasUri); + } + STRING_delete(correlationId); + } + } + } + } + HTTPAPIEX_Destroy(iotHubHttpApiExHandle); + } + } + + /*Codes_SRS_IOTHUBCLIENT_LL_99_003: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` return `IOTHUB_CLIENT_OK`, it shall call `getDataCallbackEx` with `result` set to `FILE_UPLOAD_OK`, and `data` and `size` set to NULL. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_004: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` does not return `IOTHUB_CLIENT_OK`, it shall call `getDataCallbackEx` with `result` set to `FILE_UPLOAD_ERROR`, and `data` and `size` set to NULL. ]*/ + (void)getDataCallbackEx(result == IOTHUB_CLIENT_OK ? FILE_UPLOAD_OK : FILE_UPLOAD_ERROR, NULL, NULL, context); + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob_Impl(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle, const char* destinationFileName, const unsigned char* source, size_t size) +{ + IOTHUB_CLIENT_RESULT result; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_063: [ If source is NULL and size is greater than 0 then IoTHubClient_LL_UploadToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (source == NULL && size > 0) + { + LogError("invalid source and size combination: source=%p size=%lu", source, size); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_99_001: [ `IoTHubClient_LL_UploadToBlob` shall create a struct containing the `source`, the `size`, and the remaining size to upload.]*/ + BLOB_UPLOAD_CONTEXT context; + context.blobSource = source; + context.blobSourceSize = size; + context.remainingSizeToUpload = size; + + /*Codes_SRS_IOTHUBCLIENT_LL_99_002: [ `IoTHubClient_LL_UploadToBlob` shall call `IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl` with `FileUpload_GetData_Callback` as `getDataCallbackEx` and pass the struct created at step SRS_IOTHUBCLIENT_LL_99_001 as `context` ]*/ + result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(handle, destinationFileName, FileUpload_GetData_Callback, &context); + } + return result; +} + +void IoTHubClient_LL_UploadToBlob_Destroy(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle) +{ + if (handle == NULL) + { + LogError("unexpected NULL argument"); + } + else + { + IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA*)handle; + switch (handleData->authorizationScheme) + { + case(SAS_TOKEN): + { + STRING_delete(handleData->credentials.sas); + break; + } + case(DEVICE_KEY): + { + STRING_delete(handleData->credentials.deviceKey); + break; + } + case(X509): + { + if (handleData->credentials.x509credentials.x509certificate != NULL) + { + free((void*)handleData->credentials.x509credentials.x509certificate); + } + if (handleData->credentials.x509credentials.x509privatekey != NULL) + { + free((void*)handleData->credentials.x509credentials.x509privatekey); + } + break; + } + default: + { + LogError("INTERNAL ERROR"); + break; + } + } + free((void*)handleData->hostname); + STRING_delete(handleData->deviceId); + if (handleData->certificates != NULL) + { + free(handleData->certificates); + } + if (handleData->http_proxy_options.host_address != NULL) + { + free((char *)handleData->http_proxy_options.host_address); + } + if (handleData->http_proxy_options.username != NULL) + { + free((char *)handleData->http_proxy_options.username); + } + if (handleData->http_proxy_options.password != NULL) + { + free((char *)handleData->http_proxy_options.password); + } + free(handleData); + } +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob_SetOption(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle, const char* optionName, const void* value) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_02_110: [ If parameter handle is NULL then IoTHubClient_LL_UploadToBlob_SetOption shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + if (handle == NULL) + { + LogError("invalid argument detected: IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle=%p, const char* optionName=%s, const void* value=%p", handle, optionName, value); + result = IOTHUB_CLIENT_ERROR; + } + else + { + IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA*)handle; + + /*Codes_SRS_IOTHUBCLIENT_LL_02_100: [ x509certificate - then value then is a null terminated string that contains the x509 certificate. ]*/ + if (strcmp(optionName, OPTION_X509_CERT) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_109: [ If the authentication scheme is NOT x509 then IoTHubClient_LL_UploadToBlob_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (handleData->authorizationScheme != X509) + { + LogError("trying to set a x509 certificate while the authentication scheme is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_103: [ The options shall be saved. ]*/ + /*try to make a copy of the certificate*/ + char* temp; + if (mallocAndStrcpy_s(&temp, value) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_104: [ If saving fails, then IoTHubClient_LL_UploadToBlob_SetOption shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_105: [ Otherwise IoTHubClient_LL_UploadToBlob_SetOption shall succeed and return IOTHUB_CLIENT_OK. ]*/ + if (handleData->credentials.x509credentials.x509certificate != NULL) /*free any previous values, if any*/ + { + free((void*)handleData->credentials.x509credentials.x509certificate); + } + handleData->credentials.x509credentials.x509certificate = temp; + result = IOTHUB_CLIENT_OK; + } + } + } + /*Codes_SRS_IOTHUBCLIENT_LL_02_101: [ x509privatekey - then value is a null terminated string that contains the x509 privatekey. ]*/ + else if (strcmp(optionName, OPTION_X509_PRIVATE_KEY) == 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_109: [ If the authentication scheme is NOT x509 then IoTHubClient_LL_UploadToBlob_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + if (handleData->authorizationScheme != X509) + { + LogError("trying to set a x509 privatekey while the authentication scheme is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_103: [ The options shall be saved. ]*/ + /*try to make a copy of the privatekey*/ + char* temp; + if (mallocAndStrcpy_s(&temp, value) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_104: [ If saving fails, then IoTHubClient_LL_UploadToBlob_SetOption shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_105: [ Otherwise IoTHubClient_LL_UploadToBlob_SetOption shall succeed and return IOTHUB_CLIENT_OK. ]*/ + if (handleData->credentials.x509credentials.x509privatekey != NULL) /*free any previous values, if any*/ + { + free((void*)handleData->credentials.x509credentials.x509privatekey); + } + handleData->credentials.x509credentials.x509privatekey = temp; + result = IOTHUB_CLIENT_OK; + } + } + } + else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) + { + if (value == NULL) + { + LogError("NULL is a not a valid value for TrustedCerts"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + char* tempCopy; + if (mallocAndStrcpy_s(&tempCopy, value) != 0) + { + LogError("failure in mallocAndStrcpy_s"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (handleData->certificates != NULL) + { + free(handleData->certificates); + } + handleData->certificates = tempCopy; + result = IOTHUB_CLIENT_OK; + } + } + } + /*Codes_SRS_IOTHUBCLIENT_LL_32_008: [ OPTION_HTTP_PROXY - then the value will be a pointer to HTTP_PROXY_OPTIONS structure. ]*/ + else if (strcmp(optionName, OPTION_HTTP_PROXY) == 0) + { + HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS *)value; + + if (proxy_options->host_address == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_32_006: [ If `host_address` is NULL, `IoTHubClient_LL_UploadToBlob_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + LogError("NULL host_address in proxy options"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + /* Codes_SRS_IOTHUBCLIENT_LL_32_007: [ If only one of `username` and `password` is NULL, `IoTHubClient_LL_UploadToBlob_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + else if (((proxy_options->username == NULL) || (proxy_options->password == NULL)) && + (proxy_options->username != proxy_options->password)) + { + LogError("Only one of username and password for proxy settings was NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + if (handleData->http_proxy_options.host_address != NULL) + { + free((char *)handleData->http_proxy_options.host_address); + handleData->http_proxy_options.host_address = NULL; + } + if (handleData->http_proxy_options.username != NULL) + { + free((char *)handleData->http_proxy_options.username); + handleData->http_proxy_options.username = NULL; + } + if (handleData->http_proxy_options.password != NULL) + { + free((char *)handleData->http_proxy_options.password); + handleData->http_proxy_options.password = NULL; + } + + handleData->http_proxy_options.port = proxy_options->port; + + if (mallocAndStrcpy_s((char **)(&handleData->http_proxy_options.host_address), proxy_options->host_address) != 0) + { + LogError("failure in mallocAndStrcpy_s - handleData->http_proxy_options.host_address"); + result = IOTHUB_CLIENT_ERROR; + } + else if (proxy_options->username != NULL && mallocAndStrcpy_s((char **)(&handleData->http_proxy_options.username), proxy_options->username) != 0) + { + LogError("failure in mallocAndStrcpy_s - handleData->http_proxy_options.username"); + result = IOTHUB_CLIENT_ERROR; + } + else if (proxy_options->password != NULL && mallocAndStrcpy_s((char **)(&handleData->http_proxy_options.password), proxy_options->password) != 0) + { + LogError("failure in mallocAndStrcpy_s - handleData->http_proxy_options.password"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + } + else if (strcmp(optionName, OPTION_CURL_VERBOSE) == 0) + { + handleData->curl_verbose = *(size_t*)value; + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(optionName, OPTION_BLOB_UPLOAD_TIMEOUT_SECS) == 0) + { + handleData->blob_upload_timeout_secs = *(size_t*)value; + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_02_102: [ If an unknown option is presented then IoTHubClient_LL_UploadToBlob_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + result = IOTHUB_CLIENT_INVALID_ARG; + } + } + return result; +} + + +#endif /*DONT_USE_UPLOADTOBLOB*/ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_client_retry_control.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,514 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "internal/iothub_client_retry_control.h" + +#include <math.h> + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)-1) + +typedef struct RETRY_CONTROL_INSTANCE_TAG +{ + IOTHUB_CLIENT_RETRY_POLICY policy; + unsigned int max_retry_time_in_secs; + + unsigned int initial_wait_time_in_secs; + unsigned int max_jitter_percent; + + unsigned int retry_count; + time_t first_retry_time; + time_t last_retry_time; + unsigned int current_wait_time_in_secs; +} RETRY_CONTROL_INSTANCE; + +typedef int (*RETRY_ACTION_EVALUATION_FUNCTION)(RETRY_CONTROL_INSTANCE* retry_state, RETRY_ACTION* retry_action); + + +// ========== Helper Functions ========== // + +// ---------- Set/Retrieve Options Helpers ----------// + +static void* retry_control_clone_option(const char* name, const void* value) +{ + void* result; + + if ((name == NULL) || (value == NULL)) + { + LogError("Failed to clone option (either name (%p) or value (%p) are NULL)", name, value); + result = NULL; + } + else if (strcmp(RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS, name) == 0 || + strcmp(RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT, name) == 0) + { + unsigned int* cloned_value; + + if ((cloned_value = (unsigned int*)malloc(sizeof(unsigned int))) == NULL) + { + LogError("Failed to clone option '%p' (malloc failed)", name); + result = NULL; + } + else + { + *cloned_value = *(unsigned int*)value; + + result = (void*)cloned_value; + } + } + else + { + LogError("Failed to clone option (option with name '%s' is not suppported)", name); + result = NULL; + } + + return result; +} + +static void retry_control_destroy_option(const char* name, const void* value) +{ + if ((name == NULL) || (value == NULL)) + { + LogError("Failed to destroy option (either name (%p) or value (%p) are NULL)", name, value); + } + else if (strcmp(RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS, name) == 0 || + strcmp(RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT, name) == 0) + { + free((void*)value); + } + else + { + LogError("Failed to destroy option (option with name '%s' is not suppported)", name); + } +} + +// ========== _should_retry() Auxiliary Functions ========== // + +static int evaluate_retry_action(RETRY_CONTROL_INSTANCE* retry_control, RETRY_ACTION* retry_action) +{ + int result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_019: [If `retry_control->retry_count` is 0, `retry_action` shall be set to RETRY_ACTION_RETRY_NOW] + if (retry_control->retry_count == 0) + { + *retry_action = RETRY_ACTION_RETRY_NOW; + result = RESULT_OK; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_020: [If `retry_control->last_retry_time` is INDEFINITE_TIME and policy is not IOTHUB_CLIENT_RETRY_IMMEDIATE, the evaluation function shall return non-zero] + else if (retry_control->last_retry_time == INDEFINITE_TIME && + retry_control->policy != IOTHUB_CLIENT_RETRY_IMMEDIATE) + { + LogError("Failed to evaluate retry action (last_retry_time is INDEFINITE_TIME)"); + result = __FAILURE__; + } + else + { + time_t current_time; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_021: [`current_time` shall be set using get_time()] + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_022: [If get_time() fails, the evaluation function shall return non-zero] + LogError("Failed to evaluate retry action (get_time() failed)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_023: [If `retry_control->max_retry_time_in_secs` is not 0 and (`current_time` - `retry_control->first_retry_time`) is greater than or equal to `retry_control->max_retry_time_in_secs`, `retry_action` shall be set to RETRY_ACTION_STOP_RETRYING] + else if (retry_control->max_retry_time_in_secs > 0 && + get_difftime(current_time, retry_control->first_retry_time) >= retry_control->max_retry_time_in_secs) + { + *retry_action = RETRY_ACTION_STOP_RETRYING; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_026: [If no errors occur, the evaluation function shall return 0] + result = RESULT_OK; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_028: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_IMMEDIATE, retry_action shall be set to RETRY_ACTION_RETRY_NOW] + else if (retry_control->policy == IOTHUB_CLIENT_RETRY_IMMEDIATE) + { + *retry_action = RETRY_ACTION_RETRY_NOW; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_026: [If no errors occur, the evaluation function shall return 0] + result = RESULT_OK; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_024: [Otherwise, if (`current_time` - `retry_control->last_retry_time`) is less than `retry_control->current_wait_time_in_secs`, `retry_action` shall be set to RETRY_ACTION_RETRY_LATER] + else if (get_difftime(current_time, retry_control->last_retry_time) < retry_control->current_wait_time_in_secs) + { + *retry_action = RETRY_ACTION_RETRY_LATER; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_026: [If no errors occur, the evaluation function shall return 0] + result = RESULT_OK; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_025: [Otherwise, if (`current_time` - `retry_control->last_retry_time`) is greater or equal to `retry_control->current_wait_time_in_secs`, `retry_action` shall be set to RETRY_ACTION_RETRY_NOW] + else + { + *retry_action = RETRY_ACTION_RETRY_NOW; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_026: [If no errors occur, the evaluation function shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +static unsigned int calculate_next_wait_time(RETRY_CONTROL_INSTANCE* retry_control) +{ + unsigned int result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_029: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_INTERVAL, `calculate_next_wait_time` shall return `retry_control->initial_wait_time_in_secs`] + if (retry_control->policy == IOTHUB_CLIENT_RETRY_INTERVAL) + { + result = retry_control->initial_wait_time_in_secs; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_030: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_LINEAR_BACKOFF, `calculate_next_wait_time` shall return (`retry_control->initial_wait_time_in_secs` * (`retry_control->retry_count`))] + else if (retry_control->policy == IOTHUB_CLIENT_RETRY_LINEAR_BACKOFF) + { + result = retry_control->initial_wait_time_in_secs * (retry_control->retry_count); + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_031: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF, `calculate_next_wait_time` shall return (pow(2, `retry_control->retry_count` - 1) * `retry_control->initial_wait_time_in_secs`)] + else if (retry_control->policy == IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF) + { + result = (unsigned int)(pow(2, retry_control->retry_count - 1) * retry_control->initial_wait_time_in_secs); + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_032: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, `calculate_next_wait_time` shall return ((pow(2, `retry_control->retry_count` - 1) * `retry_control->initial_wait_time_in_secs`) * (1 + (`retry_control->max_jitter_percent` / 100) * (rand() / RAND_MAX)))] + else if (retry_control->policy == IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER) + { + double jitter_percent = (retry_control->max_jitter_percent / 100.0) * (rand() / ((double)RAND_MAX)); + + result = (unsigned int)(pow(2, retry_control->retry_count - 1) * retry_control->initial_wait_time_in_secs * (1 + jitter_percent)); + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_033: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_RANDOM, `calculate_next_wait_time` shall return (`retry_control->initial_wait_time_in_secs` * (rand() / RAND_MAX))] + else if (retry_control->policy == IOTHUB_CLIENT_RETRY_RANDOM) + { + double random_percent = ((double)rand() / (double)RAND_MAX); + result = (unsigned int)(retry_control->initial_wait_time_in_secs * random_percent); + } + else + { + LogError("Failed to calculate the next wait time (policy %d is not expected)", retry_control->policy); + + result = 0; + } + + return result; +} + + +// ========== Public API ========== // + +int is_timeout_reached(time_t start_time, unsigned int timeout_in_secs, bool* is_timed_out) +{ + int result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_057: [If `start_time` is INDEFINITE_TIME, `is_timeout_reached` shall fail and return non-zero] + if (start_time == INDEFINITE_TIME) + { + LogError("Failed to verify timeout (start_time is INDEFINITE)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_058: [If `is_timed_out` is NULL, `is_timeout_reached` shall fail and return non-zero] + else if (is_timed_out == NULL) + { + LogError("Failed to verify timeout (is_timed_out is NULL)"); + result = __FAILURE__; + } + else + { + time_t current_time; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_059: [`is_timeout_reached` shall obtain the `current_time` using get_time()] + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_060: [If get_time() fails, `is_timeout_reached` shall fail and return non-zero] + LogError("Failed to verify timeout (get_time failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_061: [If (`current_time` - `start_time`) is greater or equal to `timeout_in_secs`, `is_timed_out` shall be set to true] + if (get_difftime(current_time, start_time) >= timeout_in_secs) + { + *is_timed_out = true; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_062: [If (`current_time` - `start_time`) is less than `timeout_in_secs`, `is_timed_out` shall be set to false] + else + { + *is_timed_out = false; + } + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_063: [If no errors occur, `is_timeout_reached` shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +void retry_control_reset(RETRY_CONTROL_HANDLE retry_control_handle) +{ + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_034: [If `retry_control_handle` is NULL, `retry_control_reset` shall return] + if (retry_control_handle == NULL) + { + LogError("Failed to reset the retry control (retry_state_handle is NULL)"); + } + else + { + RETRY_CONTROL_INSTANCE* retry_control = (RETRY_CONTROL_INSTANCE*)retry_control_handle; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_035: [`retry_control` shall have fields `retry_count` and `current_wait_time_in_secs` set to 0 (zero), `first_retry_time` and `last_retry_time` set to INDEFINITE_TIME] + retry_control->retry_count = 0; + retry_control->current_wait_time_in_secs = 0; + retry_control->first_retry_time = INDEFINITE_TIME; + retry_control->last_retry_time = INDEFINITE_TIME; + } +} + +RETRY_CONTROL_HANDLE retry_control_create(IOTHUB_CLIENT_RETRY_POLICY policy, unsigned int max_retry_time_in_secs) +{ + RETRY_CONTROL_INSTANCE* retry_control; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_002: [`retry_control_create` shall allocate memory for the retry control instance structure (a.k.a. `retry_control`)] + if ((retry_control = (RETRY_CONTROL_INSTANCE*)malloc(sizeof(RETRY_CONTROL_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_003: [If malloc fails, `retry_control_create` shall fail and return NULL] + LogError("Failed creating the retry control (malloc failed)"); + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_004: [The parameters passed to `retry_control_create` shall be saved into `retry_control`] + memset(retry_control, 0, sizeof(RETRY_CONTROL_INSTANCE)); + retry_control->policy = policy; + retry_control->max_retry_time_in_secs = max_retry_time_in_secs; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_005: [If `policy` is IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF or IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, `retry_control->initial_wait_time_in_secs` shall be set to 1] + if (retry_control->policy == IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF || + retry_control->policy == IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER) + { + retry_control->initial_wait_time_in_secs = 1; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_006: [Otherwise `retry_control->initial_wait_time_in_secs` shall be set to 5] + else + { + retry_control->initial_wait_time_in_secs = 5; + } + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_007: [`retry_control->max_jitter_percent` shall be set to 5] + retry_control->max_jitter_percent = 5; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_008: [The remaining fields in `retry_control` shall be initialized according to retry_control_reset()] + retry_control_reset(retry_control); + } + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_009: [If no errors occur, `retry_control_create` shall return a handle to `retry_control`] + return (RETRY_CONTROL_HANDLE)retry_control; +} + +void retry_control_destroy(RETRY_CONTROL_HANDLE retry_control_handle) +{ + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_055: [If `retry_control_handle` is NULL, `retry_control_destroy` shall return] + if (retry_control_handle == NULL) + { + LogError("Failed to destroy the retry control (retry_control_handle is NULL)"); + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_056: [`retry_control_destroy` shall destroy `retry_control_handle` using free()] + free(retry_control_handle); + } +} + +int retry_control_should_retry(RETRY_CONTROL_HANDLE retry_control_handle, RETRY_ACTION* retry_action) +{ + int result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_010: [If `retry_control_handle` or `retry_action` are NULL, `retry_control_should_retry` shall fail and return non-zero] + if ((retry_control_handle == NULL) || (retry_action == NULL)) + { + LogError("Failed to evaluate if retry should be attempted (either retry_control_handle (%p) or retry_action (%p) are NULL)", retry_control_handle, retry_action); + result = __FAILURE__; + } + else + { + RETRY_CONTROL_INSTANCE* retry_control = (RETRY_CONTROL_INSTANCE*)retry_control_handle; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_027: [If `retry_control->policy` is IOTHUB_CLIENT_RETRY_NONE, retry_action shall be set to RETRY_ACTION_STOP_RETRYING and return immediatelly with result 0] + if (retry_control->policy == IOTHUB_CLIENT_RETRY_NONE) + { + *retry_action = RETRY_ACTION_STOP_RETRYING; + result = RESULT_OK; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_011: [If `retry_control->first_retry_time` is INDEFINITE_TIME, it shall be set using get_time()] + else if (retry_control->first_retry_time == INDEFINITE_TIME && (retry_control->first_retry_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_012: [If get_time() fails, `retry_control_should_retry` shall fail and return non-zero] + LogError("Failed to evaluate if retry should be attempted (get_time() failed)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_013: [evaluate_retry_action() shall be invoked] + else if (evaluate_retry_action(retry_control, retry_action) != RESULT_OK) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_014: [If evaluate_retry_action() fails, `retry_control_should_retry` shall fail and return non-zero] + LogError("Failed to evaluate if retry should be attempted (evaluate_retry_action() failed)"); + result = __FAILURE__; + } + else + { + if (*retry_action == RETRY_ACTION_RETRY_NOW) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_015: [If `retry_action` is set to RETRY_ACTION_RETRY_NOW, `retry_control->retry_count` shall be incremented by 1] + retry_control->retry_count++; + + if (retry_control->policy != IOTHUB_CLIENT_RETRY_IMMEDIATE) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_016: [If `retry_action` is set to RETRY_ACTION_RETRY_NOW and policy is not IOTHUB_CLIENT_RETRY_IMMEDIATE, `retry_control->last_retry_time` shall be set using get_time()] + retry_control->last_retry_time = get_time(NULL); + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_017: [If `retry_action` is set to RETRY_ACTION_RETRY_NOW and policy is not IOTHUB_CLIENT_RETRY_IMMEDIATE, `retry_control->current_wait_time_in_secs` shall be set using calculate_next_wait_time()] + retry_control->current_wait_time_in_secs = calculate_next_wait_time(retry_control); + } + } + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_018: [If no errors occur, `retry_control_should_retry` shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int retry_control_set_option(RETRY_CONTROL_HANDLE retry_control_handle, const char* name, const void* value) +{ + int result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_036: [If `retry_control_handle`, `name` or `value` are NULL, `retry_control_set_option` shall fail and return non-zero] + if (retry_control_handle == NULL || name == NULL || value == NULL) + { + LogError("Failed to set option (either retry_state_handle (%p), name (%p) or value (%p) are NULL)", retry_control_handle, name, value); + result = __FAILURE__; + } + else + { + RETRY_CONTROL_INSTANCE* retry_control = (RETRY_CONTROL_INSTANCE*)retry_control_handle; + + if (strcmp(RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS, name) == 0) + { + unsigned int cast_value = *((unsigned int*)value); + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_037: [If `name` is "initial_wait_time_in_secs" and `value` is less than 1, `retry_control_set_option` shall fail and return non-zero] + if (cast_value < 1) + { + LogError("Failed to set option '%s' (value must be equal or greater to 1)", name); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_038: [If `name` is "initial_wait_time_in_secs", `value` shall be saved on `retry_control->initial_wait_time_in_secs`] + retry_control->initial_wait_time_in_secs = cast_value; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_044: [If no errors occur, retry_control_set_option shall return 0] + result = RESULT_OK; + } + } + else if (strcmp(RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT, name) == 0) + { + unsigned int cast_value = *((unsigned int*)value); + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_039: [If `name` is "max_jitter_percent" and `value` is less than 0 or greater than 100, `retry_control_set_option` shall fail and return non-zero] + if (cast_value > 100) // it's unsigned int, it doesn't need to be checked for less than zero. + { + LogError("Failed to set option '%s' (value must be in the range 0 to 100)", name); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_040: [If `name` is "max_jitter_percent", value shall be saved on `retry_control->max_jitter_percent`] + retry_control->max_jitter_percent = cast_value; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_044: [If no errors occur, retry_control_set_option shall return 0] + result = RESULT_OK; + } + } + else if (strcmp(RETRY_CONTROL_OPTION_SAVED_OPTIONS, name) == 0) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_041: [If `name` is "retry_control_options", value shall be fed to `retry_control` using OptionHandler_FeedOptions] + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, retry_control_handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_042: [If OptionHandler_FeedOptions fails, `retry_control_set_option` shall fail and return non-zero] + LogError("messenger_set_option failed (OptionHandler_FeedOptions failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_044: [If no errors occur, `retry_control_set_option` shall return 0] + result = RESULT_OK; + } + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_043: [If `name` is not a supported option, `retry_control_set_option` shall fail and return non-zero] + else + { + LogError("messenger_set_option failed (option with name '%s' is not suppported)", name); + result = __FAILURE__; + } + } + + return result; +} + +OPTIONHANDLER_HANDLE retry_control_retrieve_options(RETRY_CONTROL_HANDLE retry_control_handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_045: [If `retry_control_handle`, `retry_control_retrieve_options` shall fail and return NULL] + if (retry_control_handle == NULL) + { + LogError("Failed to retrieve options (retry_state_handle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_046: [An instance of OPTIONHANDLER_HANDLE (a.k.a. `options`) shall be created using OptionHandler_Create] + OPTIONHANDLER_HANDLE options = OptionHandler_Create(retry_control_clone_option, retry_control_destroy_option, (pfSetOption)retry_control_set_option); + + if (options == NULL) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_047: [If OptionHandler_Create fails, `retry_control_retrieve_options` shall fail and return NULL] + LogError("Failed to retrieve options (OptionHandler_Create failed)"); + result = NULL; + } + else + { + RETRY_CONTROL_INSTANCE* retry_control = (RETRY_CONTROL_INSTANCE*)retry_control_handle; + + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_050: [`retry_control->initial_wait_time_in_secs` shall be added to `options` using OptionHandler_Add] + if (OptionHandler_AddOption(options, RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS, (void*)&retry_control->initial_wait_time_in_secs) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_052: [If any call to OptionHandler_Add fails, `retry_control_retrieve_options` shall fail and return NULL] + LogError("Failed to retrieve options (OptionHandler_Create failed for option '%s')", RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS); + result = NULL; + } + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_051: [`retry_control->max_jitter_percent` shall be added to `options` using OptionHandler_Add] + else if (OptionHandler_AddOption(options, RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT, (void*)&retry_control->max_jitter_percent) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_052: [If any call to OptionHandler_Add fails, `retry_control_retrieve_options` shall fail and return NULL] + LogError("Failed to retrieve options (OptionHandler_Create failed for option '%s')", RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS); + result = NULL; + } + else + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_054: [If no errors occur, `retry_control_retrieve_options` shall return the OPTIONHANDLER_HANDLE instance] + result = options; + } + + if (result == NULL) + { + // Codes_SRS_IOTHUB_CLIENT_RETRY_CONTROL_09_053: [If any failures occur, `retry_control_retrieve_options` shall release any memory it has allocated] + OptionHandler_Destroy(options); + } + } + } + + return result; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_device_client.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/gballoc.h" + +#include "iothub_client_core.h" +#include "iothub_device_client.h" + +IOTHUB_DEVICE_CLIENT_HANDLE IoTHubDeviceClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_DEVICE_CLIENT_HANDLE)IoTHubClientCore_CreateFromConnectionString(connectionString, protocol); +} + +IOTHUB_DEVICE_CLIENT_HANDLE IoTHubDeviceClient_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + return (IOTHUB_DEVICE_CLIENT_HANDLE)IoTHubClientCore_Create(config); +} + +IOTHUB_DEVICE_CLIENT_HANDLE IoTHubDeviceClient_CreateWithTransport(TRANSPORT_HANDLE transportHandle, const IOTHUB_CLIENT_CONFIG* config) +{ + return (IOTHUB_DEVICE_CLIENT_HANDLE)IoTHubClientCore_CreateWithTransport(transportHandle, config); +} + +IOTHUB_DEVICE_CLIENT_HANDLE IoTHubDeviceClient_CreateFromDeviceAuth(const char* iothub_uri, const char* device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_DEVICE_CLIENT_HANDLE)IoTHubClientCore_CreateFromDeviceAuth(iothub_uri, device_id, protocol); +} + +void IoTHubDeviceClient_Destroy(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle) +{ + IoTHubClientCore_Destroy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SendEventAsync(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendEventAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_GetSendStatus(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + return IoTHubClientCore_GetSendStatus((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, iotHubClientStatus); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetMessageCallback(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetMessageCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, messageCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetConnectionStatusCallback(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + return IoTHubClientCore_SetConnectionStatusCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, connectionStatusCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetRetryPolicy(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_SetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_GetRetryPolicy(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_GetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_GetLastMessageReceiveTime(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) +{ + return IoTHubClientCore_GetLastMessageReceiveTime((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, lastMessageReceiveTime); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetOption(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value) +{ + return IoTHubClientCore_SetOption((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, optionName, value); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetDeviceTwinCallback(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceTwinCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, deviceTwinCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SendReportedState(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendReportedState((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, reportedState, size, reportedStateCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_SetDeviceMethodCallback(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceMethodCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, deviceMethodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_DeviceMethodResponse(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, METHOD_HANDLE methodId, const unsigned char* response, size_t respSize, int statusCode) +{ + return IoTHubClientCore_DeviceMethodResponse((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, methodId, response, respSize, statusCode); +} + +#ifndef DONT_USE_UPLOADTOBLOB + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_UploadToBlobAsync(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback, void* context) +{ + return IoTHubClientCore_UploadToBlobAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, destinationFileName, source, size, iotHubClientFileUploadCallback, context); +} + +IOTHUB_CLIENT_RESULT IoTHubDeviceClient_UploadMultipleBlocksToBlobAsync(IOTHUB_DEVICE_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + return IoTHubClientCore_UploadMultipleBlocksToBlobAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, destinationFileName, NULL, getDataCallbackEx, context); +} + +#endif /*DONT_USE_UPLOADTOBLOB*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_edge_modules.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,4 @@ +LIBRARY iothub_client_dll +EXPORTS + IoTHubModuleClient_LL_CreateFromEnvironment + IoTHubModuleClient_CreateFromEnvironment
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_message.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1027 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/buffer_.h" + +#include "iothub_message.h" + +DEFINE_ENUM_STRINGS(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUBMESSAGE_CONTENT_TYPE, IOTHUBMESSAGE_CONTENT_TYPE_VALUES); + +#define LOG_IOTHUB_MESSAGE_ERROR() \ + LogError("(result = %s)", ENUM_TO_STRING(IOTHUB_MESSAGE_RESULT, result)); + +typedef struct IOTHUB_MESSAGE_HANDLE_DATA_TAG +{ + IOTHUBMESSAGE_CONTENT_TYPE contentType; + union + { + BUFFER_HANDLE byteArray; + STRING_HANDLE string; + } value; + MAP_HANDLE properties; + char* messageId; + char* correlationId; + char* userDefinedContentType; + char* contentEncoding; + char* outputName; + char* inputName; + char* connectionModuleId; + char* connectionDeviceId; + IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE diagnosticData; +}IOTHUB_MESSAGE_HANDLE_DATA; + +static bool ContainsOnlyUsAscii(const char* asciiValue) +{ + bool result = true; + const char* iterator = asciiValue; + while (iterator != NULL && *iterator != '\0') + { + // Allow only printable ascii char + if (*iterator < ' ' || *iterator > '~') + { + result = false; + break; + } + iterator++; + } + return result; +} + +/* Codes_SRS_IOTHUBMESSAGE_07_008: [ValidateAsciiCharactersFilter shall loop through the mapKey and mapValue strings to ensure that they only contain valid US-Ascii characters Ascii value 32 - 126.] */ +static int ValidateAsciiCharactersFilter(const char* mapKey, const char* mapValue) +{ + int result; + if (!ContainsOnlyUsAscii(mapKey) || !ContainsOnlyUsAscii(mapValue)) + { + result = __FAILURE__; + } + else + { + result = 0; + } + return result; +} + +static void DestroyDiagnosticPropertyData(IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE diagnosticHandle) +{ + if (diagnosticHandle != NULL) + { + free(diagnosticHandle->diagnosticId); + free(diagnosticHandle->diagnosticCreationTimeUtc); + } + free(diagnosticHandle); +} + +static void DestroyMessageData(IOTHUB_MESSAGE_HANDLE_DATA* handleData) +{ + if (handleData->contentType == IOTHUBMESSAGE_BYTEARRAY) + { + BUFFER_delete(handleData->value.byteArray); + } + else if (handleData->contentType == IOTHUBMESSAGE_STRING) + { + STRING_delete(handleData->value.string); + } + + Map_Destroy(handleData->properties); + free(handleData->messageId); + handleData->messageId = NULL; + free(handleData->correlationId); + handleData->correlationId = NULL; + free(handleData->userDefinedContentType); + free(handleData->contentEncoding); + DestroyDiagnosticPropertyData(handleData->diagnosticData); + free(handleData->outputName); + free(handleData->inputName); + free(handleData->connectionModuleId); + free(handleData->connectionDeviceId); + free(handleData); +} + +static IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE CloneDiagnosticPropertyData(const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* source) +{ + IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE result = NULL; + if (source == NULL) + { + LogError("Invalid argument - source is NULL"); + } + else + { + result = (IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE)malloc(sizeof(IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA)); + if (result == NULL) + { + LogError("malloc failed"); + } + else + { + result->diagnosticCreationTimeUtc = NULL; + result->diagnosticId = NULL; + if (source->diagnosticCreationTimeUtc != NULL && mallocAndStrcpy_s(&result->diagnosticCreationTimeUtc, source->diagnosticCreationTimeUtc) != 0) + { + LogError("mallocAndStrcpy_s for diagnosticCreationTimeUtc failed"); + free(result); + result = NULL; + } + else if (source->diagnosticId != NULL && mallocAndStrcpy_s(&result->diagnosticId, source->diagnosticId) != 0) + { + LogError("mallocAndStrcpy_s for diagnosticId failed"); + free(result->diagnosticCreationTimeUtc); + free(result); + result = NULL; + } + } + } + return result; +} + +IOTHUB_MESSAGE_HANDLE IoTHubMessage_CreateFromByteArray(const unsigned char* byteArray, size_t size) +{ + IOTHUB_MESSAGE_HANDLE_DATA* result; + if ((byteArray == NULL) && (size != 0)) + { + LogError("Invalid argument - byteArray is NULL"); + result = NULL; + } + else + { + result = (IOTHUB_MESSAGE_HANDLE_DATA*)malloc(sizeof(IOTHUB_MESSAGE_HANDLE_DATA)); + if (result == NULL) + { + LogError("unable to malloc"); + /*Codes_SRS_IOTHUBMESSAGE_02_024: [If there are any errors then IoTHubMessage_CreateFromByteArray shall return NULL.] */ + /*let it go through*/ + } + else + { + const unsigned char* source; + unsigned char temp = 0x00; + + memset(result, 0, sizeof(*result)); + /*Codes_SRS_IOTHUBMESSAGE_02_026: [The type of the new message shall be IOTHUBMESSAGE_BYTEARRAY.] */ + result->contentType = IOTHUBMESSAGE_BYTEARRAY; + + if (size != 0) + { + /*Codes_SRS_IOTHUBMESSAGE_06_002: [If size is NOT zero then byteArray MUST NOT be NULL*/ + if (byteArray == NULL) + { + LogError("Attempted to create a Hub Message from a NULL pointer!"); + DestroyMessageData(result); + result = NULL; + source = NULL; + } + else + { + source = byteArray; + } + } + else + { + /*Codes_SRS_IOTHUBMESSAGE_06_001: [If size is zero then byteArray may be NULL.]*/ + source = &temp; + } + if (result != NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_02_022: [IoTHubMessage_CreateFromByteArray shall call BUFFER_create passing byteArray and size as parameters.] */ + if ((result->value.byteArray = BUFFER_create(source, size)) == NULL) + { + LogError("BUFFER_create failed"); + /*Codes_SRS_IOTHUBMESSAGE_02_024: [If there are any errors then IoTHubMessage_CreateFromByteArray shall return NULL.] */ + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_023: [IoTHubMessage_CreateFromByteArray shall call Map_Create to create the message properties.] */ + else if ((result->properties = Map_Create(ValidateAsciiCharactersFilter)) == NULL) + { + LogError("Map_Create for properties failed"); + /*Codes_SRS_IOTHUBMESSAGE_02_024: [If there are any errors then IoTHubMessage_CreateFromByteArray shall return NULL.] */ + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_025: [Otherwise, IoTHubMessage_CreateFromByteArray shall return a non-NULL handle.] */ + } + } + } + return result; +} + +IOTHUB_MESSAGE_HANDLE IoTHubMessage_CreateFromString(const char* source) +{ + IOTHUB_MESSAGE_HANDLE_DATA* result; + if (source == NULL) + { + LogError("Invalid argument - source is NULL"); + result = NULL; + } + else + { + result = (IOTHUB_MESSAGE_HANDLE_DATA*)malloc(sizeof(IOTHUB_MESSAGE_HANDLE_DATA)); + if (result == NULL) + { + LogError("malloc failed"); + /*Codes_SRS_IOTHUBMESSAGE_02_029: [If there are any encountered in the execution of IoTHubMessage_CreateFromString then IoTHubMessage_CreateFromString shall return NULL.] */ + /*let it go through*/ + } + else + { + memset(result, 0, sizeof(*result)); + /*Codes_SRS_IOTHUBMESSAGE_02_032: [The type of the new message shall be IOTHUBMESSAGE_STRING.] */ + result->contentType = IOTHUBMESSAGE_STRING; + + /*Codes_SRS_IOTHUBMESSAGE_02_027: [IoTHubMessage_CreateFromString shall call STRING_construct passing source as parameter.] */ + if ((result->value.string = STRING_construct(source)) == NULL) + { + LogError("STRING_construct failed"); + /*Codes_SRS_IOTHUBMESSAGE_02_029: [If there are any encountered in the execution of IoTHubMessage_CreateFromString then IoTHubMessage_CreateFromString shall return NULL.] */ + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_028: [IoTHubMessage_CreateFromString shall call Map_Create to create the message properties.] */ + else if ((result->properties = Map_Create(ValidateAsciiCharactersFilter)) == NULL) + { + LogError("Map_Create for properties failed"); + /*Codes_SRS_IOTHUBMESSAGE_02_029: [If there are any encountered in the execution of IoTHubMessage_CreateFromString then IoTHubMessage_CreateFromString shall return NULL.] */ + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_031: [Otherwise, IoTHubMessage_CreateFromString shall return a non-NULL handle.] */ + } + } + return result; +} + +/*Codes_SRS_IOTHUBMESSAGE_03_001: [IoTHubMessage_Clone shall create a new IoT hub message with data content identical to that of the iotHubMessageHandle parameter.]*/ +IOTHUB_MESSAGE_HANDLE IoTHubMessage_Clone(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + IOTHUB_MESSAGE_HANDLE_DATA* result; + const IOTHUB_MESSAGE_HANDLE_DATA* source = (const IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + /* Codes_SRS_IOTHUBMESSAGE_03_005: [IoTHubMessage_Clone shall return NULL if iotHubMessageHandle is NULL.] */ + if (source == NULL) + { + result = NULL; + LogError("iotHubMessageHandle parameter cannot be NULL for IoTHubMessage_Clone"); + } + else + { + result = (IOTHUB_MESSAGE_HANDLE_DATA*)malloc(sizeof(IOTHUB_MESSAGE_HANDLE_DATA)); + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + if (result == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + /*do nothing and return as is*/ + LogError("unable to malloc"); + } + else + { + memset(result, 0, sizeof(*result)); + result->contentType = source->contentType; + + if (source->messageId != NULL && mallocAndStrcpy_s(&result->messageId, source->messageId) != 0) + { + LogError("unable to Copy messageId"); + DestroyMessageData(result); + result = NULL; + } + else if (source->correlationId != NULL && mallocAndStrcpy_s(&result->correlationId, source->correlationId) != 0) + { + LogError("unable to Copy correlationId"); + DestroyMessageData(result); + result = NULL; + } + else if (source->userDefinedContentType != NULL && mallocAndStrcpy_s(&result->userDefinedContentType, source->userDefinedContentType) != 0) + { + LogError("unable to copy contentType"); + DestroyMessageData(result); + result = NULL; + } + else if (source->contentEncoding != NULL && mallocAndStrcpy_s(&result->contentEncoding, source->contentEncoding) != 0) + { + LogError("unable to copy contentEncoding"); + DestroyMessageData(result); + result = NULL; + } + else if (source->diagnosticData != NULL && (result->diagnosticData = CloneDiagnosticPropertyData(source->diagnosticData)) == NULL) + { + LogError("unable to copy CloneDiagnosticPropertyData"); + DestroyMessageData(result); + result = NULL; + } + else if (source->outputName != NULL && mallocAndStrcpy_s(&result->outputName, source->outputName) != 0) + { + LogError("unable to copy outputName"); + DestroyMessageData(result); + result = NULL; + } + else if (source->inputName != NULL && mallocAndStrcpy_s(&result->inputName, source->inputName) != 0) + { + LogError("unable to copy inputName"); + DestroyMessageData(result); + result = NULL; + } + else if (source->connectionModuleId != NULL && mallocAndStrcpy_s(&result->connectionModuleId, source->connectionModuleId) != 0) + { + LogError("unable to copy inputName"); + DestroyMessageData(result); + result = NULL; + } + else if (source->connectionDeviceId != NULL && mallocAndStrcpy_s(&result->connectionDeviceId, source->connectionDeviceId) != 0) + { + LogError("unable to copy inputName"); + DestroyMessageData(result); + result = NULL; + } + else if (source->contentType == IOTHUBMESSAGE_BYTEARRAY) + { + /*Codes_SRS_IOTHUBMESSAGE_02_006: [IoTHubMessage_Clone shall clone to content by a call to BUFFER_clone] */ + if ((result->value.byteArray = BUFFER_clone(source->value.byteArray)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + LogError("unable to BUFFER_clone"); + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_005: [IoTHubMessage_Clone shall clone the properties map by using Map_Clone.] */ + else if ((result->properties = Map_Clone(source->properties)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + LogError("unable to Map_Clone"); + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_03_002: [IoTHubMessage_Clone shall return upon success a non-NULL handle to the newly created IoT hub message.]*/ + } + else /*can only be STRING*/ + { + /*Codes_SRS_IOTHUBMESSAGE_02_006: [IoTHubMessage_Clone shall clone the content by a call to BUFFER_clone or STRING_clone] */ + if ((result->value.string = STRING_clone(source->value.string)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + LogError("failed to STRING_clone"); + DestroyMessageData(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGE_02_005: [IoTHubMessage_Clone shall clone the properties map by using Map_Clone.] */ + else if ((result->properties = Map_Clone(source->properties)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_03_004: [IoTHubMessage_Clone shall return NULL if it fails for any reason.]*/ + LogError("unable to Map_Clone"); + DestroyMessageData(result); + result = NULL; + } + } + } + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_GetByteArray(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const unsigned char** buffer, size_t* size) +{ + IOTHUB_MESSAGE_RESULT result; + if ( + (iotHubMessageHandle == NULL) || + (buffer == NULL) || + (size == NULL) + ) + { + /*Codes_SRS_IOTHUBMESSAGE_01_014: [If any of the arguments passed to IoTHubMessage_GetByteArray is NULL IoTHubMessage_GetByteArray shall return IOTHUBMESSAGE_INVALID_ARG.] */ + LogError("invalid parameter (NULL) to IoTHubMessage_GetByteArray IOTHUB_MESSAGE_HANDLE iotHubMessageHandle=%p, const unsigned char** buffer=%p, size_t* size=%p", iotHubMessageHandle, buffer, size); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + if (handleData->contentType != IOTHUBMESSAGE_BYTEARRAY) + { + /*Codes_SRS_IOTHUBMESSAGE_02_021: [If iotHubMessageHandle is not a iothubmessage containing BYTEARRAY data, then IoTHubMessage_GetData shall write in *buffer NULL and shall set *size to 0.] */ + result = IOTHUB_MESSAGE_INVALID_ARG; + LogError("invalid type of message %s", ENUM_TO_STRING(IOTHUBMESSAGE_CONTENT_TYPE, handleData->contentType)); + } + else + { + /*Codes_SRS_IOTHUBMESSAGE_01_011: [The pointer shall be obtained by using BUFFER_u_char and it shall be copied in the buffer argument.]*/ + *buffer = BUFFER_u_char(handleData->value.byteArray); + /*Codes_SRS_IOTHUBMESSAGE_01_012: [The size of the associated data shall be obtained by using BUFFER_length and it shall be copied to the size argument.]*/ + *size = BUFFER_length(handleData->value.byteArray); + result = IOTHUB_MESSAGE_OK; + } + } + return result; +} + +const char* IoTHubMessage_GetString(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + if (iotHubMessageHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_02_016: [If any parameter is NULL then IoTHubMessage_GetString shall return NULL.] */ + result = NULL; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + if (handleData->contentType != IOTHUBMESSAGE_STRING) + { + /*Codes_SRS_IOTHUBMESSAGE_02_017: [IoTHubMessage_GetString shall return NULL if the iotHubMessageHandle does not refer to a IOTHUBMESSAGE of type STRING.] */ + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGE_02_018: [IoTHubMessage_GetStringData shall return the currently stored null terminated string.] */ + result = STRING_c_str(handleData->value.string); + } + } + return result; +} + +IOTHUBMESSAGE_CONTENT_TYPE IoTHubMessage_GetContentType(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + IOTHUBMESSAGE_CONTENT_TYPE result; + /*Codes_SRS_IOTHUBMESSAGE_02_008: [If any parameter is NULL then IoTHubMessage_GetContentType shall return IOTHUBMESSAGE_UNKNOWN.] */ + if (iotHubMessageHandle == NULL) + { + result = IOTHUBMESSAGE_UNKNOWN; + } + else + { + /*Codes_SRS_IOTHUBMESSAGE_02_009: [Otherwise IoTHubMessage_GetContentType shall return the type of the message.] */ + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + result = handleData->contentType; + } + return result; +} + +MAP_HANDLE IoTHubMessage_Properties(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + MAP_HANDLE result; + /*Codes_SRS_IOTHUBMESSAGE_02_001: [If iotHubMessageHandle is NULL then IoTHubMessage_Properties shall return NULL.]*/ + if (iotHubMessageHandle == NULL) + { + LogError("invalid arg (NULL) passed to IoTHubMessage_Properties"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGE_02_002: [Otherwise, for any non-NULL iotHubMessageHandle it shall return a non-NULL MAP_HANDLE.]*/ + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + result = handleData->properties; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetProperty(IOTHUB_MESSAGE_HANDLE msg_handle, const char* key, const char* value) +{ + IOTHUB_MESSAGE_RESULT result; + if (msg_handle == NULL || key == NULL || value == NULL) + { + LogError("invalid parameter (NULL) to IoTHubMessage_SetProperty iotHubMessageHandle=%p, key=%p, value=%p", msg_handle, key, value); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + if (Map_AddOrUpdate(msg_handle->properties, key, value) != MAP_OK) + { + LogError("Failure adding property to internal map"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + result = IOTHUB_MESSAGE_OK; + } + } + return result; +} + +const char* IoTHubMessage_GetProperty(IOTHUB_MESSAGE_HANDLE msg_handle, const char* key) +{ + const char* result; + if (msg_handle == NULL || key == NULL) + { + LogError("invalid parameter (NULL) to IoTHubMessage_GetProperty iotHubMessageHandle=%p, key=%p", msg_handle, key); + result = NULL; + } + else + { + bool key_exists = false; + // The return value is not neccessary, just check the key_exist variable + if ((Map_ContainsKey(msg_handle->properties, key, &key_exists) == MAP_OK) && key_exists) + { + result = Map_GetValueFromKey(msg_handle->properties, key); + } + else + { + result = NULL; + } + } + return result; +} + +const char* IoTHubMessage_GetCorrelationId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + /* Codes_SRS_IOTHUBMESSAGE_07_016: [if the iotHubMessageHandle parameter is NULL then IoTHubMessage_GetCorrelationId shall return a NULL value.] */ + if (iotHubMessageHandle == NULL) + { + LogError("invalid arg (NULL) passed to IoTHubMessage_GetCorrelationId"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBMESSAGE_07_017: [IoTHubMessage_GetCorrelationId shall return the correlationId as a const char*.] */ + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + result = handleData->correlationId; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetCorrelationId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* correlationId) +{ + IOTHUB_MESSAGE_RESULT result; + /* Codes_SRS_IOTHUBMESSAGE_07_018: [if any of the parameters are NULL then IoTHubMessage_SetCorrelationId shall return a IOTHUB_MESSAGE_INVALID_ARG value.]*/ + if (iotHubMessageHandle == NULL || correlationId == NULL) + { + LogError("invalid arg (NULL) passed to IoTHubMessage_SetCorrelationId"); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + /* Codes_SRS_IOTHUBMESSAGE_07_019: [If the IOTHUB_MESSAGE_HANDLE correlationId is not NULL, then the IOTHUB_MESSAGE_HANDLE correlationId will be deallocated.] */ + if (handleData->correlationId != NULL) + { + free(handleData->correlationId); + handleData->correlationId = NULL; + } + + if (mallocAndStrcpy_s(&handleData->correlationId, correlationId) != 0) + { + /* Codes_SRS_IOTHUBMESSAGE_07_020: [If the allocation or the copying of the correlationId fails, then IoTHubMessage_SetCorrelationId shall return IOTHUB_MESSAGE_ERROR.] */ + result = IOTHUB_MESSAGE_ERROR; + } + else + { + /* Codes_SRS_IOTHUBMESSAGE_07_021: [IoTHubMessage_SetCorrelationId finishes successfully it shall return IOTHUB_MESSAGE_OK.] */ + result = IOTHUB_MESSAGE_OK; + } + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetMessageId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* messageId) +{ + IOTHUB_MESSAGE_RESULT result; + /* Codes_SRS_IOTHUBMESSAGE_07_012: [if any of the parameters are NULL then IoTHubMessage_SetMessageId shall return a IOTHUB_MESSAGE_INVALID_ARG value.] */ + if (iotHubMessageHandle == NULL || messageId == NULL) + { + LogError("invalid arg (NULL) passed to IoTHubMessage_SetMessageId"); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + /* Codes_SRS_IOTHUBMESSAGE_07_013: [If the IOTHUB_MESSAGE_HANDLE messageId is not NULL, then the IOTHUB_MESSAGE_HANDLE messageId will be freed] */ + if (handleData->messageId != NULL) + { + free(handleData->messageId); + handleData->messageId = NULL; + } + + /* Codes_SRS_IOTHUBMESSAGE_07_014: [If the allocation or the copying of the messageId fails, then IoTHubMessage_SetMessageId shall return IOTHUB_MESSAGE_ERROR.] */ + if (mallocAndStrcpy_s(&handleData->messageId, messageId) != 0) + { + result = IOTHUB_MESSAGE_ERROR; + } + else + { + result = IOTHUB_MESSAGE_OK; + } + } + return result; +} + +const char* IoTHubMessage_GetMessageId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + /* Codes_SRS_IOTHUBMESSAGE_07_010: [if the iotHubMessageHandle parameter is NULL then IoTHubMessage_MessageId shall return a NULL value.] */ + if (iotHubMessageHandle == NULL) + { + LogError("invalid arg (NULL) passed to IoTHubMessage_GetMessageId"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBMESSAGE_07_011: [IoTHubMessage_MessageId shall return the messageId as a const char*.] */ + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + result = handleData->messageId; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetContentTypeSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* contentType) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_09_001: [If any of the parameters are NULL then IoTHubMessage_SetContentTypeSystemProperty shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if (iotHubMessageHandle == NULL || contentType == NULL) + { + LogError("Invalid argument (iotHubMessageHandle=%p, contentType=%p)", iotHubMessageHandle, contentType); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_09_002: [If the IOTHUB_MESSAGE_HANDLE `contentType` is not NULL it shall be deallocated.] + if (handleData->userDefinedContentType != NULL) + { + free(handleData->userDefinedContentType); + handleData->userDefinedContentType = NULL; + } + + if (mallocAndStrcpy_s(&handleData->userDefinedContentType, contentType) != 0) + { + LogError("Failed saving a copy of contentType"); + // Codes_SRS_IOTHUBMESSAGE_09_003: [If the allocation or the copying of `contentType` fails, then IoTHubMessage_SetContentTypeSystemProperty shall return IOTHUB_MESSAGE_ERROR.] + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_09_004: [If IoTHubMessage_SetContentTypeSystemProperty finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + } + + return result; +} + +const char* IoTHubMessage_GetContentTypeSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + + // Codes_SRS_IOTHUBMESSAGE_09_005: [If any of the parameters are NULL then IoTHubMessage_GetContentTypeSystemProperty shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_09_006: [IoTHubMessage_GetContentTypeSystemProperty shall return the `contentType` as a const char* ] + result = (const char*)handleData->userDefinedContentType; + } + + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetContentEncodingSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* contentEncoding) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_09_006: [If any of the parameters are NULL then IoTHubMessage_SetContentEncodingSystemProperty shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if (iotHubMessageHandle == NULL || contentEncoding == NULL) + { + LogError("Invalid argument (iotHubMessageHandle=%p, contentEncoding=%p)", iotHubMessageHandle, contentEncoding); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_09_007: [If the IOTHUB_MESSAGE_HANDLE `contentEncoding` is not NULL it shall be deallocated.] + if (handleData->contentEncoding != NULL) + { + free(handleData->contentEncoding); + handleData->contentEncoding = NULL; + } + + if (mallocAndStrcpy_s(&handleData->contentEncoding, contentEncoding) != 0) + { + LogError("Failed saving a copy of contentEncoding"); + // Codes_SRS_IOTHUBMESSAGE_09_008: [If the allocation or the copying of `contentEncoding` fails, then IoTHubMessage_SetContentEncodingSystemProperty shall return IOTHUB_MESSAGE_ERROR.] + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_09_009: [If IoTHubMessage_SetContentEncodingSystemProperty finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + } + + return result; +} + +const char* IoTHubMessage_GetContentEncodingSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + + // Codes_SRS_IOTHUBMESSAGE_09_010: [If any of the parameters are NULL then IoTHubMessage_GetContentEncodingSystemProperty shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_09_011: [IoTHubMessage_GetContentEncodingSystemProperty shall return the `contentEncoding` as a const char* ] + result = (const char*)handleData->contentEncoding; + } + + return result; +} + +const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* IoTHubMessage_GetDiagnosticPropertyData(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* result; + // Codes_SRS_IOTHUBMESSAGE_10_001: [If any of the parameters are NULL then IoTHubMessage_GetDiagnosticPropertyData shall return a NULL value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBMESSAGE_10_002: [IoTHubMessage_GetDiagnosticPropertyData shall return the diagnosticData as a const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA*.] */ + result = iotHubMessageHandle->diagnosticData; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetDiagnosticPropertyData(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData) +{ + IOTHUB_MESSAGE_RESULT result; + // Codes_SRS_IOTHUBMESSAGE_10_003: [If any of the parameters are NULL then IoTHubMessage_SetDiagnosticId shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if (iotHubMessageHandle == NULL || + diagnosticData == NULL || + diagnosticData->diagnosticCreationTimeUtc == NULL || + diagnosticData->diagnosticId == NULL) + { + LogError("Invalid argument (iotHubMessageHandle=%p, diagnosticData=%p, diagnosticData->diagnosticId=%p, diagnosticData->diagnosticCreationTimeUtc=%p)", + iotHubMessageHandle, diagnosticData, + diagnosticData == NULL ? NULL : diagnosticData->diagnosticId, + diagnosticData == NULL ? NULL : diagnosticData->diagnosticCreationTimeUtc); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_10_004: [If the IOTHUB_MESSAGE_HANDLE `diagnosticData` is not NULL it shall be deallocated.] + if (iotHubMessageHandle->diagnosticData != NULL) + { + DestroyDiagnosticPropertyData(iotHubMessageHandle->diagnosticData); + iotHubMessageHandle->diagnosticData = NULL; + } + + // Codes_SRS_IOTHUBMESSAGE_10_005: [If the allocation or the copying of `diagnosticData` fails, then IoTHubMessage_SetDiagnosticPropertyData shall return IOTHUB_MESSAGE_ERROR.] + if ((iotHubMessageHandle->diagnosticData = CloneDiagnosticPropertyData(diagnosticData)) == NULL) + { + LogError("Failed saving a copy of diagnosticData"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_10_006: [If IoTHubMessage_SetDiagnosticPropertyData finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + } + return result; +} + + +const char* IoTHubMessage_GetOutputName(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + // Codes_SRS_IOTHUBMESSAGE_31_034: [If the iotHubMessageHandle parameter is NULL then IoTHubMessage_GetOutputName shall return a NULL value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_035: [IoTHubMessage_GetOutputName shall return the OutputName as a const char*.] + result = iotHubMessageHandle->outputName; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetOutputName(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* outputName) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_31_036: [If any of the parameters are NULL then IoTHubMessage_SetOutputName shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if ((iotHubMessageHandle == NULL) || (outputName == NULL)) + { + LogError("Invalid argument (iotHubMessageHandle=%p, outputName=%p)", iotHubMessageHandle, outputName); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_31_037: [If the IOTHUB_MESSAGE_HANDLE OutputName is not NULL, then the IOTHUB_MESSAGE_HANDLE OutputName will be deallocated.] + if (handleData->outputName != NULL) + { + free(handleData->outputName); + handleData->outputName = NULL; + } + + if (mallocAndStrcpy_s(&handleData->outputName, outputName) != 0) + { + // Codes_SRS_IOTHUBMESSAGE_31_038: [If the allocation or the copying of the OutputName fails, then IoTHubMessage_SetOutputName shall return IOTHUB_MESSAGE_ERROR.] + LogError("Failed saving a copy of outputName"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_039: [IoTHubMessage_SetOutputName finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + + } + + return result; +} + +const char* IoTHubMessage_GetInputName(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + // Codes_SRS_IOTHUBMESSAGE_31_040: [if the iotHubMessageHandle parameter is NULL then IoTHubMessage_GetInputName shall return a NULL value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_041: [IoTHubMessage_GetInputName shall return the InputName as a const char*.] + result = iotHubMessageHandle->inputName; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetInputName(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* inputName) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_31_042: [if any of the parameters are NULL then IoTHubMessage_SetInputName shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if ((iotHubMessageHandle == NULL) || (inputName == NULL)) + { + LogError("Invalid argument (iotHubMessageHandle=%p, inputName=%p)", iotHubMessageHandle, inputName); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_31_043: [If the IOTHUB_MESSAGE_HANDLE InputName is not NULL, then the IOTHUB_MESSAGE_HANDLE InputName will be deallocated.] + if (handleData->inputName != NULL) + { + free(handleData->inputName); + handleData->inputName = NULL; + } + + if (mallocAndStrcpy_s(&handleData->inputName, inputName) != 0) + { + // Codes_SRS_IOTHUBMESSAGE_31_044: [If the allocation or the copying of the InputName fails, then IoTHubMessage_SetInputName shall return IOTHUB_MESSAGE_ERROR.] + LogError("Failed saving a copy of inputName"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_045: [IoTHubMessage_SetInputName finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + + } + + return result; +} + + +const char* IoTHubMessage_GetConnectionModuleId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + // Codes_SRS_IOTHUBMESSAGE_31_046: [if the iotHubMessageHandle parameter is NULL then IoTHubMessage_GetConnectionModuleId shall return a NULL value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_047: [IoTHubMessage_GetConnectionModuleId shall return the ConnectionModuleId as a const char*.] + result = iotHubMessageHandle->connectionModuleId; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetConnectionModuleId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* connectionModuleId) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_31_048: [if any of the parameters are NULL then IoTHubMessage_SetConnectionModuleId shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if ((iotHubMessageHandle == NULL) || (connectionModuleId == NULL)) + { + LogError("Invalid argument (iotHubMessageHandle=%p, connectionModuleId=%p)", iotHubMessageHandle, connectionModuleId); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_31_049: [If the IOTHUB_MESSAGE_HANDLE ConnectionModuleId is not NULL, then the IOTHUB_MESSAGE_HANDLE ConnectionModuleId will be deallocated.] + if (handleData->connectionModuleId != NULL) + { + free(handleData->connectionModuleId); + handleData->connectionModuleId = NULL; + } + + if (mallocAndStrcpy_s(&handleData->connectionModuleId, connectionModuleId) != 0) + { + // Codes_SRS_IOTHUBMESSAGE_31_050: [If the allocation or the copying of the ConnectionModuleId fails, then IoTHubMessage_SetConnectionModuleId shall return IOTHUB_MESSAGE_ERROR.] + LogError("Failed saving a copy of connectionModuleId"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_051: [IoTHubMessage_SetConnectionModuleId finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + + } + + return result; +} + + +const char* IoTHubMessage_GetConnectionDeviceId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + const char* result; + // Codes_SRS_IOTHUBMESSAGE_31_052: [if the iotHubMessageHandle parameter is NULL then IoTHubMessage_GetConnectionDeviceId shall return a NULL value.] + if (iotHubMessageHandle == NULL) + { + LogError("Invalid argument (iotHubMessageHandle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_053: [IoTHubMessage_GetConnectionDeviceId shall return the ConnectionDeviceId as a const char*.] + result = iotHubMessageHandle->connectionDeviceId; + } + return result; +} + +IOTHUB_MESSAGE_RESULT IoTHubMessage_SetConnectionDeviceId(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* connectionDeviceId) +{ + IOTHUB_MESSAGE_RESULT result; + + // Codes_SRS_IOTHUBMESSAGE_31_054: [if any of the parameters are NULL then IoTHubMessage_SetConnectionDeviceId shall return a IOTHUB_MESSAGE_INVALID_ARG value.] + if ((iotHubMessageHandle == NULL) || (connectionDeviceId == NULL)) + { + LogError("Invalid argument (iotHubMessageHandle=%p, connectionDeviceId=%p)", iotHubMessageHandle, connectionDeviceId); + result = IOTHUB_MESSAGE_INVALID_ARG; + } + else + { + IOTHUB_MESSAGE_HANDLE_DATA* handleData = (IOTHUB_MESSAGE_HANDLE_DATA*)iotHubMessageHandle; + + // Codes_SRS_IOTHUBMESSAGE_31_055: [If the IOTHUB_MESSAGE_HANDLE ConnectionDeviceId is not NULL, then the IOTHUB_MESSAGE_HANDLE ConnectionDeviceId will be deallocated.] + if (handleData->connectionDeviceId != NULL) + { + free(handleData->connectionDeviceId); + handleData->connectionDeviceId = NULL; + } + + if (mallocAndStrcpy_s(&handleData->connectionDeviceId, connectionDeviceId) != 0) + { + // Codes_SRS_IOTHUBMESSAGE_31_056: [If the allocation or the copying of the ConnectionDeviceId fails, then IoTHubMessage_SetConnectionDeviceId shall return IOTHUB_MESSAGE_ERROR.] + LogError("Failed saving a copy of connectionDeviceId"); + result = IOTHUB_MESSAGE_ERROR; + } + else + { + // Codes_SRS_IOTHUBMESSAGE_31_057: [IoTHubMessage_SetConnectionDeviceId finishes successfully it shall return IOTHUB_MESSAGE_OK.] + result = IOTHUB_MESSAGE_OK; + } + + } + + return result; +} + +void IoTHubMessage_Destroy(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) +{ + /*Codes_SRS_IOTHUBMESSAGE_01_004: [If iotHubMessageHandle is NULL, IoTHubMessage_Destroy shall do nothing.] */ + if (iotHubMessageHandle != NULL) + { + /*Codes_SRS_IOTHUBMESSAGE_01_003: [IoTHubMessage_Destroy shall free all resources associated with iotHubMessageHandle.] */ + DestroyMessageData((IOTHUB_MESSAGE_HANDLE_DATA* )iotHubMessageHandle); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_module_client.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" + +#include "iothub_client_core.h" +#include "iothub_module_client.h" + +IOTHUB_MODULE_CLIENT_HANDLE IoTHubModuleClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return (IOTHUB_MODULE_CLIENT_HANDLE)IoTHubClientCore_CreateFromConnectionString(connectionString, protocol); +} + +void IoTHubModuleClient_Destroy(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle) +{ + IoTHubClientCore_Destroy((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SendEventAsync(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendEventAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_GetSendStatus(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + return IoTHubClientCore_GetSendStatus((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, iotHubClientStatus); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetMessageCallback(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetInputMessageCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, NULL, messageCallback, userContextCallback);} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetConnectionStatusCallback(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + return IoTHubClientCore_SetConnectionStatusCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, connectionStatusCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetRetryPolicy(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_SetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_GetRetryPolicy(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + return IoTHubClientCore_GetRetryPolicy((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, retryPolicy, retryTimeoutLimitInSeconds); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_GetLastMessageReceiveTime(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, time_t* lastMessageReceiveTime) +{ + return IoTHubClientCore_GetLastMessageReceiveTime((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, lastMessageReceiveTime); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetOption(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, const char* optionName, const void* value) +{ + return IoTHubClientCore_SetOption((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, optionName, value); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetModuleTwinCallback(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK moduleTwinCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceTwinCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, moduleTwinCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SendReportedState(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendReportedState((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, reportedState, size, reportedStateCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetModuleMethodCallback(IOTHUB_MODULE_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC methodCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetDeviceMethodCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubClientHandle, (IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC)methodCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SendEventToOutputAsync(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, const char* outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + return IoTHubClientCore_SendEventToOutputAsync((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, eventMessageHandle, outputName, eventConfirmationCallback, userContextCallback); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_SetInputMessageCallback(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback, void* userContextCallback) +{ + return IoTHubClientCore_SetInputMessageCallback((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, inputName, eventHandlerCallback, userContextCallback); +} + +#ifdef USE_EDGE_MODULES + +IOTHUB_MODULE_CLIENT_HANDLE IoTHubModuleClient_CreateFromEnvironment(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + return IoTHubClientCore_CreateFromEnvironment(protocol); +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_DeviceMethodInvokeAsync(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, const char* deviceId, const char* methodName, const char* methodPayload, unsigned int timeout, IOTHUB_METHOD_INVOKE_CALLBACK methodInvokeCallback, void* context) +{ + IOTHUB_CLIENT_RESULT result; + + if ((iotHubModuleClientHandle == NULL) || (deviceId == NULL) || (methodName == NULL) || (methodPayload == NULL)) + { + LogError("Argument cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + result = IoTHubClientCore_GenericMethodInvoke((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, deviceId, NULL, methodName, methodPayload, timeout, methodInvokeCallback, context); + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_ModuleMethodInvokeAsync(IOTHUB_MODULE_CLIENT_HANDLE iotHubModuleClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, IOTHUB_METHOD_INVOKE_CALLBACK methodInvokeCallback, void* context) +{ + IOTHUB_CLIENT_RESULT result; + + if ((iotHubModuleClientHandle == NULL) || (deviceId == NULL) || (moduleId == NULL) || (methodName == NULL) || (methodPayload == NULL)) + { + LogError("Argument cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + result = IoTHubClientCore_GenericMethodInvoke((IOTHUB_CLIENT_CORE_HANDLE)iotHubModuleClientHandle, deviceId, moduleId, methodName, methodPayload, timeout, methodInvokeCallback, context); + } + return result; +} + +#endif /*USE_EDGE_MODULES*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothub_module_client_ll.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,343 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/envvariable.h" + +#include "iothub_client_core_ll.h" +#include "iothub_client_authorization.h" +#include "iothub_module_client_ll.h" +#include "iothub_transport_ll.h" +#include "iothub_client_private.h" +#include "iothub_client_options.h" +#include "iothub_client_version.h" +#include "iothub_client_diagnostic.h" +#include <stdint.h> + +#ifndef DONT_USE_UPLOADTOBLOB +#include "iothub_client_ll_uploadtoblob.h" +#endif + +#ifdef USE_EDGE_MODULES +#include "internal/iothub_client_edge.h" +#endif + +typedef struct IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA_TAG +{ + IOTHUB_CLIENT_CORE_LL_HANDLE coreHandle; +#ifdef USE_EDGE_MODULES + IOTHUB_CLIENT_EDGE_HANDLE methodHandle; +#endif +} IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA; + + +IOTHUB_MODULE_CLIENT_LL_HANDLE IoTHubModuleClient_LL_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA* result; + + if ((result = malloc(sizeof(IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA))) == NULL) + { + LogError("Failed to allocate module client ll handle"); + } + else + { + memset(result, 0, sizeof(IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA)); + + if ((result->coreHandle = IoTHubClientCore_LL_CreateFromConnectionString(connectionString, protocol)) == NULL) + { + LogError("Failed to create core handle"); + IoTHubModuleClient_LL_Destroy(result); + result = NULL; + } + //Edge handle is not added + } + + return result; +} + +void IoTHubModuleClient_LL_Destroy(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle) +{ + if (iotHubModuleClientHandle != NULL) + { + IoTHubClientCore_LL_Destroy(iotHubModuleClientHandle->coreHandle); + //Note that we don't have to free the module method handle because it's (currently, until major refactor) owned by the core + free(iotHubModuleClientHandle); + } +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SendEventAsync(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SendEventAsync(iotHubModuleClientHandle->coreHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_GetSendStatus(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_GetSendStatus(iotHubModuleClientHandle->coreHandle, iotHubClientStatus); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetMessageCallback(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetInputMessageCallback(iotHubModuleClientHandle->coreHandle, NULL, messageCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetConnectionStatusCallback(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetConnectionStatusCallback(iotHubModuleClientHandle->coreHandle, connectionStatusCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetRetryPolicy(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetRetryPolicy(iotHubModuleClientHandle->coreHandle, retryPolicy, retryTimeoutLimitInSeconds); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_GetRetryPolicy(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_GetRetryPolicy(iotHubModuleClientHandle->coreHandle, retryPolicy, retryTimeoutLimitInSeconds); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_GetLastMessageReceiveTime(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, time_t* lastMessageReceiveTime) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_GetLastMessageReceiveTime(iotHubModuleClientHandle->coreHandle, lastMessageReceiveTime); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +void IoTHubModuleClient_LL_DoWork(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle) +{ + if (iotHubModuleClientHandle != NULL) + { + IoTHubClientCore_LL_DoWork(iotHubModuleClientHandle->coreHandle); + } +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetOption(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, const char* optionName, const void* value) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetOption(iotHubModuleClientHandle->coreHandle, optionName, value); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetModuleTwinCallback(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK moduleTwinCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetDeviceTwinCallback(iotHubModuleClientHandle->coreHandle, moduleTwinCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SendReportedState(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SendReportedState(iotHubModuleClientHandle->coreHandle, reportedState, size, reportedStateCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetModuleMethodCallback(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC moduleMethodCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetDeviceMethodCallback(iotHubModuleClientHandle->coreHandle, moduleMethodCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SendEventToOutputAsync(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, const char* outputName, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SendEventToOutputAsync(iotHubModuleClientHandle->coreHandle, eventMessageHandle, outputName, eventConfirmationCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_SetInputMessageCallback(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, const char* inputName, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC eventHandlerCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClientCore_LL_SetInputMessageCallback(iotHubModuleClientHandle->coreHandle, inputName, eventHandlerCallback, userContextCallback); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +#ifdef USE_EDGE_MODULES + +IOTHUB_MODULE_CLIENT_LL_HANDLE IoTHubModuleClient_LL_CreateFromEnvironment(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA* result; + + if ((result = malloc(sizeof(IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA))) == NULL) + { + LogError("Failed to allocate module client ll handle"); + } + else + { + memset(result, 0, sizeof(IOTHUB_MODULE_CLIENT_LL_HANDLE_DATA)); + + if ((result->coreHandle = IoTHubClientCore_LL_CreateFromEnvironment(protocol)) == NULL) + { + LogError("Failed to create core handle"); + IoTHubModuleClient_LL_Destroy(result); + result = NULL; + } + else if ((result->methodHandle = IoTHubClientCore_LL_GetEdgeHandle(result->coreHandle)) == NULL) + { + LogError("Failed to set module method handle"); + IoTHubModuleClient_LL_Destroy(result); + result = NULL; + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_DeviceMethodInvoke(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, const char* deviceId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClient_Edge_DeviceMethodInvoke(iotHubModuleClientHandle->methodHandle, deviceId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubModuleClient_LL_ModuleMethodInvoke(IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_CLIENT_RESULT result; + + if (iotHubModuleClientHandle != NULL) + { + result = IoTHubClient_Edge_ModuleMethodInvoke(iotHubModuleClientHandle->methodHandle, deviceId, moduleId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + else + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} + +#endif /*USE_EDGE_MODULES*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,457 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <signal.h> +#include <stddef.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "internal/iothubtransport.h" +#include "iothub_client_core.h" +#include "internal/iothub_client_private.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/vector.h" + +#include "internal/iothubtransport.h" +#include "internal/iothub_client_private.h" +#include "iothub_transport_ll.h" +#include "iothub_client_core.h" +#include "../inc/iothub_client_core_common.h" + +typedef struct TRANSPORT_HANDLE_DATA_TAG +{ + TRANSPORT_LL_HANDLE transportLLHandle; + THREAD_HANDLE workerThreadHandle; + LOCK_HANDLE lockHandle; + sig_atomic_t stopThread; + TRANSPORT_PROVIDER_FIELDS; + VECTOR_HANDLE clients; + LOCK_HANDLE clientsLockHandle; + IOTHUB_CLIENT_MULTIPLEXED_DO_WORK clientDoWork; +} TRANSPORT_HANDLE_DATA; + +/* Used for Unit test */ +const size_t IoTHubTransport_ThreadTerminationOffset = offsetof(TRANSPORT_HANDLE_DATA, stopThread); + +TRANSPORT_HANDLE IoTHubTransport_Create(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol, const char* iotHubName, const char* iotHubSuffix) +{ + TRANSPORT_HANDLE_DATA *result; + + if (protocol == NULL || iotHubName == NULL || iotHubSuffix == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_002: [ If protocol is NULL, this function shall return NULL. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_003: [ If iotHubName is NULL, this function shall return NULL. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_004: [ If iotHubSuffix is NULL, this function shall return NULL. ]*/ + LogError("Invalid NULL argument, protocol [%p], name [%p], suffix [%p].", protocol, iotHubName, iotHubSuffix); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_032: [ IoTHubTransport_Create shall allocate memory for the transport data. ]*/ + result = (TRANSPORT_HANDLE_DATA*)malloc(sizeof(TRANSPORT_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_040: [ If memory allocation fails, IoTHubTransport_Create shall return NULL. ]*/ + LogError("Transport handle was not allocated."); + } + else + { + TRANSPORT_PROVIDER * transportProtocol = (TRANSPORT_PROVIDER*)(protocol()); + IOTHUB_CLIENT_CONFIG upperConfig; + upperConfig.deviceId = NULL; + upperConfig.deviceKey = NULL; + upperConfig.iotHubName = iotHubName; + upperConfig.iotHubSuffix = iotHubSuffix; + upperConfig.protocol = protocol; + upperConfig.protocolGatewayHostName = NULL; + + IOTHUBTRANSPORT_CONFIG transportLLConfig; + memset(&transportLLConfig, 0, sizeof(IOTHUBTRANSPORT_CONFIG)); + transportLLConfig.upperConfig = &upperConfig; + transportLLConfig.waitingToSend = NULL; + + /*Codes_SRS_IOTHUBTRANSPORT_17_005: [ IoTHubTransport_Create shall create the lower layer transport by calling the protocol's IoTHubTransport_Create function. ]*/ + result->transportLLHandle = transportProtocol->IoTHubTransport_Create(&transportLLConfig); + if (result->transportLLHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_006: [ If the creation of the transport fails, IoTHubTransport_Create shall return NULL. ]*/ + LogError("Lower Layer transport not created."); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_007: [ IoTHubTransport_Create shall create the transport lock by Calling Lock_Init. ]*/ + result->lockHandle = Lock_Init(); + if (result->lockHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_008: [ If the lock creation fails, IoTHubTransport_Create shall return NULL. ]*/ + LogError("transport Lock not created."); + transportProtocol->IoTHubTransport_Destroy(result->transportLLHandle); + free(result); + result = NULL; + } + else if ((result->clientsLockHandle = Lock_Init()) == NULL) + { + LogError("clients Lock not created."); + Lock_Deinit(result->lockHandle); + transportProtocol->IoTHubTransport_Destroy(result->transportLLHandle); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_038: [ IoTHubTransport_Create shall call VECTOR_Create to make a list of IOTHUB_CLIENT_CORE_HANDLE using this transport. ]*/ + result->clients = VECTOR_create(sizeof(IOTHUB_CLIENT_CORE_HANDLE)); + if (result->clients == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_039: [ If the Vector creation fails, IoTHubTransport_Create shall return NULL. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_009: [ IoTHubTransport_Create shall clean up any resources it creates if the function does not succeed. ]*/ + LogError("clients list not created."); + Lock_Deinit(result->clientsLockHandle); + Lock_Deinit(result->lockHandle); + transportProtocol->IoTHubTransport_Destroy(result->transportLLHandle); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_001: [ IoTHubTransport_Create shall return a non-NULL handle on success.]*/ + result->stopThread = 1; + result->clientDoWork = NULL; + result->workerThreadHandle = NULL; /* create thread when work needs to be done */ + result->IoTHubTransport_GetHostname = transportProtocol->IoTHubTransport_GetHostname; + result->IoTHubTransport_SetOption = transportProtocol->IoTHubTransport_SetOption; + result->IoTHubTransport_Create = transportProtocol->IoTHubTransport_Create; + result->IoTHubTransport_Destroy = transportProtocol->IoTHubTransport_Destroy; + result->IoTHubTransport_Register = transportProtocol->IoTHubTransport_Register; + result->IoTHubTransport_Unregister = transportProtocol->IoTHubTransport_Unregister; + result->IoTHubTransport_Subscribe = transportProtocol->IoTHubTransport_Subscribe; + result->IoTHubTransport_Unsubscribe = transportProtocol->IoTHubTransport_Unsubscribe; + result->IoTHubTransport_DoWork = transportProtocol->IoTHubTransport_DoWork; + result->IoTHubTransport_SetRetryPolicy = transportProtocol->IoTHubTransport_SetRetryPolicy; + result->IoTHubTransport_GetSendStatus = transportProtocol->IoTHubTransport_GetSendStatus; + } + } + } + } + } + + return result; +} + +static void multiplexed_client_do_work(TRANSPORT_HANDLE_DATA* transportData) +{ + if (Lock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to lock for multiplexed_client_do_work"); + } + else + { + size_t numberOfClients; + size_t iterator; + + numberOfClients = VECTOR_size(transportData->clients); + for (iterator = 0; iterator < numberOfClients; iterator++) + { + IOTHUB_CLIENT_CORE_HANDLE* clientHandle = (IOTHUB_CLIENT_CORE_HANDLE*)VECTOR_element(transportData->clients, iterator); + + if (clientHandle != NULL) + { + transportData->clientDoWork(*clientHandle); + } + } + + if (Unlock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to unlock on multiplexed_client_do_work"); + } + } +} + +static int transport_worker_thread(void* threadArgument) +{ + TRANSPORT_HANDLE_DATA* transportData = (TRANSPORT_HANDLE_DATA*)threadArgument; + + while (1) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_030: [ All calls to lower layer transport DoWork shall be protected by the lock created in IoTHubTransport_Create. ]*/ + if (Lock(transportData->lockHandle) == LOCK_OK) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_031: [ If acquiring the lock fails, lower layer transport DoWork shall not be called. ]*/ + if (transportData->stopThread) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_028: [ The thread shall exit when IoTHubTransport_EndWorkerThread has been called for each clientHandle which invoked IoTHubTransport_StartWorkerThread. ]*/ + (void)Unlock(transportData->lockHandle); + break; + } + else + { + (transportData->IoTHubTransport_DoWork)(transportData->transportLLHandle, NULL); + + (void)Unlock(transportData->lockHandle); + } + } + + multiplexed_client_do_work(transportData); + + /*Codes_SRS_IOTHUBTRANSPORT_17_029: [ The thread shall call lower layer transport DoWork every 1 ms. ]*/ + ThreadAPI_Sleep(1); + } + + ThreadAPI_Exit(0); + return 0; +} + +static bool find_by_handle(const void* element, const void* value) +{ + /* data stored at element is device handle */ + const IOTHUB_CLIENT_CORE_HANDLE * guess = (const IOTHUB_CLIENT_CORE_HANDLE *)element; + const IOTHUB_CLIENT_CORE_HANDLE match = (const IOTHUB_CLIENT_CORE_HANDLE)value; + return (*guess == match); +} + +static IOTHUB_CLIENT_RESULT start_worker_if_needed(TRANSPORT_HANDLE_DATA * transportData, IOTHUB_CLIENT_CORE_HANDLE clientHandle) +{ + IOTHUB_CLIENT_RESULT result; + if (transportData->workerThreadHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_018: [ If the worker thread does not exist, IoTHubTransport_StartWorkerThread shall start the thread using ThreadAPI_Create. ]*/ + transportData->stopThread = 0; + if (ThreadAPI_Create(&transportData->workerThreadHandle, transport_worker_thread, transportData) != THREADAPI_OK) + { + transportData->workerThreadHandle = NULL; + } + } + if (transportData->workerThreadHandle != NULL) + { + if (Lock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to lock for start_worker_if_needed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_020: [ IoTHubTransport_StartWorkerThread shall search for IoTHubClient clientHandle in the list of IoTHubClient handles. ]*/ + bool addToList = ((VECTOR_size(transportData->clients) == 0) || (VECTOR_find_if(transportData->clients, find_by_handle, clientHandle) == NULL)); + if (addToList) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_021: [ If handle is not found, then clientHandle shall be added to the list. ]*/ + if (VECTOR_push_back(transportData->clients, &clientHandle, 1) != 0) + { + LogError("Failed adding device to list (VECTOR_push_back failed)"); + /*Codes_SRS_IOTHUBTRANSPORT_17_042: [ If Adding to the client list fails, IoTHubTransport_StartWorkerThread shall return IOTHUB_CLIENT_ERROR. ]*/ + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else + { + result = IOTHUB_CLIENT_OK; + } + + if (Unlock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to unlock on start_worker_if_needed"); + } + } + } + else + { + result = IOTHUB_CLIENT_ERROR; + } + return result; +} + +static void stop_worker_thread(TRANSPORT_HANDLE_DATA * transportData) +{ + /*Codes_SRS_IOTHUBTRANSPORT_17_043: [** IoTHubTransport_SignalEndWorkerThread shall signal the worker thread to end.*/ + transportData->stopThread = 1; +} + +static void wait_worker_thread(TRANSPORT_HANDLE_DATA * transportData) +{ + if (transportData->workerThreadHandle != NULL) + { + int res; + /*Codes_SRS_IOTHUBTRANSPORT_17_027: [ If handle list is empty, IoTHubTransport_EndWorkerThread shall be joined. ]*/ + if (ThreadAPI_Join(transportData->workerThreadHandle, &res) != THREADAPI_OK) + { + LogError("ThreadAPI_Join failed"); + } + else + { + transportData->workerThreadHandle = NULL; + } + } +} + +static bool signal_end_worker_thread(TRANSPORT_HANDLE_DATA * transportData, IOTHUB_CLIENT_CORE_HANDLE clientHandle) +{ + bool okToJoin; + + if (Lock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to lock for signal_end_worker_thread"); + okToJoin = false; + } + else + { + void* element = VECTOR_find_if(transportData->clients, find_by_handle, clientHandle); + if (element != NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_026: [ IoTHubTransport_EndWorkerThread shall remove clientHandlehandle from handle list. ]*/ + VECTOR_erase(transportData->clients, element, 1); + } + /*Codes_SRS_IOTHUBTRANSPORT_17_025: [ If the worker thread does not exist, then IoTHubTransport_EndWorkerThread shall return. ]*/ + if (transportData->workerThreadHandle != NULL) + { + if (VECTOR_size(transportData->clients) == 0) + { + stop_worker_thread(transportData); + okToJoin = true; + } + else + { + okToJoin = false; + } + } + else + { + okToJoin = false; + } + + if (Unlock(transportData->clientsLockHandle) != LOCK_OK) + { + LogError("failed to unlock on signal_end_worker_thread"); + } + } + return okToJoin; +} + +void IoTHubTransport_Destroy(TRANSPORT_HANDLE transportHandle) +{ + /*Codes_SRS_IOTHUBTRANSPORT_17_011: [ IoTHubTransport_Destroy shall do nothing if transportHandle is NULL. ]*/ + if (transportHandle != NULL) + { + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + /*Codes_SRS_IOTHUBTRANSPORT_17_033: [ IoTHubTransport_Destroy shall lock the transport lock. ]*/ + if (Lock(transportData->lockHandle) != LOCK_OK) + { + LogError("Unable to lock - will still attempt to end thread without thread safety"); + stop_worker_thread(transportData); + } + else + { + stop_worker_thread(transportData); + (void)Unlock(transportData->lockHandle); + } + wait_worker_thread(transportData); + /*Codes_SRS_IOTHUBTRANSPORT_17_010: [ IoTHubTransport_Destroy shall free all resources. ]*/ + Lock_Deinit(transportData->lockHandle); + (transportData->IoTHubTransport_Destroy)(transportData->transportLLHandle); + VECTOR_destroy(transportData->clients); + Lock_Deinit(transportData->clientsLockHandle); + free(transportHandle); + } +} + +LOCK_HANDLE IoTHubTransport_GetLock(TRANSPORT_HANDLE transportHandle) +{ + LOCK_HANDLE lock; + if (transportHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_013: [ If transportHandle is NULL, IoTHubTransport_GetLock shall return NULL. ]*/ + lock = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_012: [ IoTHubTransport_GetLock shall return a handle to the transport lock. ]*/ + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + lock = transportData->lockHandle; + } + return lock; +} + +TRANSPORT_LL_HANDLE IoTHubTransport_GetLLTransport(TRANSPORT_HANDLE transportHandle) +{ + TRANSPORT_LL_HANDLE llTransport; + if (transportHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_015: [ If transportHandle is NULL, IoTHubTransport_GetLLTransport shall return NULL. ]*/ + llTransport = NULL; + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_014: [ IoTHubTransport_GetLLTransport shall return a handle to the lower layer transport. ]*/ + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + llTransport = transportData->transportLLHandle; + } + return llTransport; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_StartWorkerThread(TRANSPORT_HANDLE transportHandle, IOTHUB_CLIENT_CORE_HANDLE clientHandle, IOTHUB_CLIENT_MULTIPLEXED_DO_WORK muxDoWork) +{ + IOTHUB_CLIENT_RESULT result; + if (transportHandle == NULL || clientHandle == NULL) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_016: [ If transportHandle is NULL, IoTHubTransport_StartWorkerThread shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_017: [ If clientHandle is NULL, IoTHubTransport_StartWorkerThread shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + + if (transportData->clientDoWork == NULL) + { + transportData->clientDoWork = muxDoWork; + } + + if ((result = start_worker_if_needed(transportData, clientHandle)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBTRANSPORT_17_019: [ If thread creation fails, IoTHubTransport_StartWorkerThread shall return IOTHUB_CLIENT_ERROR. */ + LogError("Unable to start thread safely"); + } + else + { + /*Codes_SRS_IOTHUBTRANSPORT_17_022: [ Upon success, IoTHubTransport_StartWorkerThread shall return IOTHUB_CLIENT_OK. ]*/ + result = IOTHUB_CLIENT_OK; + } + } + return result; +} + +bool IoTHubTransport_SignalEndWorkerThread(TRANSPORT_HANDLE transportHandle, IOTHUB_CLIENT_CORE_HANDLE clientHandle) +{ + bool okToJoin; + /*Codes_SRS_IOTHUBTRANSPORT_17_023: [ If transportHandle is NULL, IoTHubTransport_EndWorkerThread shall return. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_024: [ If clientHandle is NULL, IoTHubTransport_EndWorkerThread shall return. ]*/ + if (!(transportHandle == NULL || clientHandle == NULL)) + { + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + okToJoin = signal_end_worker_thread(transportData, clientHandle); + } + else + { + okToJoin = false; + } + return okToJoin; +} + +void IoTHubTransport_JoinWorkerThread(TRANSPORT_HANDLE transportHandle, IOTHUB_CLIENT_CORE_HANDLE clientHandle) +{ + /*Codes_SRS_IOTHUBTRANSPORT_17_044: [ If transportHandle is NULL, IoTHubTransport_JoinWorkerThread shall do nothing. ]*/ + /*Codes_SRS_IOTHUBTRANSPORT_17_045: [ If clientHandle is NULL, IoTHubTransport_JoinWorkerThread shall do nothing. ]*/ + if (!(transportHandle == NULL || clientHandle == NULL)) + { + TRANSPORT_HANDLE_DATA * transportData = (TRANSPORT_HANDLE_DATA*)transportHandle; + /*Codes_SRS_IOTHUBTRANSPORT_17_027: [ The worker thread shall be joined. ]*/ + wait_worker_thread(transportData); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_cbs_auth.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,810 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "internal/iothubtransport_amqp_cbs_auth.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/sastoken.h" + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) +#define SAS_TOKEN_TYPE "servicebus.windows.net:sastoken" +#define IOTHUB_DEVICES_PATH_FMT "%s/devices/%s" +#define IOTHUB_DEVICES_MODULE_PATH_FMT "%s/devices/%s/modules/%s" +#define DEFAULT_CBS_REQUEST_TIMEOUT_SECS UINT32_MAX +#define DEFAULT_SAS_TOKEN_LIFETIME_SECS 3600 +#define DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS 1800 + +typedef struct AUTHENTICATION_INSTANCE_TAG +{ + const char* device_id; + const char* module_id; + STRING_HANDLE iothub_host_fqdn; + + ON_AUTHENTICATION_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_callback_context; + + ON_AUTHENTICATION_ERROR_CALLBACK on_error_callback; + void* on_error_callback_context; + + size_t cbs_request_timeout_secs; + size_t sas_token_lifetime_secs; + size_t sas_token_refresh_time_secs; + + AUTHENTICATION_STATE state; + CBS_HANDLE cbs_handle; + + bool is_cbs_put_token_in_progress; + bool is_sas_token_refresh_in_progress; + + time_t current_sas_token_put_time; + + // Auth module used to generating handle authorization + // with either SAS Token, x509 Certs, and Device SAS Token + IOTHUB_AUTHORIZATION_HANDLE authorization_module; +} AUTHENTICATION_INSTANCE; + + +// Helper functions: + +static void update_state(AUTHENTICATION_INSTANCE* instance, AUTHENTICATION_STATE new_state) +{ + if (new_state != instance->state) + { + AUTHENTICATION_STATE previous_state = instance->state; + instance->state = new_state; + + if (instance->on_state_changed_callback != NULL) + { + instance->on_state_changed_callback(instance->on_state_changed_callback_context, previous_state, new_state); + } + } +} + +static void notify_error(AUTHENTICATION_INSTANCE* instance, AUTHENTICATION_ERROR_CODE error_code) +{ + if (instance->on_error_callback != NULL) + { + instance->on_error_callback(instance->on_error_callback_context, error_code); + } +} + +static int verify_cbs_put_token_timeout(AUTHENTICATION_INSTANCE* instance, bool* is_timed_out) +{ + int result; + + if (instance->current_sas_token_put_time == INDEFINITE_TIME) + { + result = __FAILURE__; + LogError("Failed verifying if cbs_put_token has timed out (current_sas_token_put_time is not set)"); + } + else + { + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + result = __FAILURE__; + LogError("Failed verifying if cbs_put_token has timed out (get_time failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_083: [authentication_do_work() shall check for authentication timeout comparing the current time since `instance->current_sas_token_put_time` to `instance->cbs_request_timeout_secs`] + else if ((uint32_t)get_difftime(current_time, instance->current_sas_token_put_time) >= instance->cbs_request_timeout_secs) + { + *is_timed_out = true; + result = RESULT_OK; + } + else + { + *is_timed_out = false; + result = RESULT_OK; + } + } + + return result; +} + +static int verify_sas_token_refresh_timeout(AUTHENTICATION_INSTANCE* instance, bool* is_timed_out) +{ + int result; + + if (instance->current_sas_token_put_time == INDEFINITE_TIME) + { + result = __FAILURE__; + LogError("Failed verifying if SAS token refresh timed out (current_sas_token_put_time is not set)"); + } + else + { + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + result = __FAILURE__; + LogError("Failed verifying if SAS token refresh timed out (get_time failed)"); + } + else if ((uint32_t)get_difftime(current_time, instance->current_sas_token_put_time) >= instance->sas_token_refresh_time_secs) + { + *is_timed_out = true; + result = RESULT_OK; + } + else + { + *is_timed_out = false; + result = RESULT_OK; + } + } + + return result; +} + +static STRING_HANDLE create_device_and_module_path(STRING_HANDLE iothub_host_fqdn, const char* device_id, const char* module_id) +{ + STRING_HANDLE devices_and_modules_path; + + if (module_id == NULL) + { + if ((devices_and_modules_path = STRING_construct_sprintf(IOTHUB_DEVICES_PATH_FMT, STRING_c_str(iothub_host_fqdn), device_id)) == NULL) + { + LogError("Failed creating devices_and_modules_path (STRING_new failed)"); + } + } + else + { + if ((devices_and_modules_path = STRING_construct_sprintf(IOTHUB_DEVICES_MODULE_PATH_FMT, STRING_c_str(iothub_host_fqdn), device_id, module_id)) == NULL) + { + LogError("Failed creating devices_and_modules_path (STRING_new failed)"); + } + } + return devices_and_modules_path; +} + +static void on_cbs_put_token_complete_callback(void* context, CBS_OPERATION_RESULT operation_result, unsigned int status_code, const char* status_description) +{ +#ifdef NO_LOGGING + UNUSED(status_code); + UNUSED(status_description); +#endif + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_095: [`instance->is_sas_token_refresh_in_progress` and `instance->is_cbs_put_token_in_progress` shall be set to FALSE] + instance->is_cbs_put_token_in_progress = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_091: [If `result` is CBS_OPERATION_RESULT_OK `instance->state` shall be set to AUTHENTICATION_STATE_STARTED and `instance->on_state_changed_callback` invoked] + if (operation_result == CBS_OPERATION_RESULT_OK) + { + update_state(instance, AUTHENTICATION_STATE_STARTED); + } + else + { + LogError("CBS reported status code %u, error: '%s' for put-token operation for device '%s'", status_code, status_description, instance->device_id); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_092: [If `result` is not CBS_OPERATION_RESULT_OK `instance->state` shall be set to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_ERROR); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_094: [If `result` is not CBS_OPERATION_RESULT_OK and `instance->is_sas_token_refresh_in_progress` is TRUE, `instance->on_error_callback`shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_FAILED] + if (instance->is_sas_token_refresh_in_progress) + { + notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_FAILED); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_093: [If `result` is not CBS_OPERATION_RESULT_OK and `instance->is_sas_token_refresh_in_progress` is FALSE, `instance->on_error_callback`shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED] + else + { + notify_error(instance, AUTHENTICATION_ERROR_AUTH_FAILED); + } + } + + instance->is_sas_token_refresh_in_progress = false; +} + +static int put_SAS_token_to_cbs(AUTHENTICATION_INSTANCE* instance, STRING_HANDLE cbs_audience, const char* sas_token) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_043: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_057: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_075: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE] + instance->is_cbs_put_token_in_progress = true; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_046: [The SAS token provided shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_and_modules_path` as audience and passing on_cbs_put_token_complete_callback] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_058: [The SAS token shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_and_modules_path` as audience and passing on_cbs_put_token_complete_callback] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_076: [The SAS token shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_and_modules_path` as audience and passing on_cbs_put_token_complete_callback] + const char* cbs_audience_c_str = STRING_c_str(cbs_audience); + if (cbs_put_token_async(instance->cbs_handle, SAS_TOKEN_TYPE, cbs_audience_c_str, sas_token, on_cbs_put_token_complete_callback, instance) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_048: [If cbs_put_token() failed, authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to FALSE, destroy `devices_and_modules_path` and return] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_060: [If cbs_put_token() fails, `instance->is_cbs_put_token_in_progress` shall be set to FALSE] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_078: [If cbs_put_token() fails, `instance->is_cbs_put_token_in_progress` shall be set to FALSE] + instance->is_cbs_put_token_in_progress = false; + result = __FAILURE__; + LogError("Failed putting SAS token to CBS for device '%s' (cbs_put_token failed)", instance->device_id); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_047: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with current time] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_059: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with current time] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_077: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with the current time] + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed setting current_sas_token_put_time for device '%s' (get_time() failed)", instance->device_id); + } + + instance->current_sas_token_put_time = current_time; // If it failed, fear not. `current_sas_token_put_time` shall be checked for INDEFINITE_TIME wherever it is used. + + result = RESULT_OK; + } + + return result; +} + +static int create_and_put_SAS_token_to_cbs(AUTHENTICATION_INSTANCE* instance) +{ + int result; + char* sas_token; + STRING_HANDLE device_and_module_path; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_053: [A STRING_HANDLE, referred to as `devices_and_modules_path`, shall be created from: iothub_host_fqdn + "/devices/" + device_id (+ "/modules/" + module_id if a module)] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_071: [A STRING_HANDLE, referred to as `devices_and_modules_path`, shall be created from: iothub_host_fqdn + "/devices/" + device_id (+ "/modules/" + module_id if a module)] + if ((device_and_module_path = create_device_and_module_path(instance->iothub_host_fqdn, instance->device_id, instance->module_id)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_054: [If `devices_and_modules_path` failed to be created, authentication_do_work() shall fail and return] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_072: [If `devices_and_modules_path` failed to be created, authentication_do_work() shall fail and return] + result = __FAILURE__; + sas_token = NULL; + LogError("Failed creating a SAS token (create_device_and_module_path() failed)"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_07_001: [ authentication_do_work() shall determine what credential type is used SAS_TOKEN or DEVICE_KEY by calling IoTHubClient_Auth_Get_Credential_Type ] */ + IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(instance->authorization_module); + if (cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY || cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_049: [authentication_do_work() shall create a SAS token using IoTHubClient_Auth_Get_SasToken, unless it has failed previously] */ + sas_token = IoTHubClient_Auth_Get_SasToken(instance->authorization_module, STRING_c_str(device_and_module_path), instance->sas_token_lifetime_secs, NULL); + if (sas_token == NULL) + { + LogError("failure getting sas token."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (cred_type == IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_07_002: [ If credential Type is SAS_TOKEN authentication_do_work() shall validate the sas_token, and fail if it's not valid. ] */ + SAS_TOKEN_STATUS token_status = IoTHubClient_Auth_Is_SasToken_Valid(instance->authorization_module); + if (token_status == SAS_TOKEN_STATUS_INVALID) + { + LogError("sas token is invalid."); + sas_token = NULL; + result = __FAILURE__; + } + else if (token_status == SAS_TOKEN_STATUS_FAILED) + { + LogError("testing Sas Token failed."); + sas_token = NULL; + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_049: [authentication_do_work() shall create a SAS token using IoTHubClient_Auth_Get_SasToken, unless it has failed previously] */ + sas_token = IoTHubClient_Auth_Get_SasToken(instance->authorization_module, NULL, 0, NULL); + if (sas_token == NULL) + { + LogError("failure getting sas Token."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + else if (cred_type == IOTHUB_CREDENTIAL_TYPE_X509 || cred_type == IOTHUB_CREDENTIAL_TYPE_X509_ECC) + { + sas_token = NULL; + result = RESULT_OK; + } + else + { + LogError("failure unknown credential type found."); + sas_token = NULL; + result = __FAILURE__; + } + + + if (sas_token != NULL) + { + if (put_SAS_token_to_cbs(instance, device_and_module_path, sas_token) != RESULT_OK) + { + result = __FAILURE__; + LogError("Failed putting SAS token to CBS"); + } + else + { + result = RESULT_OK; + } + free(sas_token); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_081: [authentication_do_work() shall free the memory it allocated for `devices_and_modules_path`, `sasTokenKeyName` and SAS token] + STRING_delete(device_and_module_path); + } + return result; +} + +// ---------- Set/Retrieve Options Helpers ----------// +static void* authentication_clone_option(const char* name, const void* value) +{ + void* result; + + if (name == NULL) + { + LogError("Failed to clone authentication option (name is NULL)"); + result = NULL; + } + else if (value == NULL) + { + LogError("Failed to clone authentication option (value is NULL)"); + result = NULL; + } + else + { + if (strcmp(AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0 || + strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0 || + strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0 || + strcmp(AUTHENTICATION_OPTION_SAVED_OPTIONS, name) == 0) + { + result = (void*)value; + } + else + { + LogError("Failed to clone authentication option (option with name '%s' is not suppported)", name); + result = NULL; + } + } + + return result; +} + +static void authentication_destroy_option(const char* name, const void* value) +{ + if (name == NULL) + { + LogError("Failed to destroy authentication option (name is NULL)"); + } + else if (value == NULL) + { + LogError("Failed to destroy authentication option (value is NULL)"); + } + else + { + if (strcmp(name, AUTHENTICATION_OPTION_SAVED_OPTIONS) == 0) + { + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + } +} + +// Public APIs: +int authentication_start(AUTHENTICATION_HANDLE authentication_handle, const CBS_HANDLE cbs_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_025: [If authentication_handle is NULL, authentication_start() shall fail and return __FAILURE__ as error code] + if (authentication_handle == NULL) + { + result = __FAILURE__; + LogError("authentication_start failed (authentication_handle is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_026: [If `cbs_handle` is NULL, authentication_start() shall fail and return __FAILURE__ as error code] + else if (cbs_handle == NULL) + { + result = __FAILURE__; + LogError("authentication_start failed (cbs_handle is NULL)"); + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_027: [If authenticate state has been started already, authentication_start() shall fail and return __FAILURE__ as error code] + if (instance->state != AUTHENTICATION_STATE_STOPPED) + { + result = __FAILURE__; + LogError("authentication_start failed (messenger has already been started; current state: %d)", instance->state); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_028: [authentication_start() shall save `cbs_handle` on `instance->cbs_handle`] + instance->cbs_handle = cbs_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_029: [If no failures occur, `instance->state` shall be set to AUTHENTICATION_STATE_STARTING and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_STARTING); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_030: [If no failures occur, authentication_start() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int authentication_stop(AUTHENTICATION_HANDLE authentication_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_031: [If `authentication_handle` is NULL, authentication_stop() shall fail and return __FAILURE__] + if (authentication_handle == NULL) + { + result = __FAILURE__; + LogError("authentication_stop failed (authentication_handle is NULL)"); + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_032: [If `instance->state` is AUTHENTICATION_STATE_STOPPED, authentication_stop() shall fail and return __FAILURE__] + if (instance->state == AUTHENTICATION_STATE_STOPPED) + { + result = __FAILURE__; + LogError("authentication_stop failed (messenger is already stopped)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_033: [`instance->cbs_handle` shall be set to NULL] + instance->cbs_handle = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_034: [`instance->state` shall be set to AUTHENTICATION_STATE_STOPPED and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_STOPPED); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_035: [authentication_stop() shall return success code 0] + result = RESULT_OK; + } + } + + return result; +} + +void authentication_destroy(AUTHENTICATION_HANDLE authentication_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_106: [If authentication_handle is NULL, authentication_destroy() shall return] + if (authentication_handle == NULL) + { + LogError("authentication_destroy failed (authentication_handle is NULL)"); + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_107: [If `instance->state` is AUTHENTICATION_STATE_STARTING or AUTHENTICATION_STATE_STARTED, authentication_stop() shall be invoked and its result ignored] + if (instance->state != AUTHENTICATION_STATE_STOPPED) + { + (void)authentication_stop(authentication_handle); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_108: [authentication_destroy() shall destroy all resouces used by this module] + if (instance->iothub_host_fqdn != NULL) + STRING_delete(instance->iothub_host_fqdn); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_108: [authentication_destroy() shall destroy all resouces used by this module] + free(instance); + } +} + +AUTHENTICATION_HANDLE authentication_create(const AUTHENTICATION_CONFIG* config) +{ + AUTHENTICATION_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_001: [If parameter `config` is NULL, authentication_create() shall fail and return NULL.] + if (config == NULL) + { + result = NULL; + LogError("authentication_create failed (config is NULL)"); + } + else if (config->authorization_module == NULL) + { + result = NULL; + LogError("authentication_create failed (config->authorization_module is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_004: [If `config->iothub_host_fqdn` is NULL, authentication_create() shall fail and return NULL.] + else if (config->iothub_host_fqdn == NULL) + { + result = NULL; + LogError("authentication_create failed (config->iothub_host_fqdn is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_005: [If `config->on_state_changed_callback` is NULL, authentication_create() shall fail and return NULL] + else if (config->on_state_changed_callback == NULL) + { + result = NULL; + LogError("authentication_create failed (config->on_state_changed_callback is NULL)"); + } + else + { + AUTHENTICATION_INSTANCE* instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_006: [authentication_create() shall allocate memory for a new authenticate state structure AUTHENTICATION_INSTANCE.] + if ((instance = (AUTHENTICATION_INSTANCE*)malloc(sizeof(AUTHENTICATION_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_007: [If malloc() fails, authentication_create() shall fail and return NULL.] + result = NULL; + LogError("authentication_create failed (malloc failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_123: [authentication_create() shall initialize all fields of `instance` with 0 using memset().] + memset(instance, 0, sizeof(AUTHENTICATION_INSTANCE)); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_008: [authentication_create() shall save the device_id into the `instance->device_id`] + if ((instance->device_id = IoTHubClient_Auth_Get_DeviceId(config->authorization_module) ) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_009: [If STRING_construct() fails, authentication_create() shall fail and return NULL] + result = NULL; + LogError("authentication_create failed (config->device_id could not be copied; STRING_construct failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_016: [If provided, authentication_create() shall save a copy of `config->iothub_host_fqdn` into `instance->iothub_host_fqdn`] + else if ((instance->iothub_host_fqdn = STRING_construct(config->iothub_host_fqdn)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_017: [If STRING_clone() fails to copy `config->iothub_host_fqdn`, authentication_create() shall fail and return NULL] + result = NULL; + LogError("authentication_create failed (config->iothub_host_fqdn could not be copied; STRING_construct failed)"); + } + else + { + instance->state = AUTHENTICATION_STATE_STOPPED; + + instance->module_id = IoTHubClient_Auth_Get_ModuleId(config->authorization_module); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_018: [authentication_create() shall save `config->on_state_changed_callback` and `config->on_state_changed_callback_context` into `instance->on_state_changed_callback` and `instance->on_state_changed_callback_context`.] + instance->on_state_changed_callback = config->on_state_changed_callback; + instance->on_state_changed_callback_context = config->on_state_changed_callback_context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_019: [authentication_create() shall save `config->on_error_callback` and `config->on_error_callback_context` into `instance->on_error_callback` and `instance->on_error_callback_context`.] + instance->on_error_callback = config->on_error_callback; + instance->on_error_callback_context = config->on_error_callback_context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_021: [authentication_create() shall set `instance->cbs_request_timeout_secs` with the default value of UINT32_MAX] + instance->cbs_request_timeout_secs = DEFAULT_CBS_REQUEST_TIMEOUT_SECS; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_022: [authentication_create() shall set `instance->sas_token_lifetime_secs` with the default value of one hour] + instance->sas_token_lifetime_secs = DEFAULT_SAS_TOKEN_LIFETIME_SECS; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_023: [authentication_create() shall set `instance->sas_token_refresh_time_secs` with the default value of 30 minutes] + instance->sas_token_refresh_time_secs = DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS; + + instance->authorization_module = config->authorization_module; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_024: [If no failure occurs, authentication_create() shall return a reference to the AUTHENTICATION_INSTANCE handle] + result = (AUTHENTICATION_HANDLE)instance; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_020: [If any failure occurs, authentication_create() shall free any memory it allocated previously] + if (result == NULL) + { + authentication_destroy((AUTHENTICATION_HANDLE)instance); + } + } + } + + return result; +} + +void authentication_do_work(AUTHENTICATION_HANDLE authentication_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_036: [If authentication_handle is NULL, authentication_do_work() shall fail and return] + if (authentication_handle == NULL) + { + LogError("authentication_do_work failed (authentication_handle is NULL)"); + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_038: [If `instance->is_cbs_put_token_in_progress` is TRUE, authentication_do_work() shall only verify the authentication timeout] + if (instance->is_cbs_put_token_in_progress) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_084: [If no timeout has occurred, authentication_do_work() shall return] + + bool is_timed_out; + if (verify_cbs_put_token_timeout(instance, &is_timed_out) == RESULT_OK && is_timed_out) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_085: [`instance->is_cbs_put_token_in_progress` shall be set to FALSE] + instance->is_cbs_put_token_in_progress = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_086: [`instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_ERROR); + + if (instance->is_sas_token_refresh_in_progress) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_087: [If `instance->is_sas_token_refresh_in_progress` is TRUE, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT] + notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_088: [If `instance->is_sas_token_refresh_in_progress` is FALSE, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_TIMEOUT] + notify_error(instance, AUTHENTICATION_ERROR_AUTH_TIMEOUT); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_089: [`instance->is_sas_token_refresh_in_progress` shall be set to FALSE] + instance->is_sas_token_refresh_in_progress = false; + } + } + else if (instance->state == AUTHENTICATION_STATE_STARTED) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_040: [If `instance->state` is AUTHENTICATION_STATE_STARTED and user-provided SAS token was used, authentication_do_work() shall return] + if (IoTHubClient_Auth_Get_Credential_Type(instance->authorization_module) == IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_039: [If `instance->state` is AUTHENTICATION_STATE_STARTED and device keys were used, authentication_do_work() shall only verify the SAS token refresh time] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_065: [The SAS token shall be refreshed if the current time minus `instance->current_sas_token_put_time` equals or exceeds `instance->sas_token_refresh_time_secs`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_066: [If SAS token does not need to be refreshed, authentication_do_work() shall return] + bool is_timed_out; + if (verify_sas_token_refresh_timeout(instance, &is_timed_out) == RESULT_OK && is_timed_out) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_119: [authentication_do_work() shall set `instance->is_sas_token_refresh_in_progress` to TRUE] + instance->is_sas_token_refresh_in_progress = true; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_067: [authentication_do_work() shall create a SAS token using `instance->device_primary_key`, unless it has failed previously] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_069: [If using `instance->device_primary_key` has failed previously, a SAS token shall be created using `instance->device_secondary_key`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_068: [If using `instance->device_primary_key` has failed previously and `instance->device_secondary_key` is not provided, authentication_do_work() shall fail and return] + if (create_and_put_SAS_token_to_cbs(instance) != RESULT_OK) + { + LogError("Failed refreshing SAS token '%'", instance->device_id); + } + + if (!instance->is_cbs_put_token_in_progress) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_120: [If cbs_put_token() fails, `instance->is_sas_token_refresh_in_progress` shall be set to FALSE] + instance->is_sas_token_refresh_in_progress = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_079: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_ERROR); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_080: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_FAILED] + notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_FAILED); + } + } + } + } + else if (instance->state == AUTHENTICATION_STATE_STARTING) + { + if (create_and_put_SAS_token_to_cbs(instance) != RESULT_OK) + { + LogError("Failed authenticating device '%s' using device keys", instance->device_id); + } + + if (!instance->is_cbs_put_token_in_progress) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_061: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_121: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked] + update_state(instance, AUTHENTICATION_STATE_ERROR); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_062: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_122: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED] + notify_error(instance, AUTHENTICATION_ERROR_AUTH_FAILED); + } + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_037: [If `instance->state` is not AUTHENTICATION_STATE_STARTING or AUTHENTICATION_STATE_STARTED, authentication_do_work() shall fail and return] + // Nothing to be done. + } + } +} + +int authentication_set_option(AUTHENTICATION_HANDLE authentication_handle, const char* name, void* value) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_097: [If `authentication_handle` or `name` or `value` is NULL, authentication_set_option shall fail and return a non-zero value] + if (authentication_handle == NULL || name == NULL || value == NULL) + { + LogError("authentication_set_option failed (one of the followin are NULL: authentication_handle=%p, name=%p, value=%p)", + authentication_handle, name, value); + result = __FAILURE__; + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_098: [If name matches AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, `value` shall be saved on `instance->cbs_request_timeout_secs`] + if (strcmp(AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0) + { + instance->cbs_request_timeout_secs = *((size_t*)value); + result = RESULT_OK; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_124: [If name matches AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, `value` shall be saved on `instance->sas_token_refresh_time_secs`] + else if (strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0) + { + instance->sas_token_refresh_time_secs = *((size_t*)value); + result = RESULT_OK; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_125: [If name matches AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, `value` shall be saved on `instance->sas_token_lifetime_secs`] + else if (strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0) + { + instance->sas_token_lifetime_secs = *((size_t*)value); + result = RESULT_OK; + } + else if (strcmp(AUTHENTICATION_OPTION_SAVED_OPTIONS, name) == 0) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_098: [If name matches AUTHENTICATION_OPTION_SAVED_OPTIONS, `value` shall be applied using OptionHandler_FeedOptions] + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, authentication_handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_126: [If OptionHandler_FeedOptions fails, authentication_set_option shall fail and return a non-zero value] + LogError("authentication_set_option failed (OptionHandler_FeedOptions failed)"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_128: [If name does not match any supported option, authentication_set_option shall fail and return a non-zero value] + LogError("authentication_set_option failed (option with name '%s' is not suppported)", name); + result = __FAILURE__; + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_099: [If no errors occur, authentication_set_option shall return 0] + return result; +} + +OPTIONHANDLER_HANDLE authentication_retrieve_options(AUTHENTICATION_HANDLE authentication_handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_100: [If `authentication_handle` is NULL, authentication_retrieve_options shall fail and return NULL] + if (authentication_handle == NULL) + { + LogError("Failed to retrieve options from authentication instance (authentication_handle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_101: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create] + OPTIONHANDLER_HANDLE options = OptionHandler_Create(authentication_clone_option, authentication_destroy_option, (pfSetOption)authentication_set_option); + + if (options == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_102: [If an OPTIONHANDLER_HANDLE instance fails to be created, authentication_retrieve_options shall fail and return NULL] + LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed)"); + result = NULL; + } + else + { + AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_103: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_104: [If OptionHandler_AddOption fails, authentication_retrieve_options shall fail and return NULL] + if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, (void*)&instance->cbs_request_timeout_secs) != OPTIONHANDLER_OK) + { + LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS); + result = NULL; + } + else if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, (void*)&instance->sas_token_refresh_time_secs) != OPTIONHANDLER_OK) + { + LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS); + result = NULL; + } + else if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, (void*)&instance->sas_token_lifetime_secs) != OPTIONHANDLER_OK) + { + LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_127: [If no failures occur, authentication_retrieve_options shall return the OPTIONHANDLER_HANDLE instance] + result = options; + } + + if (result == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_105: [If authentication_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(options); + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_common.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2465 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <time.h> +#include <limits.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/urlencode.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/macro_utils.h" + +#include "azure_uamqp_c/cbs.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/messaging.h" + +#include "iothub_client_core_ll.h" +#include "iothub_client_options.h" +#include "internal/iothub_client_private.h" +#include "internal/iothubtransportamqp_methods.h" +#include "internal/iothub_client_retry_control.h" +#include "internal/iothubtransport_amqp_common.h" +#include "internal/iothubtransport_amqp_connection.h" +#include "internal/iothubtransport_amqp_device.h" +#include "internal/iothubtransport.h" +#include "iothub_client_version.h" + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) +#define DEFAULT_CBS_REQUEST_TIMEOUT_SECS 30 +#define DEFAULT_DEVICE_STATE_CHANGE_TIMEOUT_SECS 60 +#define DEFAULT_EVENT_SEND_TIMEOUT_SECS 300 +#define DEFAULT_SAS_TOKEN_LIFETIME_SECS 3600 +#define DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS 1800 +#define MAX_NUMBER_OF_DEVICE_FAILURES 5 +#define DEFAULT_SERVICE_KEEP_ALIVE_FREQ_SECS 240 +#define DEFAULT_REMOTE_IDLE_PING_RATIO 0.50 +#define DEFAULT_RETRY_POLICY IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER +// DEFAULT_MAX_RETRY_TIME_IN_SECS = 0 means infinite retry. +#define DEFAULT_MAX_RETRY_TIME_IN_SECS 0 +#define MAX_SERVICE_KEEP_ALIVE_RATIO 0.9 + +// ---------- Data Definitions ---------- // + +typedef enum AMQP_TRANSPORT_AUTHENTICATION_MODE_TAG +{ + AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET, + AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS, + AMQP_TRANSPORT_AUTHENTICATION_MODE_X509 +} AMQP_TRANSPORT_AUTHENTICATION_MODE; + +/* +Definition of transport states: + +AMQP_TRANSPORT_STATE_NOT_CONNECTED: Initial state when the transport is created. +AMQP_TRANSPORT_STATE_CONNECTING: First connection ever. +AMQP_TRANSPORT_STATE_CONNECTED: Transition from AMQP_TRANSPORT_STATE_CONNECTING or AMQP_TRANSPORT_STATE_RECONNECTING. +AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED: When a failure occurred and the transport identifies a reconnection is needed. +AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION: Transition from AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED after all prep is done (transient instances are destroyed, devices are stopped). +AMQP_TRANSPORT_STATE_RECONNECTING: Transition from AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION. +AMQP_TRANSPORT_STATE_NOT_CONNECTED_NO_MORE_RETRIES: State reached if the maximum number/length of reconnections has been reached. +AMQP_TRANSPORT_STATE_BEING_DESTROYED: State set if IoTHubTransport_AMQP_Common_Destroy function is invoked. +*/ + +#define AMQP_TRANSPORT_STATE_STRINGS \ + AMQP_TRANSPORT_STATE_NOT_CONNECTED, \ + AMQP_TRANSPORT_STATE_CONNECTING, \ + AMQP_TRANSPORT_STATE_CONNECTED, \ + AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED, \ + AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION, \ + AMQP_TRANSPORT_STATE_RECONNECTING, \ + AMQP_TRANSPORT_STATE_NOT_CONNECTED_NO_MORE_RETRIES, \ + AMQP_TRANSPORT_STATE_BEING_DESTROYED + +// Suppress unused function warning for AMQP_TRANSPORT_STATEstrings +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif +DEFINE_LOCAL_ENUM(AMQP_TRANSPORT_STATE, AMQP_TRANSPORT_STATE_STRINGS); +#ifdef __APPLE__ +#pragma clang diagnostic pop +#endif + +typedef struct AMQP_TRANSPORT_INSTANCE_TAG +{ + STRING_HANDLE iothub_host_fqdn; // FQDN of the IoT Hub. + XIO_HANDLE tls_io; // TSL I/O transport. + AMQP_GET_IO_TRANSPORT underlying_io_transport_provider; // Pointer to the function that creates the TLS I/O (internal use only). + AMQP_CONNECTION_HANDLE amqp_connection; // Base amqp connection with service. + AMQP_CONNECTION_STATE amqp_connection_state; // Current state of the amqp_connection. + AMQP_TRANSPORT_AUTHENTICATION_MODE preferred_authentication_mode; // Used to avoid registered devices using different authentication modes. + SINGLYLINKEDLIST_HANDLE registered_devices; // List of devices currently registered in this transport. + bool is_trace_on; // Turns logging on and off. + OPTIONHANDLER_HANDLE saved_tls_options; // Here are the options from the xio layer if any is saved. + AMQP_TRANSPORT_STATE state; // Current state of the transport. + RETRY_CONTROL_HANDLE connection_retry_control; // Controls when the re-connection attempt should occur. + size_t svc2cl_keep_alive_timeout_secs; // Service to device keep alive frequency + double cl2svc_keep_alive_send_ratio; // Client to service keep alive frequency + + char* http_proxy_hostname; + int http_proxy_port; + char* http_proxy_username; + char* http_proxy_password; + + size_t option_sas_token_lifetime_secs; // Device-specific option. + size_t option_sas_token_refresh_time_secs; // Device-specific option. + size_t option_cbs_request_timeout_secs; // Device-specific option. + size_t option_send_event_timeout_secs; // Device-specific option. + + // Auth module used to generating handle authorization + IOTHUB_AUTHORIZATION_HANDLE authorization_module; // with either SAS Token, x509 Certs, and Device SAS Token +} AMQP_TRANSPORT_INSTANCE; + +typedef struct AMQP_TRANSPORT_DEVICE_INSTANCE_TAG +{ + STRING_HANDLE device_id; // Identity of the device. + AMQP_DEVICE_HANDLE device_handle; // Logic unit that performs authentication, messaging, etc. + IOTHUB_CLIENT_CORE_LL_HANDLE iothub_client_handle; // Saved reference to the IoTHub Core LL Client. + AMQP_TRANSPORT_INSTANCE* transport_instance; // Saved reference to the transport the device is registered on. + PDLIST_ENTRY waiting_to_send; // List of events waiting to be sent to the iot hub (i.e., haven't been processed by the transport yet). + DEVICE_STATE device_state; // Current state of the device_handle instance. + size_t number_of_previous_failures; // Number of times the device has failed in sequence; this value is reset to 0 if device succeeds to authenticate, send and/or recv messages. + size_t number_of_send_event_complete_failures; // Number of times on_event_send_complete was called in row with an error. + time_t time_of_last_state_change; // Time the device_handle last changed state; used to track timeouts of device_start_async and device_stop. + unsigned int max_state_change_timeout_secs; // Maximum number of seconds allowed for device_handle to complete start and stop state changes. + // the methods portion + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE methods_handle; // Handle to instance of module that deals with device methods for AMQP. + // is subscription for methods needed? + bool subscribe_methods_needed; // Indicates if should subscribe for device methods. + // is the transport subscribed for methods? + bool subscribed_for_methods; // Indicates if device is subscribed for device methods. +} AMQP_TRANSPORT_DEVICE_INSTANCE; + +typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG +{ + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state; + char* link_name; + delivery_number message_id; +} MESSAGE_DISPOSITION_CONTEXT; + +typedef struct AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT_TAG +{ + uint32_t item_id; + IOTHUB_CLIENT_CORE_LL_HANDLE client_handle; +} AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT; + + +// ---------- General Helpers ---------- // + +static void free_proxy_data(AMQP_TRANSPORT_INSTANCE* amqp_transport_instance) +{ + if (amqp_transport_instance->http_proxy_hostname != NULL) + { + free(amqp_transport_instance->http_proxy_hostname); + amqp_transport_instance->http_proxy_hostname = NULL; + } + + if (amqp_transport_instance->http_proxy_username != NULL) + { + free(amqp_transport_instance->http_proxy_username); + amqp_transport_instance->http_proxy_username = NULL; + } + + if (amqp_transport_instance->http_proxy_password != NULL) + { + free(amqp_transport_instance->http_proxy_password); + amqp_transport_instance->http_proxy_password = NULL; + } +} + +static STRING_HANDLE get_target_iothub_fqdn(const IOTHUBTRANSPORT_CONFIG* config) +{ + STRING_HANDLE fqdn; + + if (config->upperConfig->protocolGatewayHostName == NULL) + { + if ((fqdn = STRING_construct_sprintf("%s.%s", config->upperConfig->iotHubName, config->upperConfig->iotHubSuffix)) == NULL) + { + LogError("Failed to copy iotHubName and iotHubSuffix (STRING_construct_sprintf failed)"); + } + } + else if ((fqdn = STRING_construct(config->upperConfig->protocolGatewayHostName)) == NULL) + { + LogError("Failed to copy protocolGatewayHostName (STRING_construct failed)"); + } + + return fqdn; +} + +static void update_state(AMQP_TRANSPORT_INSTANCE* transport_instance, AMQP_TRANSPORT_STATE new_state) +{ + transport_instance->state = new_state; +} + +static void reset_retry_control(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device) +{ + retry_control_reset(registered_device->transport_instance->connection_retry_control); +} + + +// ---------- Register/Unregister Helpers ---------- // + +static void internal_destroy_amqp_device_instance(AMQP_TRANSPORT_DEVICE_INSTANCE *trdev_inst) +{ + if (trdev_inst->methods_handle != NULL) + { + iothubtransportamqp_methods_destroy(trdev_inst->methods_handle); + } + + if (trdev_inst->device_handle != NULL) + { + device_destroy(trdev_inst->device_handle); + } + + if (trdev_inst->device_id != NULL) + { + STRING_delete(trdev_inst->device_id); + } + + free(trdev_inst); +} + +static DEVICE_AUTH_MODE get_authentication_mode(const IOTHUB_DEVICE_CONFIG* device) +{ + DEVICE_AUTH_MODE result; + + if (device->deviceKey != NULL || device->deviceSasToken != NULL) + { + result = DEVICE_AUTH_MODE_CBS; + } + else if (IoTHubClient_Auth_Get_Credential_Type(device->authorization_module) == IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH) + { + result = DEVICE_AUTH_MODE_CBS; + } + else + { + result = DEVICE_AUTH_MODE_X509; + } + return result; +} + +static void raise_connection_status_callback_retry_expired(const void* item, const void* action_context, bool* continue_processing) +{ + (void)action_context; + + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)item; + + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED); + + *continue_processing = true; +} + +// @brief +// Saves the new state, if it is different than the previous one. +static void on_device_state_changed_callback(void* context, DEVICE_STATE previous_state, DEVICE_STATE new_state) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_061: [If `new_state` is the same as `previous_state`, on_device_state_changed_callback shall return] + if (context != NULL && new_state != previous_state) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_062: [If `new_state` shall be saved into the `registered_device` instance] + registered_device->device_state = new_state; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_063: [If `registered_device->time_of_last_state_change` shall be set using get_time()] + registered_device->time_of_last_state_change = get_time(NULL); + + if (new_state == DEVICE_STATE_STARTED) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_127: [If `new_state` is DEVICE_STATE_STARTED, retry_control_reset() shall be invoked passing `instance->connection_retry_control`] + reset_retry_control(registered_device); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_120: [If `new_state` is DEVICE_STATE_STARTED, IoTHubClientCore_LL_ConnectionStatusCallBack shall be invoked with IOTHUB_CLIENT_CONNECTION_AUTHENTICATED and IOTHUB_CLIENT_CONNECTION_OK] + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_AUTHENTICATED, IOTHUB_CLIENT_CONNECTION_OK); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_121: [If `new_state` is DEVICE_STATE_STOPPED, IoTHubClientCore_LL_ConnectionStatusCallBack shall be invoked with IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED and IOTHUB_CLIENT_CONNECTION_OK] + else if (new_state == DEVICE_STATE_STOPPED) + { + if (registered_device->transport_instance->state == AMQP_TRANSPORT_STATE_CONNECTED || + registered_device->transport_instance->state == AMQP_TRANSPORT_STATE_BEING_DESTROYED) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_OK); + } + else if (registered_device->transport_instance->state == AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED || + registered_device->transport_instance->state == AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION || + registered_device->transport_instance->state == AMQP_TRANSPORT_STATE_RECONNECTING) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_NO_NETWORK); + } + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_122: [If `new_state` is DEVICE_STATE_ERROR_AUTH, IoTHubClientCore_LL_ConnectionStatusCallBack shall be invoked with IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED and IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL] + else if (new_state == DEVICE_STATE_ERROR_AUTH) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_123: [If `new_state` is DEVICE_STATE_ERROR_AUTH_TIMEOUT or DEVICE_STATE_ERROR_MSG, IoTHubClientCore_LL_ConnectionStatusCallBack shall be invoked with IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED and IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR] + else if (new_state == DEVICE_STATE_ERROR_AUTH_TIMEOUT || new_state == DEVICE_STATE_ERROR_MSG) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(registered_device->iothub_client_handle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR); + } + } +} + +// @brief Auxiliary function to be used to find a device in the registered_devices list. +// @returns true if the device ids match, false otherwise. +static bool find_device_by_id_callback(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + bool result; + + if (match_context == NULL) + { + result = false; + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item); + + if (device_instance == NULL || + device_instance->device_id == NULL || + STRING_c_str(device_instance->device_id) != match_context) + { + result = false; + } + else + { + result = true; + } + } + + return result; +} + +// @brief Verifies if a device is already registered within the transport that owns the list of registered devices. +// @remarks Returns the correspoding LIST_ITEM_HANDLE in registered_devices, if found. +// @returns true if the device is already in the list, false otherwise. +static bool is_device_registered_ex(SINGLYLINKEDLIST_HANDLE registered_devices, const char* device_id, LIST_ITEM_HANDLE *list_item) +{ + return ((*list_item = singlylinkedlist_find(registered_devices, find_device_by_id_callback, device_id)) != NULL ? 1 : 0); +} + +// @brief Verifies if a device is already registered within the transport that owns the list of registered devices. +// @returns true if the device is already in the list, false otherwise. +static bool is_device_registered(AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance) +{ + LIST_ITEM_HANDLE list_item; + const char* device_id = STRING_c_str(amqp_device_instance->device_id); + return is_device_registered_ex(amqp_device_instance->transport_instance->registered_devices, device_id, &list_item); +} + +static size_t get_number_of_registered_devices(AMQP_TRANSPORT_INSTANCE* transport) +{ + size_t result = 0; + + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(transport->registered_devices); + + while (list_item != NULL) + { + result++; + list_item = singlylinkedlist_get_next_item(list_item); + } + + return result; +} + + +// ---------- Callbacks ---------- // + +static MESSAGE_CALLBACK_INFO* MESSAGE_CALLBACK_INFO_Create(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, AMQP_TRANSPORT_DEVICE_INSTANCE* device_state) +{ + MESSAGE_CALLBACK_INFO* result; + + if ((result = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO))) == NULL) + { + LogError("Failed creating MESSAGE_CALLBACK_INFO (malloc failed)"); + } + else + { + memset(result, 0, sizeof(MESSAGE_CALLBACK_INFO)); + MESSAGE_DISPOSITION_CONTEXT* tc; + + if ((tc = (MESSAGE_DISPOSITION_CONTEXT*)malloc(sizeof(MESSAGE_DISPOSITION_CONTEXT))) == NULL) + { + LogError("Failed creating MESSAGE_DISPOSITION_CONTEXT (malloc failed)"); + free(result); + result = NULL; + } + else + { + memset(tc, 0, sizeof(MESSAGE_DISPOSITION_CONTEXT)); + if (mallocAndStrcpy_s(&(tc->link_name), disposition_info->source) == 0) + { + tc->device_state = device_state; + tc->message_id = (delivery_number)disposition_info->message_id; + + result->messageHandle = message; + result->transportContext = tc; + } + else + { + LogError("Failed creating MESSAGE_CALLBACK_INFO (mallocAndStrcyp_s failed)"); + free(tc); + free(result); + result = NULL; + } + } + } + + return result; +} + +static void MESSAGE_CALLBACK_INFO_Destroy(MESSAGE_CALLBACK_INFO* message_callback_info) +{ + if (message_callback_info->transportContext != NULL) + { + free(message_callback_info->transportContext->link_name); + free(message_callback_info->transportContext); + } + free(message_callback_info); +} + +static DEVICE_MESSAGE_DISPOSITION_RESULT get_device_disposition_result_from(IOTHUBMESSAGE_DISPOSITION_RESULT iothubclient_disposition_result) +{ + DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result; + + if (iothubclient_disposition_result == IOTHUBMESSAGE_ACCEPTED) + { + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED; + } + else if (iothubclient_disposition_result == IOTHUBMESSAGE_ABANDONED) + { + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED; + } + else if (iothubclient_disposition_result == IOTHUBMESSAGE_REJECTED) + { + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED; + } + else + { + LogError("Failed getting corresponding DEVICE_MESSAGE_DISPOSITION_RESULT for IOTHUBMESSAGE_DISPOSITION_RESULT (%d is not supported)", iothubclient_disposition_result); + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED; + } + + return device_disposition_result; +} + +static DEVICE_MESSAGE_DISPOSITION_RESULT on_message_received(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, void* context) +{ + AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result; + MESSAGE_CALLBACK_INFO* message_data; + + if ((message_data = MESSAGE_CALLBACK_INFO_Create(message, disposition_info, amqp_device_instance)) == NULL) + { + LogError("Failed processing message received (failed to assemble callback info)"); + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_089: [IoTHubClientCore_LL_MessageCallback() shall be invoked passing the client and the incoming message handles as parameters] + if (IoTHubClientCore_LL_MessageCallback(amqp_device_instance->iothub_client_handle, message_data) != true) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_090: [If IoTHubClientCore_LL_MessageCallback() fails, on_message_received_callback shall return DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED] + LogError("Failed processing message received (IoTHubClientCore_LL_MessageCallback failed)"); + IoTHubMessage_Destroy(message); + MESSAGE_CALLBACK_INFO_Destroy(message_data); + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_091: [If IoTHubClientCore_LL_MessageCallback() succeeds, on_message_received_callback shall return DEVICE_MESSAGE_DISPOSITION_RESULT_NONE] + device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_NONE; + } + } + + return device_disposition_result; +} + +static void on_methods_error(void* context) +{ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_030: [ `on_methods_error` shall do nothing. ]*/ + (void)context; +} + +static void on_methods_unsubscribed(void* context) +{ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_12_001: [ `on_methods_unsubscribed` calls iothubtransportamqp_methods_unsubscribe. ]*/ + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + + iothubtransportamqp_methods_unsubscribe(device_state->methods_handle); + device_state->subscribed_for_methods = false; +} + +static int on_method_request_received(void* context, const char* method_name, const unsigned char* request, size_t request_size, IOTHUBTRANSPORT_AMQP_METHOD_HANDLE method_handle) +{ + int result; + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_017: [ `on_methods_request_received` shall call the `IoTHubClientCore_LL_DeviceMethodComplete` passing the method name, request buffer and size and the newly created BUFFER handle. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_022: [ The status code shall be the return value of the call to `IoTHubClientCore_LL_DeviceMethodComplete`. ]*/ + if (IoTHubClientCore_LL_DeviceMethodComplete(device_state->iothub_client_handle, method_name, request, request_size, (void*)method_handle) != 0) + { + LogError("Failure: IoTHubClientCore_LL_DeviceMethodComplete"); + result = __FAILURE__; + } + else + { + result = 0; + } + return result; +} + +static int subscribe_methods(AMQP_TRANSPORT_DEVICE_INSTANCE* deviceState) +{ + int result; + + if (deviceState->subscribed_for_methods) + { + result = 0; + } + else + { + SESSION_HANDLE session_handle; + + if ((amqp_connection_get_session_handle(deviceState->transport_instance->amqp_connection, &session_handle)) != RESULT_OK) + { + LogError("Device '%s' failed subscribing for methods (failed getting session handle)", STRING_c_str(deviceState->device_id)); + result = __FAILURE__; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_024: [ If the device authentication status is AUTHENTICATION_STATUS_OK and `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` was called to register for methods, `IoTHubTransport_AMQP_Common_DoWork` shall call `iothubtransportamqp_methods_subscribe`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_027: [ The current session handle shall be passed to `iothubtransportamqp_methods_subscribe`. ]*/ + else if (iothubtransportamqp_methods_subscribe(deviceState->methods_handle, session_handle, on_methods_error, deviceState, on_method_request_received, deviceState, on_methods_unsubscribed, deviceState) != 0) + { + LogError("Cannot subscribe for methods"); + result = __FAILURE__; + } + else + { + deviceState->subscribed_for_methods = true; + result = 0; + } + } + + return result; +} + +static void on_device_send_twin_update_complete_callback(DEVICE_TWIN_UPDATE_RESULT result, int status_code, void* context) +{ + (void)result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_151: [If `context` is NULL, the callback shall return] + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else + { + AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT* dev_twin_ctx = (AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_152: [`IoTHubClientCore_LL_ReportedStateComplete` shall be invoked passing `status_code` and `context` details] + IoTHubClientCore_LL_ReportedStateComplete(dev_twin_ctx->client_handle, dev_twin_ctx->item_id, status_code); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_153: [The memory allocated for `context` shall be released] + free(dev_twin_ctx); + } +} + +static void on_device_twin_update_received_callback(DEVICE_TWIN_UPDATE_TYPE update_type, const unsigned char* message, size_t length, void* context) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_137: [If `context` is NULL, the callback shall return.] + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_138: [If `update_type` is DEVICE_TWIN_UPDATE_TYPE_PARTIAL IoTHubClientCore_LL_RetrievePropertyComplete shall be invoked passing `context` as handle, `DEVICE_TWIN_UPDATE_PARTIAL`, `payload` and `size`.] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_139: [If `update_type` is DEVICE_TWIN_UPDATE_TYPE_COMPLETE IoTHubClientCore_LL_RetrievePropertyComplete shall be invoked passing `context` as handle, `DEVICE_TWIN_UPDATE_COMPLETE`, `payload` and `size`.] + IoTHubClientCore_LL_RetrievePropertyComplete( + registered_device->iothub_client_handle, + (update_type == DEVICE_TWIN_UPDATE_TYPE_COMPLETE ? DEVICE_TWIN_UPDATE_COMPLETE : DEVICE_TWIN_UPDATE_PARTIAL), + message, length); + } +} + + +// ---------- Underlying TLS I/O Helpers ---------- // + +// @brief +// Retrieves the options of the current underlying TLS I/O instance and saves in the transport instance. +// @remarks +// This is used when the new underlying I/O transport (TLS I/O, or WebSockets, etc) needs to be recreated, +// and the options previously set must persist. +// +// If no TLS I/O instance was created yet, results in failure. +// @returns +// 0 if succeeds, non-zero otherwise. +static int save_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance) +{ + int result; + + if (transport_instance->tls_io == NULL) + { + LogError("failed saving underlying I/O transport options (tls_io instance is NULL)"); + result = __FAILURE__; + } + else + { + OPTIONHANDLER_HANDLE fresh_options; + + if ((fresh_options = xio_retrieveoptions(transport_instance->tls_io)) == NULL) + { + LogError("failed saving underlying I/O transport options (tls_io instance is NULL)"); + result = __FAILURE__; + } + else + { + OPTIONHANDLER_HANDLE previous_options = transport_instance->saved_tls_options; + transport_instance->saved_tls_options = fresh_options; + + if (previous_options != NULL) + { + OptionHandler_Destroy(previous_options); + } + + result = RESULT_OK; + } + } + + return result; +} + +static void destroy_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance) +{ + if (transport_instance->saved_tls_options != NULL) + { + OptionHandler_Destroy(transport_instance->saved_tls_options); + transport_instance->saved_tls_options = NULL; + } +} + +// @brief +// Applies TLS I/O options if previously saved to a new TLS I/O instance. +// @returns +// 0 if succeeds, non-zero otherwise. +static int restore_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance, XIO_HANDLE xio_handle) +{ + int result; + + if (transport_instance->saved_tls_options == NULL) + { + result = RESULT_OK; + } + else + { + if (OptionHandler_FeedOptions(transport_instance->saved_tls_options, xio_handle) != OPTIONHANDLER_OK) + { + LogError("Failed feeding existing options to new TLS instance."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + + return result; +} + +// @brief Destroys the XIO_HANDLE obtained with underlying_io_transport_provider(), saving its options beforehand. +static void destroy_underlying_io_transport(AMQP_TRANSPORT_INSTANCE* transport_instance) +{ + if (transport_instance->tls_io != NULL) + { + xio_destroy(transport_instance->tls_io); + transport_instance->tls_io = NULL; + } +} + +// @brief Invokes underlying_io_transport_provider() and retrieves a new XIO_HANDLE to use for I/O (TLS, or websockets, or w/e is supported). +// @param xio_handle: if successfull, set with the new XIO_HANDLE acquired; not changed otherwise. +// @returns 0 if successfull, non-zero otherwise. +static int get_new_underlying_io_transport(AMQP_TRANSPORT_INSTANCE* transport_instance, XIO_HANDLE *xio_handle) +{ + int result; + AMQP_TRANSPORT_PROXY_OPTIONS amqp_transport_proxy_options; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_041: [ If the `proxy_data` option has been set, the proxy options shall be filled in the argument `amqp_transport_proxy_options` when calling the function `underlying_io_transport_provider()` to obtain the underlying IO handle. ]*/ + amqp_transport_proxy_options.host_address = transport_instance->http_proxy_hostname; + amqp_transport_proxy_options.port = transport_instance->http_proxy_port; + amqp_transport_proxy_options.username = transport_instance->http_proxy_username; + amqp_transport_proxy_options.password = transport_instance->http_proxy_password; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_042: [ If no `proxy_data` option has been set, NULL shall be passed as the argument `amqp_transport_proxy_options` when calling the function `underlying_io_transport_provider()`. ]*/ + if ((*xio_handle = transport_instance->underlying_io_transport_provider(STRING_c_str(transport_instance->iothub_host_fqdn), amqp_transport_proxy_options.host_address == NULL ? NULL : &amqp_transport_proxy_options)) == NULL) + { + LogError("Failed to obtain a TLS I/O transport layer (underlying_io_transport_provider() failed)"); + result = __FAILURE__; + } + else + { + // If this is the HSM x509 ECC certificate + if (transport_instance->authorization_module != NULL && IoTHubClient_Auth_Get_Credential_Type(transport_instance->authorization_module) == IOTHUB_CREDENTIAL_TYPE_X509_ECC) + { + // Set the xio_handle + if (IoTHubClient_Auth_Set_xio_Certificate(transport_instance->authorization_module, *xio_handle) != 0) + { + LogError("Unable to create the lower level TLS layer."); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + + if (restore_underlying_io_transport_options(transport_instance, *xio_handle) != RESULT_OK) + { + /*pessimistically hope TLS will fail, be recreated and options re-given*/ + LogError("Failed to apply options previous saved to new underlying I/O transport instance."); + } + + result = RESULT_OK; + } + + return result; +} + + +// ---------- AMQP connection establishment/tear-down, connectry retry ---------- // + +static void on_amqp_connection_state_changed(const void* context, AMQP_CONNECTION_STATE previous_state, AMQP_CONNECTION_STATE new_state) +{ + if (context != NULL && new_state != previous_state) + { + AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_059: [`new_state` shall be saved in to the transport instance] + transport_instance->amqp_connection_state = new_state; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_060: [If `new_state` is AMQP_CONNECTION_STATE_ERROR, the connection shall be flagged as faulty (so the connection retry logic can be triggered)] + if (new_state == AMQP_CONNECTION_STATE_ERROR) + { + LogError("Transport received an ERROR from the amqp_connection (state changed %s -> %s); it will be flagged for connection retry.", ENUM_TO_STRING(AMQP_CONNECTION_STATE, previous_state), ENUM_TO_STRING(AMQP_CONNECTION_STATE, new_state)); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED); + } + else if (new_state == AMQP_CONNECTION_STATE_OPENED) + { + update_state(transport_instance, AMQP_TRANSPORT_STATE_CONNECTED); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_115: [If the AMQP connection is closed by the service side, the connection retry logic shall be triggered] + else if (new_state == AMQP_CONNECTION_STATE_CLOSED && previous_state == AMQP_CONNECTION_STATE_OPENED && transport_instance->state != AMQP_TRANSPORT_STATE_BEING_DESTROYED) + { + LogError("amqp_connection was closed unexpectedly; connection retry will be triggered."); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED); + } + } +} + +static int establish_amqp_connection(AMQP_TRANSPORT_INSTANCE* transport_instance) +{ + int result; + + if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET) + { + LogError("Failed establishing connection (transport doesn't have a preferred authentication mode set; unexpected!)."); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_023: [If `instance->tls_io` is NULL, it shall be set invoking instance->underlying_io_transport_provider()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_025: [When `instance->tls_io` is created, it shall be set with `instance->saved_tls_options` using OptionHandler_FeedOptions()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_111: [If OptionHandler_FeedOptions() fails, it shall be ignored] + else if (transport_instance->tls_io == NULL && + get_new_underlying_io_transport(transport_instance, &transport_instance->tls_io) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_024: [If instance->underlying_io_transport_provider() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return] + LogError("Failed establishing connection (failed to obtain a TLS I/O transport layer)."); + result = __FAILURE__; + } + else + { + AMQP_CONNECTION_CONFIG amqp_connection_config; + amqp_connection_config.iothub_host_fqdn = STRING_c_str(transport_instance->iothub_host_fqdn); + amqp_connection_config.underlying_io_transport = transport_instance->tls_io; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_029: [`instance->is_trace_on` shall be set into `AMQP_CONNECTION_CONFIG->is_trace_on`] + amqp_connection_config.is_trace_on = transport_instance->is_trace_on; + amqp_connection_config.on_state_changed_callback = on_amqp_connection_state_changed; + amqp_connection_config.on_state_changed_context = transport_instance; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_12_003: [AMQP connection will be configured using the `svc2cl_keep_alive_timeout_secs` value from SetOption ] + amqp_connection_config.svc2cl_keep_alive_timeout_secs = transport_instance->svc2cl_keep_alive_timeout_secs; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_99_001: [AMQP connection will be configured using the `remote_idle_timeout_ratio` value from SetOption ] + amqp_connection_config.cl2svc_keep_alive_send_ratio = transport_instance->cl2svc_keep_alive_send_ratio; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_027: [If `transport->preferred_authentication_method` is CBS, AMQP_CONNECTION_CONFIG shall be set with `create_sasl_io` = true and `create_cbs_connection` = true] + if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS) + { + amqp_connection_config.create_sasl_io = true; + amqp_connection_config.create_cbs_connection = true; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_028: [If `transport->preferred_credential_method` is X509, AMQP_CONNECTION_CONFIG shall be set with `create_sasl_io` = false and `create_cbs_connection` = false] + else if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_X509) + { + amqp_connection_config.create_sasl_io = false; + amqp_connection_config.create_cbs_connection = false; + } + // If new AMQP_TRANSPORT_AUTHENTICATION_MODE values are added, they need to be covered here. + + transport_instance->amqp_connection_state = AMQP_CONNECTION_STATE_CLOSED; + + if (transport_instance->state == AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION) + { + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTING); + } + else + { + update_state(transport_instance, AMQP_TRANSPORT_STATE_CONNECTING); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_026: [If `transport->connection` is NULL, it shall be created using amqp_connection_create()] + if ((transport_instance->amqp_connection = amqp_connection_create(&amqp_connection_config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_030: [If amqp_connection_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return] + LogError("Failed establishing connection (failed to create the amqp_connection instance)."); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_110: [If amqp_connection_create() succeeds, IoTHubTransport_AMQP_Common_DoWork shall proceed to invoke amqp_connection_do_work] + result = RESULT_OK; + } + } + + return result; +} + +static void prepare_device_for_connection_retry(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_032: [ Each `instance->registered_devices` shall unsubscribe from receiving C2D method requests by calling `iothubtransportamqp_methods_unsubscribe`] + iothubtransportamqp_methods_unsubscribe(registered_device->methods_handle); + registered_device->subscribed_for_methods = 0; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_031: [device_stop() shall be invoked on all `instance->registered_devices` that are not already stopped] + if (registered_device->device_state != DEVICE_STATE_STOPPED) + { + if (device_stop(registered_device->device_handle) != RESULT_OK) + { + LogError("Failed preparing device '%s' for connection retry (device_stop failed)", STRING_c_str(registered_device->device_id)); + } + } + + registered_device->number_of_previous_failures = 0; + registered_device->number_of_send_event_complete_failures = 0; +} + +static void prepare_for_connection_retry(AMQP_TRANSPORT_INSTANCE* transport_instance) +{ + LogInfo("Preparing transport for re-connection"); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_034: [`instance->tls_io` options shall be saved on `instance->saved_tls_options` using xio_retrieveoptions()] + if (save_underlying_io_transport_options(transport_instance) != RESULT_OK) + { + LogError("Failed saving TLS I/O options while preparing for connection retry; failure will be ignored"); + } + + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(transport_instance->registered_devices); + + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item); + + if (registered_device == NULL) + { + LogError("Failed preparing device for connection retry (singlylinkedlist_item_get_value failed)"); + } + else + { + prepare_device_for_connection_retry(registered_device); + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_033: [`instance->connection` shall be destroyed using amqp_connection_destroy()] + amqp_connection_destroy(transport_instance->amqp_connection); + transport_instance->amqp_connection = NULL; + transport_instance->amqp_connection_state = AMQP_CONNECTION_STATE_CLOSED; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_035: [`instance->tls_io` shall be destroyed using xio_destroy()] + destroy_underlying_io_transport(transport_instance); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_READY_FOR_RECONNECTION); +} + + +// @brief Verifies if the crendentials used by the device match the requirements and authentication mode currently supported by the transport. +// @returns true if credentials are good, false otherwise. +static bool is_device_credential_acceptable(const IOTHUB_DEVICE_CONFIG* device_config, AMQP_TRANSPORT_AUTHENTICATION_MODE preferred_authentication_mode) +{ + bool result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_003: [IoTHubTransport_AMQP_Common_Register shall return NULL if both deviceKey and deviceSasToken are not NULL.] + if ((device_config->deviceSasToken != NULL) && (device_config->deviceKey != NULL)) + { + LogError("Credential of device '%s' is not acceptable (must provide EITHER deviceSasToken OR deviceKey)", device_config->deviceId); + result = false; + } + else if (preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET) + { + result = true; + } + else if (preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_X509 && (device_config->deviceKey != NULL || device_config->deviceSasToken != NULL)) + { + LogError("Credential of device '%s' is not acceptable (transport is using X509 certificate authentication, but device config contains deviceKey or sasToken)", device_config->deviceId); + result = false; + } + else if (preferred_authentication_mode != AMQP_TRANSPORT_AUTHENTICATION_MODE_X509 && (device_config->deviceKey == NULL && device_config->deviceSasToken == NULL)) + { + LogError("Credential of device '%s' is not acceptable (transport is using CBS authentication, but device config does not contain deviceKey nor sasToken)", device_config->deviceId); + result = false; + } + else + { + result = true; + } + + return result; +} + + + +//---------- DoWork Helpers ----------// + +static IOTHUB_MESSAGE_LIST* get_next_event_to_send(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device) +{ + IOTHUB_MESSAGE_LIST* message; + + if (!DList_IsListEmpty(registered_device->waiting_to_send)) + { + PDLIST_ENTRY list_entry = registered_device->waiting_to_send->Flink; + message = containingRecord(list_entry, IOTHUB_MESSAGE_LIST, entry); + (void)DList_RemoveEntryList(list_entry); + } + else + { + message = NULL; + } + + return message; +} + +// @brief "Parses" the D2C_EVENT_SEND_RESULT (from iothubtransport_amqp_device module) into a IOTHUB_CLIENT_CONFIRMATION_RESULT. +static IOTHUB_CLIENT_CONFIRMATION_RESULT get_iothub_client_confirmation_result_from(D2C_EVENT_SEND_RESULT result) +{ + IOTHUB_CLIENT_CONFIRMATION_RESULT iothub_send_result; + + switch (result) + { + case D2C_EVENT_SEND_COMPLETE_RESULT_OK: + iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_OK; + break; + case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE: + case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING: + iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_ERROR; + break; + case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT: + iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT; + break; + case D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED: + iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY; + break; + case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN: + default: + iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_ERROR; + break; + } + + return iothub_send_result; +} + +// @brief +// Callback function for device_send_event_async. +static void on_event_send_complete(IOTHUB_MESSAGE_LIST* message, D2C_EVENT_SEND_RESULT result, void* context) +{ + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context; + + if (result != D2C_EVENT_SEND_COMPLETE_RESULT_OK && result != D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED) + { + registered_device->number_of_send_event_complete_failures++; + } + else + { + registered_device->number_of_send_event_complete_failures = 0; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_056: [If `message->callback` is not NULL, it shall invoked with the `iothub_send_result`] + if (message->callback != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_050: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_OK, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_OK] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_051: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_052: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_053: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_054: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_055: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR] + IOTHUB_CLIENT_CONFIRMATION_RESULT iothub_send_result = get_iothub_client_confirmation_result_from(result); + + message->callback(iothub_send_result, message->context); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_057: [`message->messageHandle` shall be destroyed using IoTHubMessage_Destroy] + IoTHubMessage_Destroy(message->messageHandle); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_058: [`message` shall be destroyed using free] + free(message); +} + +// @brief +// Gets events from wait to send list and sends to service in the order they were added. +// @returns +// 0 if all events could be sent to the next layer successfully, non-zero otherwise. +static int send_pending_events(AMQP_TRANSPORT_DEVICE_INSTANCE* device_state) +{ + int result; + IOTHUB_MESSAGE_LIST* message; + + result = RESULT_OK; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_047: [If the registered device is started, each event on `registered_device->wait_to_send_list` shall be removed from the list and sent using device_send_event_async()] + while ((message = get_next_event_to_send(device_state)) != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_048: [device_send_event_async() shall be invoked passing `on_event_send_complete`] + if (device_send_event_async(device_state->device_handle, message, on_event_send_complete, device_state) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_049: [If device_send_event_async() fails, `on_event_send_complete` shall be invoked passing EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING and return] + LogError("Device '%s' failed to send message (device_send_event_async failed)", STRING_c_str(device_state->device_id)); + result = __FAILURE__; + + on_event_send_complete(message, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, device_state); + break; + } + } + + return result; +} + +// @brief +// Auxiliary function for the public DoWork API, performing DoWork activities (authenticate, messaging) for a specific device. +// @requires +// The transport to have a valid instance of AMQP_CONNECTION (from which to obtain SESSION_HANDLE and CBS_HANDLE) +// @returns +// 0 if no errors occur, non-zero otherwise. +static int IoTHubTransport_AMQP_Common_Device_DoWork(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device) +{ + int result; + + if (registered_device->device_state != DEVICE_STATE_STARTED) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_036: [If the device state is DEVICE_STATE_STOPPED, it shall be started] + if (registered_device->device_state == DEVICE_STATE_STOPPED) + { + SESSION_HANDLE session_handle; + CBS_HANDLE cbs_handle = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_039: [amqp_connection_get_session_handle() shall be invoked on `instance->connection`] + if (amqp_connection_get_session_handle(registered_device->transport_instance->amqp_connection, &session_handle) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_040: [If amqp_connection_get_session_handle() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return] + LogError("Failed performing DoWork for device '%s' (failed to get the amqp_connection session_handle)", STRING_c_str(registered_device->device_id)); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_037: [If transport is using CBS authentication, amqp_connection_get_cbs_handle() shall be invoked on `instance->connection`] + else if (registered_device->transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS && + amqp_connection_get_cbs_handle(registered_device->transport_instance->amqp_connection, &cbs_handle) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_038: [If amqp_connection_get_cbs_handle() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return] + LogError("Failed performing DoWork for device '%s' (failed to get the amqp_connection cbs_handle)", STRING_c_str(registered_device->device_id)); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_041: [The device handle shall be started using device_start_async()] + else if (device_start_async(registered_device->device_handle, session_handle, cbs_handle) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_042: [If device_start_async() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and skip to the next registered device] + LogError("Failed performing DoWork for device '%s' (failed to start device)", STRING_c_str(registered_device->device_id)); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (registered_device->device_state == DEVICE_STATE_STARTING || + registered_device->device_state == DEVICE_STATE_STOPPING) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_043: [If the device handle is in state DEVICE_STATE_STARTING or DEVICE_STATE_STOPPING, it shall be checked for state change timeout] + bool is_timed_out; + if (is_timeout_reached(registered_device->time_of_last_state_change, registered_device->max_state_change_timeout_secs, &is_timed_out) != RESULT_OK) + { + LogError("Failed performing DoWork for device '%s' (failed tracking timeout of device %d state)", STRING_c_str(registered_device->device_id), registered_device->device_state); + registered_device->device_state = DEVICE_STATE_ERROR_AUTH; // if time could not be calculated, the worst must be assumed. + result = __FAILURE__; + } + else if (is_timed_out) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_044: [If the device times out in state DEVICE_STATE_STARTING or DEVICE_STATE_STOPPING, the registered device shall be marked with failure] + LogError("Failed performing DoWork for device '%s' (device failed to start or stop within expected timeout)", STRING_c_str(registered_device->device_id)); + registered_device->device_state = DEVICE_STATE_ERROR_AUTH; // this will cause device to be stopped bellow on the next call to this function. + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else // i.e., DEVICE_STATE_ERROR_AUTH || DEVICE_STATE_ERROR_AUTH_TIMEOUT || DEVICE_STATE_ERROR_MSG + { + LogError("Failed performing DoWork for device '%s' (device reported state %d; number of previous failures: %d)", + STRING_c_str(registered_device->device_id), registered_device->device_state, registered_device->number_of_previous_failures); + + registered_device->number_of_previous_failures++; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_046: [If the device has failed for MAX_NUMBER_OF_DEVICE_FAILURES in a row, it shall trigger a connection retry on the transport] + if (registered_device->number_of_previous_failures >= MAX_NUMBER_OF_DEVICE_FAILURES) + { + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_045: [If the registered device has a failure, it shall be stopped using device_stop()] + else if (device_stop(registered_device->device_handle) != RESULT_OK) + { + LogError("Failed to stop reset device '%s' (device_stop failed)", STRING_c_str(registered_device->device_id)); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_031: [ Once the device is authenticated, `iothubtransportamqp_methods_subscribe` shall be invoked (subsequent DoWork calls shall not call it if already subscribed). ] + else if (registered_device->subscribe_methods_needed && + !registered_device->subscribed_for_methods && + subscribe_methods(registered_device) != RESULT_OK) + { + LogError("Failed performing DoWork for device '%s' (failed registering for device methods)", STRING_c_str(registered_device->device_id)); + registered_device->number_of_previous_failures++; + result = __FAILURE__; + } + else + { + if (send_pending_events(registered_device) != RESULT_OK) + { + LogError("Failed performing DoWork for device '%s' (failed sending pending events)", STRING_c_str(registered_device->device_id)); + registered_device->number_of_previous_failures++; + result = __FAILURE__; + } + else + { + registered_device->number_of_previous_failures = 0; + result = RESULT_OK; + } + } + + // No harm in invoking this as API will simply exit if the state is not "started". + device_do_work(registered_device->device_handle); + + return result; +} + + +//---------- SetOption-ish Helpers ----------// + +// @brief +// Gets all the device-specific options and replicates them into this new registered device. +// @returns +// 0 if the function succeeds, non-zero otherwise. +static int replicate_device_options_to(AMQP_TRANSPORT_DEVICE_INSTANCE* dev_instance, DEVICE_AUTH_MODE auth_mode) +{ + int result; + + if (device_set_option( + dev_instance->device_handle, + DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS, + &dev_instance->transport_instance->option_send_event_timeout_secs) != RESULT_OK) + { + LogError("Failed to apply option DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id)); + result = __FAILURE__; + } + else if (auth_mode == DEVICE_AUTH_MODE_CBS) + { + if (device_set_option( + dev_instance->device_handle, + DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS, + &dev_instance->transport_instance->option_cbs_request_timeout_secs) != RESULT_OK) + { + LogError("Failed to apply option DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id)); + result = __FAILURE__; + } + else if (device_set_option( + dev_instance->device_handle, + DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS, + &dev_instance->transport_instance->option_sas_token_lifetime_secs) != RESULT_OK) + { + LogError("Failed to apply option DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id)); + result = __FAILURE__; + } + else if (device_set_option( + dev_instance->device_handle, + DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, + &dev_instance->transport_instance->option_sas_token_refresh_time_secs) != RESULT_OK) + { + LogError("Failed to apply option DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id)); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + result = RESULT_OK; + } + + return result; +} + +// @brief +// Translates from the option names supported by iothubtransport_amqp_common to the ones supported by iothubtransport_amqp_device. +static const char* get_device_option_name_from(const char* iothubclient_option_name) +{ + const char* device_option_name; + + if (strcmp(OPTION_SAS_TOKEN_LIFETIME, iothubclient_option_name) == 0) + { + device_option_name = DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS; + } + else if (strcmp(OPTION_SAS_TOKEN_REFRESH_TIME, iothubclient_option_name) == 0) + { + device_option_name = DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS; + } + else if (strcmp(OPTION_CBS_REQUEST_TIMEOUT, iothubclient_option_name) == 0) + { + device_option_name = DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS; + } + else if (strcmp(OPTION_EVENT_SEND_TIMEOUT_SECS, iothubclient_option_name) == 0) + { + device_option_name = DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS; + } + else + { + device_option_name = NULL; + } + + return device_option_name; +} + +// @brief +// Auxiliary function invoked by IoTHubTransport_AMQP_Common_SetOption to set an option on every registered device. +// @returns +// 0 if it succeeds, non-zero otherwise. +static int IoTHubTransport_AMQP_Common_Device_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, void* value) +{ + int result; + const char* device_option; + + if ((device_option = get_device_option_name_from(option)) == NULL) + { + LogError("failed setting option '%s' to registered device (could not match name to options supported by device)", option); + result = __FAILURE__; + } + else + { + AMQP_TRANSPORT_INSTANCE* instance = (AMQP_TRANSPORT_INSTANCE*)handle; + result = RESULT_OK; + + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->registered_devices); + + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device; + + if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL) + { + LogError("failed setting option '%s' to registered device (singlylinkedlist_item_get_value failed)", option); + result = __FAILURE__; + break; + } + else if (device_set_option(registered_device->device_handle, device_option, value) != RESULT_OK) + { + LogError("failed setting option '%s' to registered device '%s' (device_set_option failed)", + option, STRING_c_str(registered_device->device_id)); + result = __FAILURE__; + break; + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + } + + return result; +} + +static void internal_destroy_instance(AMQP_TRANSPORT_INSTANCE* instance) +{ + if (instance != NULL) + { + update_state(instance, AMQP_TRANSPORT_STATE_BEING_DESTROYED); + + if (instance->registered_devices != NULL) + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->registered_devices); + + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item); + list_item = singlylinkedlist_get_next_item(list_item); + IoTHubTransport_AMQP_Common_Unregister(registered_device); + } + + singlylinkedlist_destroy(instance->registered_devices); + } + + if (instance->amqp_connection != NULL) + { + amqp_connection_destroy(instance->amqp_connection); + } + + destroy_underlying_io_transport(instance); + destroy_underlying_io_transport_options(instance); + retry_control_destroy(instance->connection_retry_control); + + STRING_delete(instance->iothub_host_fqdn); + + /* SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_043: [ `IoTHubTransport_AMQP_Common_Destroy` shall free the stored proxy options. ]*/ + free_proxy_data(instance); + + free(instance); + } +} + +// ---------- SendMessageDisposition helpers ---------- // + +static DEVICE_MESSAGE_DISPOSITION_INFO* create_device_message_disposition_info_from(MESSAGE_CALLBACK_INFO* message_data) +{ + DEVICE_MESSAGE_DISPOSITION_INFO* result; + + if ((result = (DEVICE_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(DEVICE_MESSAGE_DISPOSITION_INFO))) == NULL) + { + LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (malloc failed)"); + } + else if (mallocAndStrcpy_s(&result->source, message_data->transportContext->link_name) != RESULT_OK) + { + LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)"); + free(result); + result = NULL; + } + else + { + result->message_id = message_data->transportContext->message_id; + } + + return result; +} + +static void destroy_device_message_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info) +{ + free(device_message_disposition_info->source); + free(device_message_disposition_info); +} + + +// ---------- API functions ---------- // + +TRANSPORT_LL_HANDLE IoTHubTransport_AMQP_Common_Create(const IOTHUBTRANSPORT_CONFIG* config, AMQP_GET_IO_TRANSPORT get_io_transport) +{ + TRANSPORT_LL_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_001: [If `config` or `config->upperConfig` or `get_io_transport` are NULL then IoTHubTransport_AMQP_Common_Create shall fail and return NULL.] + if (config == NULL || config->upperConfig == NULL || get_io_transport == NULL) + { + LogError("IoTHub AMQP client transport null configuration parameter (config=%p, get_io_transport=%p).", config, get_io_transport); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_002: [IoTHubTransport_AMQP_Common_Create shall fail and return NULL if `config->upperConfig->protocol` is NULL] + else if (config->upperConfig->protocol == NULL) + { + LogError("Failed to create the AMQP transport common instance (NULL parameter received: protocol=%p, iotHubName=%p, iotHubSuffix=%p)", + config->upperConfig->protocol, config->upperConfig->iotHubName, config->upperConfig->iotHubSuffix); + result = NULL; + } + else + { + AMQP_TRANSPORT_INSTANCE* instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_003: [Memory shall be allocated for the transport's internal state structure (`instance`)] + if ((instance = (AMQP_TRANSPORT_INSTANCE*)malloc(sizeof(AMQP_TRANSPORT_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_004: [If malloc() fails, IoTHubTransport_AMQP_Common_Create shall fail and return NULL] + LogError("Could not allocate AMQP transport state (malloc failed)"); + result = NULL; + } + else + { + memset(instance, 0, sizeof(AMQP_TRANSPORT_INSTANCE)); + instance->amqp_connection_state = AMQP_CONNECTION_STATE_CLOSED; + instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET; + instance->state = AMQP_TRANSPORT_STATE_NOT_CONNECTED; + instance->authorization_module = config->auth_module_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_124: [`instance->connection_retry_control` shall be set using retry_control_create(), passing defaults EXPONENTIAL_BACKOFF_WITH_JITTER and 0] + if ((instance->connection_retry_control = retry_control_create(DEFAULT_RETRY_POLICY, DEFAULT_MAX_RETRY_TIME_IN_SECS)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_125: [If retry_control_create() fails, IoTHubTransport_AMQP_Common_Create shall fail and return NULL] + LogError("Failed to create the connection retry control."); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_005: [If `config->upperConfig->protocolGatewayHostName` is NULL, `instance->iothub_target_fqdn` shall be set as `config->upperConfig->iotHubName` + "." + `config->upperConfig->iotHubSuffix`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_006: [If `config->upperConfig->protocolGatewayHostName` is not NULL, `instance->iothub_target_fqdn` shall be set with a copy of it] + else if ((instance->iothub_host_fqdn = get_target_iothub_fqdn(config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_007: [If `instance->iothub_target_fqdn` fails to be set, IoTHubTransport_AMQP_Common_Create shall fail and return NULL] + LogError("Failed to obtain the iothub target fqdn."); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_008: [`instance->registered_devices` shall be set using singlylinkedlist_create()] + else if ((instance->registered_devices = singlylinkedlist_create()) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_009: [If singlylinkedlist_create() fails, IoTHubTransport_AMQP_Common_Create shall fail and return NULL] + LogError("Failed to initialize the internal list of registered devices (singlylinkedlist_create failed)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_010: [`get_io_transport` shall be saved on `instance->underlying_io_transport_provider`] + instance->underlying_io_transport_provider = get_io_transport; + instance->is_trace_on = false; + instance->option_sas_token_lifetime_secs = DEFAULT_SAS_TOKEN_LIFETIME_SECS; + instance->option_sas_token_refresh_time_secs = DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS; + instance->option_cbs_request_timeout_secs = DEFAULT_CBS_REQUEST_TIMEOUT_SECS; + instance->option_send_event_timeout_secs = DEFAULT_EVENT_SEND_TIMEOUT_SECS; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_12_002: [The connection idle timeout parameter default value shall be set to 240000 milliseconds using connection_set_idle_timeout()] + instance->svc2cl_keep_alive_timeout_secs = DEFAULT_SERVICE_KEEP_ALIVE_FREQ_SECS; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_99_001: [The remote idle timeout ratio shall be set to 0.5 using connection_set_remote_idle_timeout_empty_frame_send_ratio()] + instance->cl2svc_keep_alive_send_ratio = DEFAULT_REMOTE_IDLE_PING_RATIO; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_012: [If IoTHubTransport_AMQP_Common_Create succeeds it shall return a pointer to `instance`.] + result = (TRANSPORT_LL_HANDLE)instance; + } + + if (result == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_011: [If IoTHubTransport_AMQP_Common_Create fails it shall free any memory it allocated] + internal_destroy_instance(instance); + } + } + } + + return result; +} + +IOTHUB_PROCESS_ITEM_RESULT IoTHubTransport_AMQP_Common_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + IOTHUB_PROCESS_ITEM_RESULT result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_144: [If `handle` or `iothub_item` are NULL, `IoTHubTransport_AMQP_Common_ProcessItem` shall fail and return IOTHUB_PROCESS_ERROR.] + if (handle == NULL || iothub_item == NULL) + { + LogError("Invalid argument (handle=%p, iothub_item=%p)", handle, iothub_item); + result = IOTHUB_PROCESS_ERROR; + } + else + { + if (item_type == IOTHUB_TYPE_DEVICE_TWIN) + { + AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT* dev_twin_ctx; + + if ((dev_twin_ctx = (AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT*)malloc(sizeof(AMQP_TRANSPORT_DEVICE_TWIN_CONTEXT))) == NULL) + { + LogError("Failed allocating context for TWIN message"); + result = IOTHUB_PROCESS_ERROR; + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)iothub_item->device_twin->device_handle; + + dev_twin_ctx->client_handle = iothub_item->device_twin->client_handle; + dev_twin_ctx->item_id = iothub_item->device_twin->item_id; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_146: [device_send_twin_update_async() shall be invoked passing `iothub_item->device_twin->report_data_handle` and `on_device_send_twin_update_complete_callback`] + if (device_send_twin_update_async( + registered_device->device_handle, + iothub_item->device_twin->report_data_handle, + on_device_send_twin_update_complete_callback, (void*)dev_twin_ctx) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_147: [If device_send_twin_update_async() fails, `IoTHubTransport_AMQP_Common_ProcessItem` shall fail and return IOTHUB_PROCESS_ERROR.] + LogError("Failed sending TWIN update"); + free(dev_twin_ctx); + result = IOTHUB_PROCESS_ERROR; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_150: [If no errors occur, `IoTHubTransport_AMQP_Common_ProcessItem` shall return IOTHUB_PROCESS_OK.] + result = IOTHUB_PROCESS_OK; + } + } + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_145: [If `item_type` is not IOTHUB_TYPE_DEVICE_TWIN, `IoTHubTransport_AMQP_Common_ProcessItem` shall fail and return IOTHUB_PROCESS_ERROR.] + else + { + LogError("Item type not supported (%d)", item_type); + result = IOTHUB_PROCESS_ERROR; + } + } + + return result; +} + +void IoTHubTransport_AMQP_Common_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + (void)iotHubClientHandle; // unused as of now. + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_016: [If `handle` is NULL, IoTHubTransport_AMQP_Common_DoWork shall return without doing any work] + if (handle == NULL) + { + LogError("IoTHubClient DoWork failed: transport handle parameter is NULL."); + } + else + { + AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle; + LIST_ITEM_HANDLE list_item; + + if (transport_instance->state == AMQP_TRANSPORT_STATE_NOT_CONNECTED_NO_MORE_RETRIES) + { + // Nothing to be done. + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_017: [If `instance->state` is `RECONNECTION_REQUIRED`, IoTHubTransport_AMQP_Common_DoWork shall attempt to trigger the connection-retry logic and return] + else if (transport_instance->state == AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED) + { + RETRY_ACTION retry_action; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_126: [The connection retry shall be attempted only if retry_control_should_retry() returns RETRY_ACTION_NOW, or if it fails] + if (retry_control_should_retry(transport_instance->connection_retry_control, &retry_action) != RESULT_OK) + { + LogError("retry_control_should_retry() failed; assuming immediate connection retry for safety."); + retry_action = RETRY_ACTION_RETRY_NOW; + } + + if (retry_action == RETRY_ACTION_RETRY_NOW) + { + prepare_for_connection_retry(transport_instance); + } + else if (retry_action == RETRY_ACTION_STOP_RETRYING) + { + update_state(transport_instance, AMQP_TRANSPORT_STATE_NOT_CONNECTED_NO_MORE_RETRIES); + + (void)singlylinkedlist_foreach(transport_instance->registered_devices, raise_connection_status_callback_retry_expired, NULL); + } + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_018: [If there are no devices registered on the transport, IoTHubTransport_AMQP_Common_DoWork shall skip do_work for devices] + if ((list_item = singlylinkedlist_get_head_item(transport_instance->registered_devices)) != NULL) + { + // We need to check if there are devices, otherwise the amqp_connection won't be able to be created since + // there is not a preferred authentication mode set yet on the transport. + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_019: [If `instance->amqp_connection` is NULL, it shall be established] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_12_003: [AMQP connection will be configured using the `svc2cl_keep_alive_timeout_secs` value from SetOption ] + if (transport_instance->amqp_connection == NULL && establish_amqp_connection(transport_instance) != RESULT_OK) + { + LogError("AMQP transport failed to establish connection with service."); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_020: [If the amqp_connection is OPENED, the transport shall iterate through each registered device and perform a device-specific do_work on each] + else if (transport_instance->amqp_connection_state == AMQP_CONNECTION_STATE_OPENED) + { + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device; + + if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL) + { + LogError("Transport had an unexpected failure during DoWork (failed to fetch a registered_devices list item value)"); + } + else if (registered_device->number_of_send_event_complete_failures >= MAX_NUMBER_OF_DEVICE_FAILURES) + { + LogError("Device '%s' reported a critical failure (events completed sending with failures); connection retry will be triggered.", STRING_c_str(registered_device->device_id)); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED); + } + else if (IoTHubTransport_AMQP_Common_Device_DoWork(registered_device) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_021: [If DoWork fails for the registered device for more than MAX_NUMBER_OF_DEVICE_FAILURES, connection retry shall be triggered] + if (registered_device->number_of_previous_failures >= MAX_NUMBER_OF_DEVICE_FAILURES) + { + LogError("Device '%s' reported a critical failure; connection retry will be triggered.", STRING_c_str(registered_device->device_id)); + + update_state(transport_instance, AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED); + } + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_022: [If `instance->amqp_connection` is not NULL, amqp_connection_do_work shall be invoked] + if (transport_instance->amqp_connection != NULL) + { + amqp_connection_do_work(transport_instance->amqp_connection); + } + } + } +} + +int IoTHubTransport_AMQP_Common_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_084: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result] + if (handle == NULL) + { + LogError("Invalid handle to IoTHubClient AMQP transport device handle."); + result = __FAILURE__; + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_085: [If `amqp_device_instance` is not registered, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result] + if (!is_device_registered(amqp_device_instance)) + { + LogError("Device '%s' failed subscribing to cloud-to-device messages (device is not registered)", STRING_c_str(amqp_device_instance->device_id)); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_086: [device_subscribe_message() shall be invoked passing `on_message_received_callback`] + else if (device_subscribe_message(amqp_device_instance->device_handle, on_message_received, amqp_device_instance) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_087: [If device_subscribe_message() fails, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result] + LogError("Device '%s' failed subscribing to cloud-to-device messages (device_subscribe_message failed)", STRING_c_str(amqp_device_instance->device_id)); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_088: [If no failures occur, IoTHubTransport_AMQP_Common_Subscribe shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +void IoTHubTransport_AMQP_Common_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_093: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Subscribe shall return] + if (handle == NULL) + { + LogError("Invalid handle to IoTHubClient AMQP transport device handle."); + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_094: [If `amqp_device_instance` is not registered, IoTHubTransport_AMQP_Common_Subscribe shall return] + if (!is_device_registered(amqp_device_instance)) + { + LogError("Device '%s' failed unsubscribing to cloud-to-device messages (device is not registered)", STRING_c_str(amqp_device_instance->device_id)); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_095: [device_unsubscribe_message() shall be invoked passing `amqp_device_instance->device_handle`] + else if (device_unsubscribe_message(amqp_device_instance->device_handle) != RESULT_OK) + { + LogError("Device '%s' failed unsubscribing to cloud-to-device messages (device_unsubscribe_message failed)", STRING_c_str(amqp_device_instance->device_id)); + } + } +} + +int IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_131: [If `handle` is NULL, `IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin` shall fail and return non-zero.] + if (handle == NULL) + { + LogError("Invalid argument (handle is NULL"); + result = __FAILURE__; + } + else + { + AMQP_TRANSPORT_INSTANCE* transport = (AMQP_TRANSPORT_INSTANCE*)handle; + + if (get_number_of_registered_devices(transport) != 1) + { + LogError("Device Twin not supported on device multiplexing scenario"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(transport->registered_devices); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_136: [If no errors occur, `IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin` shall return zero.] + result = RESULT_OK; + + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device; + + if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL) + { + LogError("Failed retrieving registered device information"); + result = __FAILURE__; + break; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_134: [device_subscribe_for_twin_updates() shall be invoked for the registered device, passing `on_device_twin_update_received_callback`] + else if (device_subscribe_for_twin_updates(registered_device->device_handle, on_device_twin_update_received_callback, (void*)registered_device) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_135: [If device_subscribe_for_twin_updates() fails, `IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin` shall fail and return non-zero.] + LogError("Failed subscribing for device Twin updates"); + result = __FAILURE__; + break; + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + } + } + + return result; +} + +void IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_140: [If `handle` is NULL, `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin` shall return.] + if (handle == NULL) + { + LogError("Invalid argument (handle is NULL"); + } + else + { + AMQP_TRANSPORT_INSTANCE* transport = (AMQP_TRANSPORT_INSTANCE*)handle; + + if (get_number_of_registered_devices(transport) != 1) + { + LogError("Device Twin not supported on device multiplexing scenario"); + } + else + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(transport->registered_devices); + + while (list_item != NULL) + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device; + + if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL) + { + LogError("Failed retrieving registered device information"); + break; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_142: [device_unsubscribe_for_twin_updates() shall be invoked for the registered device] + else if (device_unsubscribe_for_twin_updates(registered_device->device_handle) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_143: [If `device_unsubscribe_for_twin_updates` fails, the error shall be ignored] + LogError("Failed unsubscribing for device Twin updates"); + break; + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + } + } +} + +int IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + + if (handle == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_004: [ If `handle` is NULL, `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` shall fail and return a non-zero value. ] */ + LogError("NULL handle"); + result = __FAILURE__; + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_026: [ `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` shall remember that a subscribe is to be performed in the next call to DoWork and on success it shall return 0. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_005: [ If the transport is already subscribed to receive C2D method requests, `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` shall perform no additional action and return 0. ]*/ + device_state->subscribe_methods_needed = true; + device_state->subscribed_for_methods = false; + result = 0; + } + + return result; +} + +void IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + if (handle == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_006: [ If `handle` is NULL, `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod` shall do nothing. ]*/ + LogError("NULL handle"); + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_008: [ If the transport is not subscribed to receive C2D method requests then `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod` shall do nothing. ]*/ + if (device_state->subscribe_methods_needed) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_007: [ `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod` shall unsubscribe from receiving C2D method requests by calling `iothubtransportamqp_methods_unsubscribe`. ]*/ + device_state->subscribed_for_methods = false; + device_state->subscribe_methods_needed = false; + iothubtransportamqp_methods_unsubscribe(device_state->methods_handle); + } + } +} + +int IoTHubTransport_AMQP_Common_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + (void)response; + (void)response_size; + (void)status_response; + (void)methodId; + int result; + AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + if (device_state != NULL) + { + IOTHUBTRANSPORT_AMQP_METHOD_HANDLE saved_handle = (IOTHUBTRANSPORT_AMQP_METHOD_HANDLE)methodId; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_019: [ `IoTHubTransport_AMQP_Common_DeviceMethod_Response` shall call `iothubtransportamqp_methods_respond` passing to it the `method_handle` argument, the response bytes, response size and the status code. ]*/ + if (iothubtransportamqp_methods_respond(saved_handle, response, response_size, status_response) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_029: [ If `iothubtransportamqp_methods_respond` fails, `on_methods_request_received` shall return a non-zero value. ]*/ + LogError("iothubtransportamqp_methods_respond failed"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + result = __FAILURE__; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_AMQP_Common_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_096: [If `handle` or `iotHubClientStatus` are NULL, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG] + if (handle == NULL || iotHubClientStatus == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Failed retrieving the device send status (either handle (%p) or iotHubClientStatus (%p) are NULL)", handle, iotHubClientStatus); + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle; + + DEVICE_SEND_STATUS device_send_status; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_097: [IoTHubTransport_AMQP_Common_GetSendStatus shall invoke device_get_send_status()] + if (device_get_send_status(amqp_device_state->device_handle, &device_send_status) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_098: [If device_get_send_status() fails, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_ERROR] + LogError("Failed retrieving the device send status (device_get_send_status failed)"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_099: [If device_get_send_status() returns DEVICE_SEND_STATUS_BUSY, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY] + if (device_send_status == DEVICE_SEND_STATUS_BUSY) + { + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_100: [If device_get_send_status() returns DEVICE_SEND_STATUS_IDLE, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE] + else // DEVICE_SEND_STATUS_IDLE + { + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_109: [If no failures occur, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK] + result = IOTHUB_CLIENT_OK; + } + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_AMQP_Common_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + IOTHUB_CLIENT_RESULT result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_101: [If `handle`, `option` or `value` are NULL then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] + if ((handle == NULL) || (option == NULL) || (value == NULL)) + { + LogError("Invalid parameter (NULL) passed to AMQP transport SetOption (handle=%p, options=%p, value=%p)", handle, option, value); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle; + bool is_device_specific_option; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_102: [If `option` is a device-specific option, it shall be saved and applied to each registered device using device_set_option()] + if (strcmp(OPTION_SAS_TOKEN_LIFETIME, option) == 0) + { + is_device_specific_option = true; + transport_instance->option_sas_token_lifetime_secs = *(size_t*)value; + } + else if (strcmp(OPTION_SAS_TOKEN_REFRESH_TIME, option) == 0) + { + is_device_specific_option = true; + transport_instance->option_sas_token_refresh_time_secs = *(size_t*)value; + } + else if (strcmp(OPTION_CBS_REQUEST_TIMEOUT, option) == 0) + { + is_device_specific_option = true; + transport_instance->option_cbs_request_timeout_secs = *(size_t*)value; + } + else if (strcmp(OPTION_EVENT_SEND_TIMEOUT_SECS, option) == 0) + { + is_device_specific_option = true; + transport_instance->option_send_event_timeout_secs = *(size_t*)value; + } + else + { + is_device_specific_option = false; + } + + if (is_device_specific_option) + { + if (IoTHubTransport_AMQP_Common_Device_SetOption(handle, option, (void*)value) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_103: [If device_set_option() fails, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_ERROR] + LogError("transport failed setting option '%s' (failed setting option on one or more registered devices)", option); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else if ((strcmp(OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS, option) == 0) || (strcmp(OPTION_C2D_KEEP_ALIVE_FREQ_SECS, option) == 0)) + { + transport_instance->svc2cl_keep_alive_timeout_secs = *(size_t*)value; + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(OPTION_REMOTE_IDLE_TIMEOUT_RATIO, option) == 0) + { + + if ((*(double*)value <= 0.0) || (*(double*)value >= MAX_SERVICE_KEEP_ALIVE_RATIO)) + { + LogError("Invalid remote idle ratio %lf", *(double*) value); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + transport_instance->cl2svc_keep_alive_send_ratio = *(double*)value; // override the default and set the user configured remote idle ratio value + result = IOTHUB_CLIENT_OK; + } + + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_104: [If `option` is `logtrace`, `value` shall be saved and applied to `instance->connection` using amqp_connection_set_logging()] + else if (strcmp(OPTION_LOG_TRACE, option) == 0) + { + transport_instance->is_trace_on = *((bool*)value); + + if (transport_instance->amqp_connection != NULL && + amqp_connection_set_logging(transport_instance->amqp_connection, transport_instance->is_trace_on) != RESULT_OK) + { + LogError("transport failed setting option '%s' (amqp_connection_set_logging failed)", option); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + } + else if (strcmp(OPTION_HTTP_PROXY, option) == 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_032: [ If `option` is `proxy_data`, `value` shall be used as an `HTTP_PROXY_OPTIONS*`. ]*/ + HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS*)value; + + if (transport_instance->tls_io != NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_038: [ If the underlying IO has already been created, then `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + LogError("Cannot set proxy option once the underlying IO is created"); + result = IOTHUB_CLIENT_ERROR; + } + else if (proxy_options->host_address == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_034: [ If `host_address` is NULL, `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + LogError("NULL host_address in proxy options"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_037: [ If only one of `username` and `password` is NULL, `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + else if (((proxy_options->username == NULL) || (proxy_options->password == NULL)) && + (proxy_options->username != proxy_options->password)) + { + LogError("Only one of username and password for proxy settings was NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + char* copied_proxy_hostname = NULL; + char* copied_proxy_username = NULL; + char* copied_proxy_password = NULL; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_033: [ The fields `host_address`, `port`, `username` and `password` shall be saved for later used (needed when creating the underlying IO to be used by the transport). ]*/ + transport_instance->http_proxy_port = proxy_options->port; + if (mallocAndStrcpy_s(&copied_proxy_hostname, proxy_options->host_address) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_035: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + LogError("Cannot copy HTTP proxy hostname"); + result = IOTHUB_CLIENT_ERROR; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_036: [ `username` and `password` shall be allowed to be NULL. ]*/ + else if ((proxy_options->username != NULL) && (mallocAndStrcpy_s(&copied_proxy_username, proxy_options->username) != 0)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_035: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + free(copied_proxy_hostname); + LogError("Cannot copy HTTP proxy username"); + result = IOTHUB_CLIENT_ERROR; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_036: [ `username` and `password` shall be allowed to be NULL. ]*/ + else if ((proxy_options->password != NULL) && (mallocAndStrcpy_s(&copied_proxy_password, proxy_options->password) != 0)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_035: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_AMQP_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + if (copied_proxy_username != NULL) + { + free(copied_proxy_username); + } + free(copied_proxy_hostname); + LogError("Cannot copy HTTP proxy password"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_040: [ When setting the proxy options succeeds any previously saved proxy options shall be freed. ]*/ + free_proxy_data(transport_instance); + + transport_instance->http_proxy_hostname = copied_proxy_hostname; + transport_instance->http_proxy_username = copied_proxy_username; + transport_instance->http_proxy_password = copied_proxy_password; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_039: [ If setting the `proxy_data` option succeeds, `IoTHubTransport_AMQP_Common_SetOption` shall return `IOTHUB_CLIENT_OK` ]*/ + result = IOTHUB_CLIENT_OK; + } + } + } + else + { + result = IOTHUB_CLIENT_OK; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_007: [ If `option` is `x509certificate` and the transport preferred authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_008: [ If `option` is `x509privatekey` and the transport preferred authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ] + if (strcmp(OPTION_X509_CERT, option) == 0 || strcmp(OPTION_X509_PRIVATE_KEY, option) == 0) + { + if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET) + { + transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_X509; + } + else if (transport_instance->preferred_authentication_mode != AMQP_TRANSPORT_AUTHENTICATION_MODE_X509) + { + LogError("transport failed setting option '%s' (preferred authentication method is not x509)", option); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + IoTHubClient_Auth_Set_x509_Type(transport_instance->authorization_module, true); + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_105: [If `option` does not match one of the options handled by this module, it shall be passed to `instance->tls_io` using xio_setoption()] + if (result != IOTHUB_CLIENT_INVALID_ARG) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_106: [If `instance->tls_io` is NULL, it shall be set invoking instance->underlying_io_transport_provider()] + if (transport_instance->tls_io == NULL && + get_new_underlying_io_transport(transport_instance, &transport_instance->tls_io) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_107: [If instance->underlying_io_transport_provider() fails, IoTHubTransport_AMQP_Common_SetOption shall fail and return IOTHUB_CLIENT_ERROR] + LogError("transport failed setting option '%s' (failed to obtain a TLS I/O transport).", option); + result = IOTHUB_CLIENT_ERROR; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_108: [When `instance->tls_io` is created, IoTHubTransport_AMQP_Common_SetOption shall apply `instance->saved_tls_options` with OptionHandler_FeedOptions()] + else if (xio_setoption(transport_instance->tls_io, option, value) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_001: [If xio_setoption fails, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_ERROR.] + LogError("transport failed setting option '%s' (xio_setoption failed)", option); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (save_underlying_io_transport_options(transport_instance) != RESULT_OK) + { + LogError("IoTHubTransport_AMQP_Common_SetOption failed to save underlying I/O options; failure will be ignored"); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_001: [If no failures occur, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_OK.] + result = IOTHUB_CLIENT_OK; + } + } + } + } + + return result; +} + +IOTHUB_DEVICE_HANDLE IoTHubTransport_AMQP_Common_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ +#ifdef NO_LOGGING + UNUSED(iotHubClientHandle); +#endif + + IOTHUB_DEVICE_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_17_005: [If `handle`, `device`, `iotHubClientHandle` or `waitingToSend` is NULL, IoTHubTransport_AMQP_Common_Register shall return NULL] + if ((handle == NULL) || (device == NULL) || (waitingToSend == NULL) || (iotHubClientHandle == NULL)) + { + LogError("invalid parameter TRANSPORT_LL_HANDLE handle=%p, const IOTHUB_DEVICE_CONFIG* device=%p, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle=%p, PDLIST_ENTRY waiting_to_send=%p", + handle, device, iotHubClientHandle, waitingToSend); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_002: [IoTHubTransport_AMQP_Common_Register shall return NULL if `device->deviceId` is NULL.] + else if (device->deviceId == NULL) + { + LogError("Transport failed to register device (device_id provided is NULL)"); + result = NULL; + } + else + { + LIST_ITEM_HANDLE list_item; + AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_064: [If the device is already registered, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.] + if (is_device_registered_ex(transport_instance->registered_devices, device->deviceId, &list_item)) + { + LogError("IoTHubTransport_AMQP_Common_Register failed (device '%s' already registered on this transport instance)", device->deviceId); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_065: [IoTHubTransport_AMQP_Common_Register shall fail and return NULL if the device is not using an authentication mode compatible with the currently used by the transport.] + else if (!is_device_credential_acceptable(device, transport_instance->preferred_authentication_mode)) + { + LogError("Transport failed to register device '%s' (device credential was not accepted)", device->deviceId); + result = NULL; + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_066: [IoTHubTransport_AMQP_Common_Register shall allocate an instance of AMQP_TRANSPORT_DEVICE_INSTANCE to store the state of the new registered device.] + if ((amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)malloc(sizeof(AMQP_TRANSPORT_DEVICE_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_067: [If malloc fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.] + LogError("Transport failed to register device '%s' (failed to create the device state instance; malloc failed)", device->deviceId); + result = NULL; + } + else + { + memset(amqp_device_instance, 0, sizeof(AMQP_TRANSPORT_DEVICE_INSTANCE)); + + char* local_product_info = NULL; + void* product_info; + if ((IoTHubClientCore_LL_GetOption(iotHubClientHandle, OPTION_PRODUCT_INFO, &product_info) != IOTHUB_CLIENT_OK) || (product_info == NULL)) + { + mallocAndStrcpy_s(&local_product_info, CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION); + } + else + { + mallocAndStrcpy_s(&local_product_info, STRING_c_str((STRING_HANDLE)product_info)); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_068: [IoTHubTransport_AMQP_Common_Register shall save the handle references to the IoTHubClient, transport, waitingToSend list on `amqp_device_instance`.] + amqp_device_instance->iothub_client_handle = iotHubClientHandle; + amqp_device_instance->transport_instance = transport_instance; + amqp_device_instance->waiting_to_send = waitingToSend; + amqp_device_instance->device_state = DEVICE_STATE_STOPPED; + amqp_device_instance->max_state_change_timeout_secs = DEFAULT_DEVICE_STATE_CHANGE_TIMEOUT_SECS; + amqp_device_instance->subscribe_methods_needed = false; + amqp_device_instance->subscribed_for_methods = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_069: [A copy of `config->deviceId` shall be saved into `device_state->device_id`] + if ((amqp_device_instance->device_id = STRING_construct(device->deviceId)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_070: [If STRING_construct() fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL] + LogError("Transport failed to register device '%s' (failed to copy the deviceId)", device->deviceId); + result = NULL; + } + else + { + DEVICE_CONFIG device_config; + memset(&device_config, 0, sizeof(DEVICE_CONFIG)); + device_config.iothub_host_fqdn = (char*)STRING_c_str(transport_instance->iothub_host_fqdn); + device_config.authorization_module = device->authorization_module; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_072: [The configuration for device_create shall be set according to the authentication preferred by IOTHUB_DEVICE_CONFIG] + device_config.authentication_mode = get_authentication_mode(device); + device_config.on_state_changed_callback = on_device_state_changed_callback; + device_config.on_state_changed_context = amqp_device_instance; + device_config.product_info = local_product_info; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_071: [`amqp_device_instance->device_handle` shall be set using device_create()] + if ((amqp_device_instance->device_handle = device_create(&device_config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_073: [If device_create() fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL] + LogError("Transport failed to register device '%s' (failed to create the DEVICE_HANDLE instance)", device->deviceId); + result = NULL; + } + else + { + bool is_first_device_being_registered = (singlylinkedlist_get_head_item(transport_instance->registered_devices) == NULL); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_010: [ `IoTHubTransport_AMQP_Common_Register` shall create a new iothubtransportamqp_methods instance by calling `iothubtransportamqp_methods_create` while passing to it the the fully qualified domain name, the device Id, and optional module Id. ]*/ + amqp_device_instance->methods_handle = iothubtransportamqp_methods_create(STRING_c_str(transport_instance->iothub_host_fqdn), device->deviceId, device->moduleId); + if (amqp_device_instance->methods_handle == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_011: [ If `iothubtransportamqp_methods_create` fails, `IoTHubTransport_AMQP_Common_Create` shall fail and return NULL. ]*/ + LogError("Transport failed to register device '%s' (Cannot create the methods module)", device->deviceId); + result = NULL; + } + else + { + if (replicate_device_options_to(amqp_device_instance, device_config.authentication_mode) != RESULT_OK) + { + LogError("Transport failed to register device '%s' (failed to replicate options)", device->deviceId); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_074: [IoTHubTransport_AMQP_Common_Register shall add the `amqp_device_instance` to `instance->registered_devices`] + else if (singlylinkedlist_add(transport_instance->registered_devices, amqp_device_instance) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_075: [If it fails to add `amqp_device_instance`, IoTHubTransport_AMQP_Common_Register shall fail and return NULL] + LogError("Transport failed to register device '%s' (singlylinkedlist_add failed)", device->deviceId); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_076: [If the device is the first being registered on the transport, IoTHubTransport_AMQP_Common_Register shall save its authentication mode as the transport preferred authentication mode] + if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET && + is_first_device_being_registered) + { + if (device_config.authentication_mode == DEVICE_AUTH_MODE_CBS) + { + transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS; + } + else + { + transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_X509; + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_078: [IoTHubTransport_AMQP_Common_Register shall return a handle to `amqp_device_instance` as a IOTHUB_DEVICE_HANDLE] + result = (IOTHUB_DEVICE_HANDLE)amqp_device_instance; + } + } + } + } + + if (result == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_077: [If IoTHubTransport_AMQP_Common_Register fails, it shall free all memory it allocated] + internal_destroy_amqp_device_instance(amqp_device_instance); + } + free(local_product_info); + } + } + } + + return result; +} + +void IoTHubTransport_AMQP_Common_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_079: [if `deviceHandle` provided is NULL, IoTHubTransport_AMQP_Common_Unregister shall return.] + if (deviceHandle == NULL) + { + LogError("Failed to unregister device (deviceHandle is NULL)."); + } + else + { + AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)deviceHandle; + const char* device_id; + LIST_ITEM_HANDLE list_item; + + if ((device_id = STRING_c_str(registered_device->device_id)) == NULL) + { + LogError("Failed to unregister device (failed to get device id char ptr)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_080: [if `deviceHandle` has a NULL reference to its transport instance, IoTHubTransport_AMQP_Common_Unregister shall return.] + else if (registered_device->transport_instance == NULL) + { + LogError("Failed to unregister device '%s' (deviceHandle does not have a transport state associated to).", device_id); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_081: [If the device is not registered with this transport, IoTHubTransport_AMQP_Common_Unregister shall return] + else if (!is_device_registered_ex(registered_device->transport_instance->registered_devices, device_id, &list_item)) + { + LogError("Failed to unregister device '%s' (device is not registered within this transport).", device_id); + } + else + { + // Removing it first so the race hazzard is reduced between this function and DoWork. Best would be to use locks. + if (singlylinkedlist_remove(registered_device->transport_instance->registered_devices, list_item) != RESULT_OK) + { + LogError("Failed to unregister device '%s' (singlylinkedlist_remove failed).", device_id); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_012: [IoTHubTransport_AMQP_Common_Unregister shall destroy the C2D methods handler by calling iothubtransportamqp_methods_destroy] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_083: [IoTHubTransport_AMQP_Common_Unregister shall free all the memory allocated for the `device_instance`] + internal_destroy_amqp_device_instance(registered_device); + } + } + } +} + +void IoTHubTransport_AMQP_Common_Destroy(TRANSPORT_LL_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_013: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Destroy shall return immediatelly] + if (handle == NULL) + { + LogError("Failed to destroy AMQP transport instance (handle is NULL)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_014: [IoTHubTransport_AMQP_Common_Destroy shall invoke IoTHubTransport_AMQP_Common_Unregister on each of its registered devices.] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_015: [All members of `instance` (including tls_io) shall be destroyed and its memory released] + internal_destroy_instance((AMQP_TRANSPORT_INSTANCE*)handle); + } +} + +int IoTHubTransport_AMQP_Common_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_128: [If `handle` is NULL, `IoTHubTransport_AMQP_Common_SetRetryPolicy` shall fail and return non-zero.] + if (handle == NULL) + { + LogError("Cannot set retry policy (transport handle is NULL)"); + result = __FAILURE__; + } + else + { + RETRY_CONTROL_HANDLE new_retry_control; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_129: [`transport_instance->connection_retry_control` shall be set using retry_control_create(), passing `retryPolicy` and `retryTimeoutLimitInSeconds`.] + if ((new_retry_control = retry_control_create(retryPolicy, (unsigned int)retryTimeoutLimitInSeconds)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_130: [If retry_control_create() fails, `IoTHubTransport_AMQP_Common_SetRetryPolicy` shall fail and return non-zero.] + LogError("Cannot set retry policy (retry_control_create failed)"); + result = __FAILURE__; + } + else + { + AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle; + RETRY_CONTROL_HANDLE previous_retry_control = transport_instance->connection_retry_control; + + transport_instance->connection_retry_control = new_retry_control; + + retry_control_destroy(previous_retry_control); + + if (transport_instance->state == AMQP_TRANSPORT_STATE_NOT_CONNECTED_NO_MORE_RETRIES) + { + transport_instance->state = AMQP_TRANSPORT_STATE_RECONNECTION_REQUIRED; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_128: [If no errors occur, `IoTHubTransport_AMQP_Common_SetRetryPolicy` shall return zero.] + result = RESULT_OK; + } + } + + return result; +} + +STRING_HANDLE IoTHubTransport_AMQP_Common_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + STRING_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_001: [If `handle` is NULL, `IoTHubTransport_AMQP_Common_GetHostname` shall return NULL.] + if (handle == NULL) + { + LogError("Cannot provide the target host name (transport handle is NULL)."); + + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_002: [IoTHubTransport_AMQP_Common_GetHostname shall return a copy of `instance->iothub_target_fqdn`.] + else if ((result = STRING_clone(((AMQP_TRANSPORT_INSTANCE*)(handle))->iothub_host_fqdn)) == NULL) + { + LogError("Cannot provide the target host name (STRING_clone failed)."); + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_AMQP_Common_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + IOTHUB_CLIENT_RESULT result; + if (message_data == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_001: [If messageData is NULL, IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_INVALID_ARG.] */ + LogError("Failed sending message disposition (message_data is NULL)"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_002: [If any of the messageData fields are NULL, IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_INVALID_ARG.] */ + if (message_data->messageHandle == NULL || message_data->transportContext == NULL) + { + LogError("Failed sending message disposition (message_data->messageHandle (%p) or message_data->transportContext (%p) are NULL)", message_data->messageHandle, message_data->transportContext); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_004: [IoTHubTransport_AMQP_Common_SendMessageDisposition shall convert the given IOTHUBMESSAGE_DISPOSITION_RESULT to the equivalent AMQP_VALUE and will return the result of calling messagereceiver_send_message_disposition. ] */ + DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result = get_device_disposition_result_from(disposition); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_112: [A DEVICE_MESSAGE_DISPOSITION_INFO instance shall be created with a copy of the `link_name` and `message_id` contained in `message_data`] + if ((device_message_disposition_info = create_device_message_disposition_info_from(message_data)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_113: [If the DEVICE_MESSAGE_DISPOSITION_INFO fails to be created, `IoTHubTransport_AMQP_Common_SendMessageDisposition()` shall fail and return IOTHUB_CLIENT_ERROR] + LogError("Device '%s' failed sending message disposition (failed creating DEVICE_MESSAGE_DISPOSITION_RESULT)", STRING_c_str(message_data->transportContext->device_state->device_id)); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_003: [IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR if the POST message fails, otherwise return IOTHUB_CLIENT_OK.] */ + if (device_send_message_disposition(message_data->transportContext->device_state->device_handle, device_message_disposition_info, device_disposition_result) != RESULT_OK) + { + LogError("Device '%s' failed sending message disposition (device_send_message_disposition failed)", STRING_c_str(message_data->transportContext->device_state->device_id)); + result = IOTHUB_CLIENT_ERROR; + } + else + { + IoTHubMessage_Destroy(message_data->messageHandle); + result = IOTHUB_CLIENT_OK; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_114: [`IoTHubTransport_AMQP_Common_SendMessageDisposition()` shall destroy the DEVICE_MESSAGE_DISPOSITION_INFO instance] + destroy_device_message_disposition_info(device_message_disposition_info); + } + } + + MESSAGE_CALLBACK_INFO_Destroy(message_data); + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_connection.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,557 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <limits.h> +#include "internal/iothubtransport_amqp_connection.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_uamqp_c/sasl_mechanism.h" +#include "azure_uamqp_c/saslclientio.h" +#include "azure_uamqp_c/sasl_mssbcbs.h" +#include "azure_uamqp_c/connection.h" +#include "azure_c_shared_utility/xlogging.h" + +#define RESULT_OK 0 +#define DEFAULT_INCOMING_WINDOW_SIZE UINT_MAX +#define DEFAULT_OUTGOING_WINDOW_SIZE 100 +#define SASL_IO_OPTION_LOG_TRACE "logtrace" +#define DEFAULT_UNIQUE_ID_LENGTH 40 + +typedef struct AMQP_CONNECTION_INSTANCE_TAG +{ + STRING_HANDLE iothub_fqdn; + XIO_HANDLE underlying_io_transport; + CBS_HANDLE cbs_handle; + CONNECTION_HANDLE connection_handle; + SESSION_HANDLE session_handle; + XIO_HANDLE sasl_io; + SASL_MECHANISM_HANDLE sasl_mechanism; + bool has_cbs; + bool has_sasl_mechanism; + bool is_trace_on; + AMQP_CONNECTION_STATE current_state; + ON_AMQP_CONNECTION_STATE_CHANGED on_state_changed_callback; + const void* on_state_changed_context; + uint32_t svc2cl_keep_alive_timeout_secs; + double cl2svc_keep_alive_send_ratio; +} AMQP_CONNECTION_INSTANCE; + + +DEFINE_ENUM_STRINGS(AMQP_CONNECTION_STATE, AMQP_CONNECTION_STATE_VALUES); + + +static int create_sasl_components(AMQP_CONNECTION_INSTANCE* instance) +{ + int result; + SASL_MECHANISM_HANDLE sasl_mechanism; + XIO_HANDLE sasl_io; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_012: [`instance->sasl_mechanism` shall be created using saslmechanism_create()] + if ((sasl_mechanism = saslmechanism_create(saslmssbcbs_get_interface(), NULL)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_013: [If saslmechanism_create() fails, amqp_connection_create() shall fail and return NULL] + LogError("Failed creating the SASL mechanism (saslmechanism_create failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_014: [A SASLCLIENTIO_CONFIG shall be set with `instance->underlying_io_transport` and `instance->sasl_mechanism`] + SASLCLIENTIO_CONFIG sasl_client_config; + sasl_client_config.sasl_mechanism = sasl_mechanism; + sasl_client_config.underlying_io = instance->underlying_io_transport; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_015: [`instance->sasl_io` shall be created using xio_create() passing saslclientio_get_interface_description() and the SASLCLIENTIO_CONFIG instance] + if ((sasl_io = xio_create(saslclientio_get_interface_description(), &sasl_client_config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_016: [If xio_create() fails, amqp_connection_create() shall fail and return NULL] + LogError("Failed creating the SASL I/O (xio_create failed)"); + saslmechanism_destroy(sasl_mechanism); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_017: [The sasl_io "logtrace" option shall be set using xio_setoption(), passing `instance->is_trace_on`] + else if (xio_setoption(sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK) + { + LogError("Failed setting the SASL I/O logging trace option (xio_setoption failed)"); + xio_destroy(sasl_io); + saslmechanism_destroy(sasl_mechanism); + result = __FAILURE__; + } + else + { + instance->sasl_mechanism = sasl_mechanism; + instance->sasl_io = sasl_io; + result = RESULT_OK; + } + } + + return result; +} + +static void update_state(AMQP_CONNECTION_INSTANCE* instance, AMQP_CONNECTION_STATE new_state) +{ + if (new_state != instance->current_state) + { + AMQP_CONNECTION_STATE previous_state = instance->current_state; + instance->current_state = new_state; + + if (instance->on_state_changed_callback != NULL) + { + instance->on_state_changed_callback(instance->on_state_changed_context, previous_state, new_state); + } + } +} + +static void on_connection_io_error(void* context) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_022: [If the connection calls back with an I/O error, `instance->on_state_changed_callback` shall be invoked if set passing code AMQP_CONNECTION_STATE_ERROR and `instance->on_state_changed_context`] + update_state((AMQP_CONNECTION_INSTANCE*)context, AMQP_CONNECTION_STATE_ERROR); +} + +static void on_connection_state_changed(void* context, CONNECTION_STATE new_connection_state, CONNECTION_STATE previous_connection_state) +{ + (void)previous_connection_state; + + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_063: [If `on_connection_state_changed` is called back, `instance->on_state_changed_callback` shall be invoked, if defined] + if (new_connection_state == CONNECTION_STATE_START) + { + // connection is using x509 authentication. + // At this point uamqp's connection only raises CONNECTION_STATE_START when using X509 auth. + // So that should be all we expect to consider the amqp_connection_handle opened. + if (instance->has_cbs == false || instance->has_sasl_mechanism == false) + { + update_state(instance, AMQP_CONNECTION_STATE_OPENED); + } + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_064: [If `on_connection_state_changed` new state is CONNECTION_STATE_OPENED, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_OPENED] + else if (new_connection_state == CONNECTION_STATE_OPENED) + { + update_state(instance, AMQP_CONNECTION_STATE_OPENED); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_065: [If `on_connection_state_changed` new state is CONNECTION_STATE_END, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_CLOSED] + else if (new_connection_state == CONNECTION_STATE_END) + { + update_state(instance, AMQP_CONNECTION_STATE_CLOSED); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_071: [If `on_connection_state_changed` new state is CONNECTION_STATE_ERROR or CONNECTION_STATE_DISCARDING, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_ERROR] + else if (new_connection_state == CONNECTION_STATE_ERROR || new_connection_state == CONNECTION_STATE_DISCARDING) + { + update_state(instance, AMQP_CONNECTION_STATE_ERROR); + } +} + +static void on_cbs_open_complete(void* context, CBS_OPEN_COMPLETE_RESULT open_complete_result) +{ + (void)context; + (void)open_complete_result; + if (open_complete_result != CBS_OPEN_OK) + { + LogError("CBS open failed"); + } +} + +static void on_cbs_error(void* context) +{ + (void)context; + LogError("CBS Error occured"); +} + +static int create_connection_handle(AMQP_CONNECTION_INSTANCE* instance) +{ + int result; + char* unique_container_id = NULL; + XIO_HANDLE connection_io_transport; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_007: [If `instance->sasl_io` is defined it shall be used as parameter `xio` in connection_create2()] + if (instance->sasl_io != NULL) + { + connection_io_transport = instance->sasl_io; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_018: [If `instance->sasl_io` is not defined, `instance->underlying_io_transport` shall be used as parameter `xio` in connection_create2()] + else + { + connection_io_transport = instance->underlying_io_transport; + } + + if ((unique_container_id = (char*)malloc(sizeof(char) * DEFAULT_UNIQUE_ID_LENGTH + 1)) == NULL) + { + result = __LINE__; + LogError("Failed creating the AMQP connection (failed creating unique ID container)"); + } + else + { + memset(unique_container_id, 0, sizeof(char) * DEFAULT_UNIQUE_ID_LENGTH + 1); + + if (UniqueId_Generate(unique_container_id, DEFAULT_UNIQUE_ID_LENGTH) != UNIQUEID_OK) + { + result = __FAILURE__; + LogError("Failed creating the AMQP connection (UniqueId_Generate failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_019: [`instance->connection_handle` shall be created using connection_create2(), passing the `connection_underlying_io`, `instance->iothub_host_fqdn` and an unique string as container ID] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_020: [connection_create2() shall also receive `on_connection_state_changed` and `on_connection_error` callback functions] + else if ((instance->connection_handle = connection_create2(connection_io_transport, STRING_c_str(instance->iothub_fqdn), unique_container_id, NULL, NULL, on_connection_state_changed, (void*)instance, on_connection_io_error, (void*)instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_021: [If connection_create2() fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed creating the AMQP connection (connection_create2 failed)"); + } + else if (connection_set_idle_timeout(instance->connection_handle, 1000 * instance->svc2cl_keep_alive_timeout_secs) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_074: [If connection_set_idle_timeout() fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed creating the AMQP connection (connection_set_idle_timeout failed)"); + } + else if (connection_set_remote_idle_timeout_empty_frame_send_ratio(instance->connection_handle, instance->cl2svc_keep_alive_send_ratio) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_99_001: [If connection_set_remote_idle_timeout_empty_frame_send_ratio fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed creating the AMQP connection (connection_set_remote_idle_timeout_empty_frame_send_ratio)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_023: [The connection tracing shall be set using connection_set_trace(), passing `instance->is_trace_on`] + connection_set_trace(instance->connection_handle, instance->is_trace_on); + + result = RESULT_OK; + } + } + + if (unique_container_id != NULL) + { + free(unique_container_id); + } + + return result; +} + +static int create_session_handle(AMQP_CONNECTION_INSTANCE* instance) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_024: [`instance->session_handle` shall be created using session_create(), passing `instance->connection_handle`] + if ((instance->session_handle = session_create(instance->connection_handle, NULL, NULL)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_025: [If session_create() fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed creating the AMQP connection (connection_create2 failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_026: [The `instance->session_handle` incoming window size shall be set as UINT_MAX using session_set_incoming_window()] + if (session_set_incoming_window(instance->session_handle, (uint32_t)DEFAULT_INCOMING_WINDOW_SIZE) != 0) + { + LogError("Failed to set the AMQP session incoming window size."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_027: [The `instance->session_handle` outgoing window size shall be set as 100 using session_set_outgoing_window()] + if (session_set_outgoing_window(instance->session_handle, DEFAULT_OUTGOING_WINDOW_SIZE) != 0) + { + LogError("Failed to set the AMQP session outgoing window size."); + } + + result = RESULT_OK; + } + + return result; +} + +static int create_cbs_handle(AMQP_CONNECTION_INSTANCE* instance) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_029: [`instance->cbs_handle` shall be created using cbs_create()`] + if ((instance->cbs_handle = cbs_create(instance->session_handle)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_030: [If cbs_create() fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed to create the CBS connection."); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_031: [`instance->cbs_handle` shall be opened using `cbs_open_async`] + else if (cbs_open_async(instance->cbs_handle, on_cbs_open_complete, instance->cbs_handle, on_cbs_error, instance->cbs_handle) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_032: [If cbs_open() fails, amqp_connection_create() shall fail and return NULL] + result = __FAILURE__; + LogError("Failed to open the connection with CBS."); + } + else + { + result = RESULT_OK; + } + + return result; +} + + +// Public APIS: + +void amqp_connection_destroy(AMQP_CONNECTION_HANDLE conn_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_035: [If `conn_handle` is NULL, amqp_connection_destroy() shall fail and return] + if (conn_handle != NULL) + { + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_036: [amqp_connection_destroy() shall destroy `instance->cbs_handle` if set using cbs_destroy()] + if (instance->cbs_handle != NULL) + { + cbs_destroy(instance->cbs_handle); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_037: [amqp_connection_destroy() shall destroy `instance->session_handle` if set using session_destroy()] + if (instance->session_handle != NULL) + { + session_destroy(instance->session_handle); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_067: [amqp_connection_destroy() shall destroy `instance->connection_handle` if set using connection_destroy()] + if (instance->connection_handle != NULL) + { + connection_destroy(instance->connection_handle); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_038: [amqp_connection_destroy() shall destroy `instance->sasl_io` if set using xio_destroy()] + if (instance->sasl_io != NULL) + { + xio_destroy(instance->sasl_io); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_039: [amqp_connection_destroy() shall destroy `instance->sasl_mechanism` if set using saslmechanism_destroy()] + if (instance->sasl_mechanism != NULL) + { + saslmechanism_destroy(instance->sasl_mechanism); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_059: [amqp_connection_destroy() shall destroy `instance->iothub_host_fqdn` if set using STRING_delete()] + if (instance->iothub_fqdn != NULL) + { + STRING_delete(instance->iothub_fqdn); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_040: [amqp_connection_destroy() shall free the memory allocated for the connection instance] + free(instance); + } +} + +AMQP_CONNECTION_HANDLE amqp_connection_create(AMQP_CONNECTION_CONFIG* config) +{ + AMQP_CONNECTION_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_001: [If `config` is NULL, amqp_connection_create() shall fail and return NULL] + if (config == NULL) + { + result = NULL; + LogError("amqp_connection_create failed (config is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_002: [If `config->iothub_host_fqdn` is NULL, amqp_connection_create() shall fail and return NULL] + else if (config->iothub_host_fqdn == NULL) + { + result = NULL; + LogError("amqp_connection_create failed (config->iothub_host_fqdn is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_003: [If `config->underlying_io_transport` is NULL, amqp_connection_create() shall fail and return NULL] + else if (config->underlying_io_transport == NULL) + { + result = NULL; + LogError("amqp_connection_create failed (config->underlying_io_transport is NULL)"); + } + else + { + AMQP_CONNECTION_INSTANCE* instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_057: [amqp_connection_create() shall allocate memory for an instance of the connection state] + if ((instance = (AMQP_CONNECTION_INSTANCE*)malloc(sizeof(AMQP_CONNECTION_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_058: [If malloc() fails, amqp_connection_create() shall fail and return NULL] + result = NULL; + LogError("amqp_connection_create failed (malloc failed)"); + } + else + { + memset(instance, 0, sizeof(AMQP_CONNECTION_INSTANCE)); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_005: [A copy of `config->iothub_host_fqdn` shall be saved on `instance->iothub_host_fqdn`] + if ((instance->iothub_fqdn = STRING_construct(config->iothub_host_fqdn)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_066: [If STRING_construct() fails, amqp_connection_create() shall fail and return NULL] + result = NULL; + LogError("amqp_connection_create failed (STRING_construct failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_006: [`config->underlying_io_transport` shall be saved on `instance->underlying_io_transport`] + instance->underlying_io_transport = config->underlying_io_transport; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_008: [`config->is_trace_on` shall be saved on `instance->is_trace_on`] + instance->is_trace_on = config->is_trace_on; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_060: [`config->on_state_changed_callback` shall be saved on `instance->on_state_changed_callback`] + instance->on_state_changed_callback = config->on_state_changed_callback; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_061: [`config->on_state_changed_context` shall be saved on `instance->on_state_changed_context`] + instance->on_state_changed_context = config->on_state_changed_context; + instance->has_sasl_mechanism = config->create_sasl_io; + instance->has_cbs = config->create_cbs_connection; + + instance->svc2cl_keep_alive_timeout_secs = (uint32_t)config->svc2cl_keep_alive_timeout_secs; + instance->cl2svc_keep_alive_send_ratio = (double)config->cl2svc_keep_alive_send_ratio; + + instance->current_state = AMQP_CONNECTION_STATE_CLOSED; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_011: [If `config->create_sasl_io` is true or `config->create_cbs_connection` is true, amqp_connection_create() shall create SASL I/O] + if ((config->create_sasl_io || config->create_cbs_connection) && create_sasl_components(instance) != RESULT_OK) + { + result = NULL; + LogError("amqp_connection_create failed (failed creating the SASL components)"); + } + else if (create_connection_handle(instance) != RESULT_OK) + { + result = NULL; + LogError("amqp_connection_create failed (failed creating the AMQP connection)"); + } + else if (create_session_handle(instance) != RESULT_OK) + { + result = NULL; + LogError("amqp_connection_create failed (failed creating the AMQP session)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_028: [Only if `config->create_cbs_connection` is true, amqp_connection_create() shall create and open the CBS_HANDLE] + else if (config->create_cbs_connection && create_cbs_handle(instance) != RESULT_OK) + { + result = NULL; + LogError("amqp_connection_create failed (failed creating the CBS handle)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_034: [If no failures occur, amqp_connection_create() shall return the handle to the connection state] + result = (AMQP_CONNECTION_HANDLE)instance; + } + } + + if (result == NULL) + { + amqp_connection_destroy((AMQP_CONNECTION_HANDLE)instance); + } + } + } + + return result; +} + +void amqp_connection_do_work(AMQP_CONNECTION_HANDLE conn_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_041: [If `conn_handle` is NULL, amqp_connection_do_work() shall fail and return] + if (conn_handle != NULL) + { + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_042: [connection_dowork() shall be invoked passing `instance->connection_handle`] + connection_dowork(instance->connection_handle); + } +} + +int amqp_connection_get_session_handle(AMQP_CONNECTION_HANDLE conn_handle, SESSION_HANDLE* session_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_043: [If `conn_handle` is NULL, amqp_connection_get_session_handle() shall fail and return __FAILURE__] + if (conn_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_get_session_handle failed (conn_handle is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_044: [If `session_handle` is NULL, amqp_connection_get_session_handle() shall fail and return __FAILURE__] + else if (session_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_get_session_handle failed (session_handle is NULL)"); + } + else + { + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_045: [`session_handle` shall be set to point to `instance->session_handle`] + *session_handle = instance->session_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_046: [amqp_connection_get_session_handle() shall return success code 0] + result = RESULT_OK; + } + + return result; +} + +int amqp_connection_get_cbs_handle(AMQP_CONNECTION_HANDLE conn_handle, CBS_HANDLE* cbs_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_047: [If `conn_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__] + if (conn_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_get_cbs_handle failed (conn_handle is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_048: [If `cbs_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__] + else if (cbs_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_get_cbs_handle failed (parameter cbs_handle is NULL)"); + } + else + { + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_049: [If `instance->cbs_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__] + if (instance->cbs_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_get_cbs_handle failed (there is not a cbs_handle to be returned)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_050: [`cbs_handle` shall be set to point to `instance->cbs_handle`] + *cbs_handle = instance->cbs_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_051: [amqp_connection_get_cbs_handle() shall return success code 0] + result = RESULT_OK; + } + } + + return result; +} + +int amqp_connection_set_logging(AMQP_CONNECTION_HANDLE conn_handle, bool is_trace_on) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_052: [If `conn_handle` is NULL, amqp_connection_set_logging() shall fail and return __FAILURE__] + if (conn_handle == NULL) + { + result = __FAILURE__; + LogError("amqp_connection_set_logging failed (conn_handle is NULL)"); + } + else + { + AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_053: [`instance->is_trace_on` shall be set to `is_trace_on`] + instance->is_trace_on = is_trace_on; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_054: [Tracing on `instance->sasl_io` shall be set to `instance->is_trace_on` if the value has changed] + if (instance->sasl_io != NULL && + xio_setoption(instance->sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_072: [If xio_setoption() fails, amqp_connection_set_logging() shall fail and return __FAILURE__] + result = __FAILURE__; + LogError("amqp_connection_set_logging failed (xio_setoption() failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_055: [Tracing on `instance->connection_handle` shall be set to `instance->is_trace_on` if the value has changed] + connection_set_trace(instance->connection_handle, instance->is_trace_on); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_056: [amqp_connection_set_logging() shall return success code 0] + result = RESULT_OK; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_device.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1670 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "internal/iothubtransport_amqp_cbs_auth.h" +#include "internal/iothubtransport_amqp_device.h" +#include "internal/iothubtransport_amqp_telemetry_messenger.h" +#include "internal/iothubtransport_amqp_twin_messenger.h" + +DEFINE_ENUM_STRINGS(DEVICE_STATE, DEVICE_STATE_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_AUTH_MODE, DEVICE_AUTH_MODE_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_SEND_STATUS, DEVICE_SEND_STATUS_VALUES); +DEFINE_ENUM_STRINGS(D2C_EVENT_SEND_RESULT, D2C_EVENT_SEND_RESULT_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_MESSAGE_DISPOSITION_RESULT, DEVICE_MESSAGE_DISPOSITION_RESULT_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_TWIN_UPDATE_RESULT, DEVICE_TWIN_UPDATE_RESULT_STRINGS); +DEFINE_ENUM_STRINGS(DEVICE_TWIN_UPDATE_TYPE, DEVICE_TWIN_UPDATE_TYPE_STRINGS) + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)-1) +#define DEFAULT_AUTH_STATE_CHANGED_TIMEOUT_SECS 60 +#define DEFAULT_MSGR_STATE_CHANGED_TIMEOUT_SECS 60 + +static const char* DEVICE_OPTION_SAVED_AUTH_OPTIONS = "saved_device_auth_options"; +static const char* DEVICE_OPTION_SAVED_MESSENGER_OPTIONS = "saved_device_messenger_options"; + +typedef struct DEVICE_INSTANCE_TAG +{ + DEVICE_CONFIG* config; + DEVICE_STATE state; + + SESSION_HANDLE session_handle; + CBS_HANDLE cbs_handle; + + AUTHENTICATION_HANDLE authentication_handle; + AUTHENTICATION_STATE auth_state; + AUTHENTICATION_ERROR_CODE auth_error_code; + time_t auth_state_last_changed_time; + size_t auth_state_change_timeout_secs; + + TELEMETRY_MESSENGER_HANDLE messenger_handle; + TELEMETRY_MESSENGER_STATE msgr_state; + time_t msgr_state_last_changed_time; + size_t msgr_state_change_timeout_secs; + + ON_DEVICE_C2D_MESSAGE_RECEIVED on_message_received_callback; + void* on_message_received_context; + + TWIN_MESSENGER_HANDLE twin_messenger_handle; + TWIN_MESSENGER_STATE twin_msgr_state; + time_t twin_msgr_state_last_changed_time; + size_t twin_msgr_state_change_timeout_secs; + DEVICE_TWIN_UPDATE_RECEIVED_CALLBACK on_device_twin_update_received_callback; + void* on_device_twin_update_received_context; +} AMQP_DEVICE_INSTANCE; + +typedef struct DEVICE_SEND_EVENT_TASK_TAG +{ + ON_DEVICE_D2C_EVENT_SEND_COMPLETE on_event_send_complete_callback; + void* on_event_send_complete_context; +} DEVICE_SEND_EVENT_TASK; + +typedef struct DEVICE_SEND_TWIN_UPDATE_CONTEXT_TAG +{ + DEVICE_SEND_TWIN_UPDATE_COMPLETE_CALLBACK on_send_twin_update_complete_callback; + void* context; +} DEVICE_SEND_TWIN_UPDATE_CONTEXT; + +// Internal state control +static void update_state(AMQP_DEVICE_INSTANCE* instance, DEVICE_STATE new_state) +{ + if (new_state != instance->state) + { + DEVICE_STATE previous_state = instance->state; + instance->state = new_state; + + if (instance->config->on_state_changed_callback != NULL) + { + instance->config->on_state_changed_callback(instance->config->on_state_changed_context, previous_state, new_state); + } + } +} + +static int is_timeout_reached(time_t start_time, size_t timeout_in_secs, int *is_timed_out) +{ + int result; + + if (start_time == INDEFINITE_TIME) + { + LogError("Failed to verify timeout (start_time is INDEFINITE)"); + result = __FAILURE__; + } + else + { + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed to verify timeout (get_time failed)"); + result = __FAILURE__; + } + else + { + if (get_difftime(current_time, start_time) >= timeout_in_secs) + { + *is_timed_out = 1; + } + else + { + *is_timed_out = 0; + } + + result = RESULT_OK; + } + } + + return result; +} + + +//---------- Callback Handlers ----------// + +static D2C_EVENT_SEND_RESULT get_d2c_event_send_result_from(TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT result) +{ + D2C_EVENT_SEND_RESULT d2c_esr; + + switch (result) + { + case TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK: + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_OK; + break; + case TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE: + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE; + break; + case TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING: + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING; + break; + case TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT: + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT; + break; + case TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED: + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED; + break; + default: + // This is not expected. All states should be mapped. + d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN; + break; + }; + + return d2c_esr; +} + +static void on_event_send_complete_messenger_callback(IOTHUB_MESSAGE_LIST* iothub_message, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT ev_send_comp_result, void* context) +{ + if (iothub_message == NULL || context == NULL) + { + LogError("on_event_send_complete_messenger_callback was invoked, but either iothub_message (%p) or context (%p) are NULL", iothub_message, context); + } + else + { + DEVICE_SEND_EVENT_TASK* send_task = (DEVICE_SEND_EVENT_TASK*)context; + + // Codes_SRS_DEVICE_09_059: [If `ev_send_comp_result` is TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK, D2C_EVENT_SEND_COMPLETE_RESULT_OK shall be reported as `event_send_complete`] + // Codes_SRS_DEVICE_09_060: [If `ev_send_comp_result` is TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE shall be reported as `event_send_complete`] + // Codes_SRS_DEVICE_09_061: [If `ev_send_comp_result` is TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING shall be reported as `event_send_complete`] + // Codes_SRS_DEVICE_09_062: [If `ev_send_comp_result` is TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT shall be reported as `event_send_complete`] + // Codes_SRS_DEVICE_09_063: [If `ev_send_comp_result` is TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED, D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED shall be reported as `event_send_complete`] + D2C_EVENT_SEND_RESULT device_send_result = get_d2c_event_send_result_from(ev_send_comp_result); + + // Codes_SRS_DEVICE_09_064: [If provided, the user callback and context saved in `send_task` shall be invoked passing the device `event_send_complete`] + if (send_task->on_event_send_complete_callback != NULL) + { + send_task->on_event_send_complete_callback(iothub_message, device_send_result, send_task->on_event_send_complete_context); + } + + // Codes_SRS_DEVICE_09_065: [The memory allocated for `send_task` shall be released] + free(send_task); + } +} + +static void on_authentication_error_callback(void* context, AUTHENTICATION_ERROR_CODE error_code) +{ + if (context == NULL) + { + LogError("on_authentication_error_callback was invoked with error %d, but context is NULL", error_code); + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)context; + instance->auth_error_code = error_code; + } +} + +static void on_authentication_state_changed_callback(void* context, AUTHENTICATION_STATE previous_state, AUTHENTICATION_STATE new_state) +{ + if (context == NULL) + { + LogError("on_authentication_state_changed_callback was invoked with new_state %d, but context is NULL", new_state); + } + else if (new_state != previous_state) + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)context; + instance->auth_state = new_state; + + if ((instance->auth_state_last_changed_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Device '%s' failed to set time of last authentication state change (get_time failed)", instance->config->device_id); + } + } +} + +static void on_messenger_state_changed_callback(void* context, TELEMETRY_MESSENGER_STATE previous_state, TELEMETRY_MESSENGER_STATE new_state) +{ + if (context == NULL) + { + LogError("on_messenger_state_changed_callback was invoked with new_state %d, but context is NULL", new_state); + } + else if (new_state != previous_state) + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)context; + instance->msgr_state = new_state; + + if ((instance->msgr_state_last_changed_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Device '%s' failed to set time of last messenger state change (get_time failed)", instance->config->device_id); + } + } +} + +static DEVICE_TWIN_UPDATE_RESULT get_device_twin_update_result_from(TWIN_REPORT_STATE_RESULT result) +{ + DEVICE_TWIN_UPDATE_RESULT device_result; + + switch (result) + { + case TWIN_REPORT_STATE_RESULT_SUCCESS: + device_result = DEVICE_TWIN_UPDATE_RESULT_OK; + break; + case TWIN_REPORT_STATE_RESULT_ERROR: + device_result = DEVICE_TWIN_UPDATE_RESULT_ERROR; + break; + default: + device_result = DEVICE_TWIN_UPDATE_RESULT_ERROR; + }; + + return device_result; +} + +static void on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT result, TWIN_REPORT_STATE_REASON reason, int status_code, const void* context) +{ + (void)reason; + + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else + { + DEVICE_SEND_TWIN_UPDATE_CONTEXT* twin_ctx = (DEVICE_SEND_TWIN_UPDATE_CONTEXT*)context; + + // Codes_SRS_DEVICE_09_141: [on_send_twin_update_complete_callback (if provided by user) shall be invoked passing the corresponding device result and `status_code`] + if (twin_ctx->on_send_twin_update_complete_callback != NULL) + { + DEVICE_TWIN_UPDATE_RESULT device_result; + + device_result = get_device_twin_update_result_from(result); + + twin_ctx->on_send_twin_update_complete_callback(device_result, status_code, twin_ctx->context); + } + + // Codes_SRS_DEVICE_09_142: [Memory allocated for `context` shall be released] + free(twin_ctx); + } +} + +static void on_twin_state_update_callback(TWIN_UPDATE_TYPE update_type, const char* payload, size_t size, const void* context) +{ + if (payload == NULL || context == NULL) + { + LogError("Invalid argument (context=%p, payload=%p)", context, payload); + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)context; + + DEVICE_TWIN_UPDATE_TYPE device_update_type; + + if (update_type == TWIN_UPDATE_TYPE_COMPLETE) + { + device_update_type = DEVICE_TWIN_UPDATE_TYPE_COMPLETE; + } + else + { + device_update_type = DEVICE_TWIN_UPDATE_TYPE_PARTIAL; + } + + // Codes_SRS_DEVICE_09_151: [on_device_twin_update_received_callback (provided by user) shall be invoked passing the corresponding update type, `payload` and `size`] + instance->on_device_twin_update_received_callback(device_update_type, (const unsigned char*)payload, size, instance->on_device_twin_update_received_context); + } +} + +static void on_twin_messenger_state_changed_callback(void* context, TWIN_MESSENGER_STATE previous_state, TWIN_MESSENGER_STATE new_state) +{ + if (context != NULL && new_state != previous_state) + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)context; + instance->twin_msgr_state = new_state; + + if ((instance->twin_msgr_state_last_changed_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed setting time of last twin messenger state changed event"); + } + } +} + + +//---------- Message Dispostion ----------// + +static DEVICE_MESSAGE_DISPOSITION_INFO* create_device_message_disposition_info_from(TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info) +{ + DEVICE_MESSAGE_DISPOSITION_INFO* device_disposition_info; + + if ((device_disposition_info = (DEVICE_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(DEVICE_MESSAGE_DISPOSITION_INFO))) == NULL) + { + LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (malloc failed)"); + } + else if (mallocAndStrcpy_s(&device_disposition_info->source, messenger_disposition_info->source) != RESULT_OK) + { + LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)"); + free(device_disposition_info); + device_disposition_info = NULL; + } + else + { + device_disposition_info->message_id = messenger_disposition_info->message_id; + } + + return device_disposition_info; +} + +static void destroy_device_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info) +{ + free(disposition_info->source); + free(disposition_info); +} + +static TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* create_messenger_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* device_disposition_info) +{ + TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info; + + if ((messenger_disposition_info = (TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO))) == NULL) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO (malloc failed)"); + } + else if (mallocAndStrcpy_s(&messenger_disposition_info->source, device_disposition_info->source) != RESULT_OK) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)"); + free(messenger_disposition_info); + messenger_disposition_info = NULL; + } + else + { + messenger_disposition_info->message_id = (delivery_number)device_disposition_info->message_id; + } + + return messenger_disposition_info; +} + +static void destroy_messenger_disposition_info(TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info) +{ + free(messenger_disposition_info->source); + free(messenger_disposition_info); +} + +static TELEMETRY_MESSENGER_DISPOSITION_RESULT get_messenger_message_disposition_result_from(DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result) +{ + TELEMETRY_MESSENGER_DISPOSITION_RESULT messenger_disposition_result; + + switch (device_disposition_result) + { + case DEVICE_MESSAGE_DISPOSITION_RESULT_NONE: + messenger_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_NONE; + break; + case DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED: + messenger_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_ACCEPTED; + break; + case DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED: + messenger_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_REJECTED; + break; + case DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED: + messenger_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED; + break; + default: + LogError("Failed to get the corresponding TELEMETRY_MESSENGER_DISPOSITION_RESULT (%d is not supported)", device_disposition_result); + messenger_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED; + break; + } + + return messenger_disposition_result; +} + +static TELEMETRY_MESSENGER_DISPOSITION_RESULT on_messenger_message_received_callback(IOTHUB_MESSAGE_HANDLE iothub_message_handle, TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context) +{ + TELEMETRY_MESSENGER_DISPOSITION_RESULT msgr_disposition_result; + + // Codes_SRS_DEVICE_09_070: [If `iothub_message_handle` or `context` is NULL, on_messenger_message_received_callback shall return TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED] + if (iothub_message_handle == NULL || context == NULL) + { + LogError("Failed receiving incoming C2D message (message handle (%p) or context (%p) is NULL)", iothub_message_handle, context); + msgr_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED; + } + else + { + AMQP_DEVICE_INSTANCE* device_instance = (AMQP_DEVICE_INSTANCE*)context; + + if (device_instance->on_message_received_callback == NULL) + { + LogError("Device '%s' failed receiving incoming C2D message (callback is NULL)", device_instance->config->device_id); + msgr_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED; + } + else + { + DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info; + + // Codes_SRS_DEVICE_09_119: [A DEVICE_MESSAGE_DISPOSITION_INFO instance shall be created containing a copy of `disposition_info->source` and `disposition_info->message_id`] + if ((device_message_disposition_info = create_device_message_disposition_info_from(disposition_info)) == NULL) + { + // Codes_SRS_DEVICE_09_120: [If the DEVICE_MESSAGE_DISPOSITION_INFO instance fails to be created, on_messenger_message_received_callback shall return TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED] + LogError("Device '%s' failed receiving incoming C2D message (failed creating DEVICE_MESSAGE_DISPOSITION_INFO)", device_instance->config->device_id); + msgr_disposition_result = TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED; + } + else + { + // Codes_SRS_DEVICE_09_071: [The user callback shall be invoked, passing the context it provided] + DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result = device_instance->on_message_received_callback(iothub_message_handle, device_message_disposition_info, device_instance->on_message_received_context); + + // Codes_SRS_DEVICE_09_072: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED, on_messenger_message_received_callback shall return TELEMETRY_MESSENGER_DISPOSITION_RESULT_ACCEPTED] + // Codes_SRS_DEVICE_09_073: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED, on_messenger_message_received_callback shall return TELEMETRY_MESSENGER_DISPOSITION_RESULT_REJECTED] + // Codes_SRS_DEVICE_09_074: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED, on_messenger_message_received_callback shall return TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED] + msgr_disposition_result = get_messenger_message_disposition_result_from(device_disposition_result); + + // Codes_SRS_DEVICE_09_121: [on_messenger_message_received_callback shall release the memory allocated for DEVICE_MESSAGE_DISPOSITION_INFO] + destroy_device_disposition_info(device_message_disposition_info); + } + } + } + + return msgr_disposition_result; +} + + +//---------- Configuration Helpers ----------// + +static void destroy_device_config(DEVICE_CONFIG* config) +{ + if (config != NULL) + { + free(config->product_info); + free(config->iothub_host_fqdn); + free(config); + } +} + +static DEVICE_CONFIG* clone_device_config(DEVICE_CONFIG *config) +{ + DEVICE_CONFIG* new_config; + + if ((new_config = (DEVICE_CONFIG*)malloc(sizeof(DEVICE_CONFIG))) == NULL) + { + LogError("Failed copying the DEVICE_CONFIG (malloc failed)"); + } + else + { + int result; + memset(new_config, 0, sizeof(DEVICE_CONFIG)); + + if (config->product_info != NULL && + mallocAndStrcpy_s(&new_config->product_info, config->product_info) != RESULT_OK) + { + LogError("Failed copying the DEVICE_CONFIG (failed copying product_info)"); + result = __FAILURE__; + } + else if (mallocAndStrcpy_s(&new_config->iothub_host_fqdn, config->iothub_host_fqdn) != RESULT_OK) + { + LogError("Failed copying the DEVICE_CONFIG (failed copying iothub_host_fqdn)"); + result = __FAILURE__; + } + else + { + new_config->authorization_module = config->authorization_module; + new_config->authentication_mode = config->authentication_mode; + new_config->on_state_changed_callback = config->on_state_changed_callback; + new_config->on_state_changed_context = config->on_state_changed_context; + new_config->device_id = IoTHubClient_Auth_Get_DeviceId(config->authorization_module); + new_config->module_id = IoTHubClient_Auth_Get_ModuleId(config->authorization_module); + result = RESULT_OK; + } + + if (result != RESULT_OK) + { + destroy_device_config(new_config); + new_config = NULL; + } + } + + return new_config; +} + +static void set_authentication_config(AMQP_DEVICE_INSTANCE* device_instance, AUTHENTICATION_CONFIG* auth_config) +{ + DEVICE_CONFIG *device_config = device_instance->config; + + auth_config->device_id = device_config->device_id; + auth_config->module_id = device_config->module_id; + auth_config->iothub_host_fqdn = device_config->iothub_host_fqdn; + auth_config->on_error_callback = on_authentication_error_callback; + auth_config->on_error_callback_context = device_instance; + auth_config->on_state_changed_callback = on_authentication_state_changed_callback; + auth_config->on_state_changed_callback_context = device_instance; + auth_config->authorization_module = device_config->authorization_module; +} + +// Create and Destroy Helpers +static void internal_destroy_device(AMQP_DEVICE_INSTANCE* instance) +{ + if (instance != NULL) + { + if (instance->messenger_handle != NULL) + { + telemetry_messenger_destroy(instance->messenger_handle); + } + + if (instance->twin_messenger_handle != NULL) + { + twin_messenger_destroy(instance->twin_messenger_handle); + } + + if (instance->authentication_handle != NULL) + { + authentication_destroy(instance->authentication_handle); + } + + destroy_device_config(instance->config); + free(instance); + } +} + +static int create_authentication_instance(AMQP_DEVICE_INSTANCE *instance) +{ + int result; + AUTHENTICATION_CONFIG auth_config; + + set_authentication_config(instance, &auth_config); + + if ((instance->authentication_handle = authentication_create(&auth_config)) == NULL) + { + LogError("Failed creating the AUTHENTICATION_HANDLE (authentication_create failed)"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static int create_telemetry_messenger_instance(AMQP_DEVICE_INSTANCE* instance, const char* pi) +{ + int result; + + TELEMETRY_MESSENGER_CONFIG messenger_config; + messenger_config.device_id = instance->config->device_id; + messenger_config.module_id = instance->config->module_id; + messenger_config.iothub_host_fqdn = instance->config->iothub_host_fqdn; + messenger_config.on_state_changed_callback = on_messenger_state_changed_callback; + messenger_config.on_state_changed_context = instance; + + if ((instance->messenger_handle = telemetry_messenger_create(&messenger_config, pi)) == NULL) + { + LogError("Failed creating the TELEMETRY_MESSENGER_HANDLE (messenger_create failed)"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static int create_twin_messenger(AMQP_DEVICE_INSTANCE* instance) +{ + int result; + TWIN_MESSENGER_CONFIG twin_msgr_config; + + twin_msgr_config.client_version = instance->config->product_info; + twin_msgr_config.device_id = instance->config->device_id; + twin_msgr_config.module_id = instance->config->module_id; + twin_msgr_config.iothub_host_fqdn = instance->config->iothub_host_fqdn; + twin_msgr_config.on_state_changed_callback = on_twin_messenger_state_changed_callback; + twin_msgr_config.on_state_changed_context = (void*)instance; + + if ((instance->twin_messenger_handle = twin_messenger_create(&twin_msgr_config)) == NULL) + { + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + + +// ---------- Set/Retrieve Options Helpers ----------// + +static void* device_clone_option(const char* name, const void* value) +{ + void* result; + + if (name == NULL || value == NULL) + { + LogError("Failed to clone device option (either name (%p) or value (%p) is NULL)", name, value); + result = NULL; + } + else + { + if (strcmp(DEVICE_OPTION_SAVED_AUTH_OPTIONS, name) == 0 || + strcmp(DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, name) == 0) + { + if ((result = (void*)OptionHandler_Clone((OPTIONHANDLER_HANDLE)value)) == NULL) + { + LogError("Failed to clone device option (OptionHandler_Clone failed for option %s)", name); + } + } + else + { + LogError("Failed to clone device option (option with name '%s' is not suppported)", name); + result = NULL; + } + } + + return result; +} + +static void device_destroy_option(const char* name, const void* value) +{ + if (name == NULL || value == NULL) + { + LogError("Failed to destroy device option (either name (%p) or value (%p) is NULL)", name, value); + } + else + { + if (strcmp(name, DEVICE_OPTION_SAVED_AUTH_OPTIONS) == 0 || + strcmp(name, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS) == 0) + { + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + LogError("Failed to clone device option (option with name '%s' is not suppported)", name); + } + } +} + + +//---------- Public APIs ----------// + +AMQP_DEVICE_HANDLE device_create(DEVICE_CONFIG *config) +{ + AMQP_DEVICE_INSTANCE *instance; + + // Codes_SRS_DEVICE_09_001: [If config, authorization_module or iothub_host_fqdn or on_state_changed_callback are NULL then device_create shall fail and return NULL] + if (config == NULL) + { + LogError("Failed creating the device instance (config is NULL)"); + instance = NULL; + } + else if (config->iothub_host_fqdn == NULL) + { + LogError("Failed creating the device instance (config->iothub_host_fqdn is NULL)"); + instance = NULL; + } + else if (config->on_state_changed_callback == NULL) + { + LogError("Failed creating the device instance (config->on_state_changed_callback is NULL)"); + instance = NULL; + } + else if (config->authorization_module == NULL) + { + LogError("Failed creating the device instance (config->authorization_module is NULL)"); + instance = NULL; + } + // Codes_SRS_DEVICE_09_002: [device_create shall allocate memory for the device instance structure] + else if ((instance = (AMQP_DEVICE_INSTANCE*)malloc(sizeof(AMQP_DEVICE_INSTANCE))) == NULL) + { + // Codes_SRS_DEVICE_09_003: [If malloc fails, device_create shall fail and return NULL] + LogError("Failed creating the device instance (malloc failed)"); + } + else + { + int result; + + memset(instance, 0, sizeof(AMQP_DEVICE_INSTANCE)); + + // Codes_SRS_DEVICE_09_004: [All `config` parameters shall be saved into `instance`] + if ((instance->config = clone_device_config(config)) == NULL) + { + // Codes_SRS_DEVICE_09_005: [If any `config` parameters fail to be saved into `instance`, device_create shall fail and return NULL] + LogError("Failed creating the device instance for device '%s' (failed copying the configuration)", config->device_id); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_006: [If `instance->authentication_mode` is DEVICE_AUTH_MODE_CBS, `instance->authentication_handle` shall be set using authentication_create()] + else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS && + create_authentication_instance(instance) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_007: [If the AUTHENTICATION_HANDLE fails to be created, device_create shall fail and return NULL] + LogError("Failed creating the device instance for device '%s' (failed creating the authentication instance)", instance->config->device_id); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_008: [`instance->messenger_handle` shall be set using telemetry_messenger_create()] + else if (create_telemetry_messenger_instance(instance, config->product_info) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_009: [If the TELEMETRY_MESSENGER_HANDLE fails to be created, device_create shall fail and return NULL] + LogError("Failed creating the device instance for device '%s' (failed creating the messenger instance)", instance->config->device_id); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_122: [`instance->twin_messenger_handle` shall be set using twin_messenger_create()] + else if (create_twin_messenger(instance) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_123: [If the TWIN_MESSENGER_HANDLE fails to be created, device_create shall fail and return NULL] + LogError("Failed creating the twin messenger for device '%s'", instance->config->device_id); + result = __FAILURE__; + } + else + { + instance->auth_state = AUTHENTICATION_STATE_STOPPED; + instance->msgr_state = TELEMETRY_MESSENGER_STATE_STOPPED; + instance->twin_msgr_state = TWIN_MESSENGER_STATE_STOPPED; + instance->state = DEVICE_STATE_STOPPED; + instance->auth_state_last_changed_time = INDEFINITE_TIME; + instance->auth_state_change_timeout_secs = DEFAULT_AUTH_STATE_CHANGED_TIMEOUT_SECS; + instance->msgr_state_last_changed_time = INDEFINITE_TIME; + instance->msgr_state_change_timeout_secs = DEFAULT_MSGR_STATE_CHANGED_TIMEOUT_SECS; + instance->twin_msgr_state_last_changed_time = INDEFINITE_TIME; + instance->twin_msgr_state_change_timeout_secs = DEFAULT_MSGR_STATE_CHANGED_TIMEOUT_SECS; + + result = RESULT_OK; + } + + if (result != RESULT_OK) + { + // Codes_SRS_DEVICE_09_010: [If device_create fails it shall release all memory it has allocated] + internal_destroy_device(instance); + instance = NULL; + } + } + + // Codes_SRS_DEVICE_09_011: [If device_create succeeds it shall return a handle to its `instance` structure] + return (AMQP_DEVICE_HANDLE)instance; +} + +int device_start_async(AMQP_DEVICE_HANDLE handle, SESSION_HANDLE session_handle, CBS_HANDLE cbs_handle) +{ + int result; + + // Codes_SRS_DEVICE_09_017: [If `handle` is NULL, device_start_async shall return a non-zero result] + if (handle == NULL) + { + LogError("Failed starting device (handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_018: [If the device state is not DEVICE_STATE_STOPPED, device_start_async shall return a non-zero result] + if (instance->state != DEVICE_STATE_STOPPED) + { + LogError("Failed starting device (device is not stopped)"); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_019: [If `session_handle` is NULL, device_start_async shall return a non-zero result] + else if (session_handle == NULL) + { + LogError("Failed starting device (session_handle is NULL)"); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_020: [If using CBS authentication and `cbs_handle` is NULL, device_start_async shall return a non-zero result] + else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS && cbs_handle == NULL) + { + LogError("Failed starting device (device using CBS authentication, but cbs_handle is NULL)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_021: [`session_handle` and `cbs_handle` shall be saved into the `instance`] + instance->session_handle = session_handle; + instance->cbs_handle = cbs_handle; + + // Codes_SRS_DEVICE_09_022: [The device state shall be updated to DEVICE_STATE_STARTING, and state changed callback invoked] + update_state(instance, DEVICE_STATE_STARTING); + + // Codes_SRS_DEVICE_09_023: [If no failures occur, device_start_async shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +// @brief +// stops a device instance (stops messenger and authentication) synchronously. +// @returns +// 0 if the function succeeds, non-zero otherwise. +int device_stop(AMQP_DEVICE_HANDLE handle) +{ + int result; + + // Codes_SRS_DEVICE_09_024: [If `handle` is NULL, device_stop shall return a non-zero result] + if (handle == NULL) + { + LogError("Failed stopping device (handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_025: [If the device state is already DEVICE_STATE_STOPPED or DEVICE_STATE_STOPPING, device_stop shall return a non-zero result] + if (instance->state == DEVICE_STATE_STOPPED || instance->state == DEVICE_STATE_STOPPING) + { + LogError("Failed stopping device '%s' (device is already stopped or stopping)", instance->config->device_id); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_026: [The device state shall be updated to DEVICE_STATE_STOPPING, and state changed callback invoked] + update_state(instance, DEVICE_STATE_STOPPING); + + // Codes_SRS_DEVICE_09_027: [If `instance->messenger_handle` state is not TELEMETRY_MESSENGER_STATE_STOPPED, messenger_stop shall be invoked] + if (instance->msgr_state != TELEMETRY_MESSENGER_STATE_STOPPED && + instance->msgr_state != TELEMETRY_MESSENGER_STATE_STOPPING && + telemetry_messenger_stop(instance->messenger_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_028: [If messenger_stop fails, the `instance` state shall be updated to DEVICE_STATE_ERROR_MSG and the function shall return non-zero result] + LogError("Failed stopping device '%s' (telemetry_messenger_stop failed)", instance->config->device_id); + result = __FAILURE__; + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_131: [If `instance->twin_messenger_handle` state is not TWIN_MESSENGER_STATE_STOPPED, twin_messenger_stop shall be invoked] + else if (instance->twin_msgr_state != TWIN_MESSENGER_STATE_STOPPED && + instance->twin_msgr_state != TWIN_MESSENGER_STATE_STOPPING && + twin_messenger_stop(instance->twin_messenger_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_132: [If twin_messenger_stop fails, the `instance` state shall be updated to DEVICE_STATE_ERROR_MSG and the function shall return non-zero result] + LogError("Failed stopping device '%s' (twin_messenger_stop failed)", instance->config->device_id); + result = __FAILURE__; + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_029: [If CBS authentication is used, if `instance->authentication_handle` state is not AUTHENTICATION_STATE_STOPPED, authentication_stop shall be invoked] + else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS && + instance->auth_state != AUTHENTICATION_STATE_STOPPED && + authentication_stop(instance->authentication_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_030: [If authentication_stop fails, the `instance` state shall be updated to DEVICE_STATE_ERROR_AUTH and the function shall return non-zero result] + LogError("Failed stopping device '%s' (authentication_stop failed)", instance->config->device_id); + result = __FAILURE__; + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + else + { + // Codes_SRS_DEVICE_09_031: [The device state shall be updated to DEVICE_STATE_STOPPED, and state changed callback invoked] + update_state(instance, DEVICE_STATE_STOPPED); + + // Codes_SRS_DEVICE_09_032: [If no failures occur, device_stop shall return 0] + result = RESULT_OK; + } + } + } + + return result; +} + +void device_do_work(AMQP_DEVICE_HANDLE handle) +{ + // Codes_SRS_DEVICE_09_033: [If `handle` is NULL, device_do_work shall return] + if (handle == NULL) + { + LogError("Failed to perform device_do_work (handle is NULL)"); + } + else + { + // Cranking the state monster: + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + if (instance->state == DEVICE_STATE_STARTING) + { + // Codes_SRS_DEVICE_09_034: [If CBS authentication is used and authentication state is AUTHENTICATION_STATE_STOPPED, authentication_start shall be invoked] + if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS) + { + if (instance->auth_state == AUTHENTICATION_STATE_STOPPED) + { + if (authentication_start(instance->authentication_handle, instance->cbs_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_035: [If authentication_start fails, the device state shall be updated to DEVICE_STATE_ERROR_AUTH] + LogError("Device '%s' failed to be authenticated (authentication_start failed)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + } + // Codes_SRS_DEVICE_09_036: [If authentication state is AUTHENTICATION_STATE_STARTING, the device shall track the time since last event change and timeout if needed] + else if (instance->auth_state == AUTHENTICATION_STATE_STARTING) + { + int is_timed_out; + if (is_timeout_reached(instance->auth_state_last_changed_time, instance->auth_state_change_timeout_secs, &is_timed_out) != RESULT_OK) + { + LogError("Device '%s' failed verifying the timeout for authentication start (is_timeout_reached failed)", instance->config->device_id); + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + // Codes_SRS_DEVICE_09_037: [If authentication_start times out, the device state shall be updated to DEVICE_STATE_ERROR_AUTH_TIMEOUT] + else if (is_timed_out == 1) + { + LogError("Device '%s' authentication did not complete starting within expected timeout (%d)", instance->config->device_id, instance->auth_state_change_timeout_secs); + + update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT); + } + } + else if (instance->auth_state == AUTHENTICATION_STATE_ERROR) + { + // Codes_SRS_DEVICE_09_038: [If authentication state is AUTHENTICATION_STATE_ERROR and error code is AUTH_FAILED, the device state shall be updated to DEVICE_STATE_ERROR_AUTH] + if (instance->auth_error_code == AUTHENTICATION_ERROR_AUTH_FAILED) + { + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + // Codes_SRS_DEVICE_09_039: [If authentication state is AUTHENTICATION_STATE_ERROR and error code is TIMEOUT, the device state shall be updated to DEVICE_STATE_ERROR_AUTH_TIMEOUT] + else // DEVICE_STATE_ERROR_TIMEOUT + { + update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT); + } + } + // There is no AUTHENTICATION_STATE_STOPPING + } + + // Codes_SRS_DEVICE_09_040: [Messenger shall not be started if using CBS authentication and authentication start has not completed yet] + // Codes_SRS_DEVICE_09_124: [TWIN Messenger shall not be started if using CBS authentication and authentication start has not completed yet] + if (instance->config->authentication_mode == DEVICE_AUTH_MODE_X509 || instance->auth_state == AUTHENTICATION_STATE_STARTED) + { + size_t number_of_messengers_started = 0; + + // Codes_SRS_DEVICE_09_041: [If messenger state is TELEMETRY_MESSENGER_STATE_STOPPED, messenger_start shall be invoked] + if (instance->msgr_state == TELEMETRY_MESSENGER_STATE_STOPPED) + { + // Codes_SRS_DEVICE_09_042: [If messenger_start fails, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + if (telemetry_messenger_start(instance->messenger_handle, instance->session_handle) != RESULT_OK) + { + LogError("Device '%s' messenger failed to be started (messenger_start failed)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + } + // Codes_SRS_DEVICE_09_043: [If messenger state is TELEMETRY_MESSENGER_STATE_STARTING, the device shall track the time since last event change and timeout if needed] + else if (instance->msgr_state == TELEMETRY_MESSENGER_STATE_STARTING) + { + int is_timed_out; + if (is_timeout_reached(instance->msgr_state_last_changed_time, instance->msgr_state_change_timeout_secs, &is_timed_out) != RESULT_OK) + { + LogError("Device '%s' failed verifying the timeout for messenger start (is_timeout_reached failed)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_044: [If messenger_start times out, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + else if (is_timed_out == 1) + { + LogError("Device '%s' messenger did not complete starting within expected timeout (%d)", instance->config->device_id, instance->msgr_state_change_timeout_secs); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + } + // Codes_SRS_DEVICE_09_045: [If messenger state is TELEMETRY_MESSENGER_STATE_ERROR, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + else if (instance->msgr_state == TELEMETRY_MESSENGER_STATE_ERROR) + { + LogError("Device '%s' messenger failed to be started (messenger got into error state)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_046: [If messenger state is TELEMETRY_MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_STARTED] + else if (instance->msgr_state == TELEMETRY_MESSENGER_STATE_STARTED) + { + number_of_messengers_started++; + } + + // Codes_SRS_DEVICE_09_125: [If TWIN messenger state is TWIN_MESSENGER_STATE_STOPPED, twin_messenger_start shall be invoked] + if (instance->twin_msgr_state == TWIN_MESSENGER_STATE_STOPPED) + { + if (twin_messenger_start(instance->twin_messenger_handle, instance->session_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_126: [If twin_messenger_start fails, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + LogError("Device '%s' twin messenger failed to be started (messenger_start failed)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + } + else if (instance->twin_msgr_state == TWIN_MESSENGER_STATE_STARTING) + { + int is_timed_out; + if (is_timeout_reached(instance->twin_msgr_state_last_changed_time, instance->twin_msgr_state_change_timeout_secs, &is_timed_out) != RESULT_OK) + { + LogError("Device '%s' failed verifying the timeout for twin messenger start (is_timeout_reached failed)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_127: [If TWIN messenger state is TWIN_MESSENGER_STATE_STARTING, the device shall track the time since last event change and timeout if needed] + else if (is_timed_out == 1) + { + // Codes_SRS_DEVICE_09_128: [If twin_messenger_start times out, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + LogError("Device '%s' twin messenger did not complete starting within expected timeout (%d)", instance->config->device_id, instance->twin_msgr_state_change_timeout_secs); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + } + // Codes_SRS_DEVICE_09_129: [If TWIN messenger state is TWIN_MESSENGER_STATE_ERROR, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + else if (instance->twin_msgr_state == TWIN_MESSENGER_STATE_ERROR) + { + LogError("Device '%s' twin messenger failed to be started (messenger got into error state)", instance->config->device_id); + + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + // Codes_SRS_DEVICE_09_130: [If TWIN messenger state is TWIN_MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_STARTED] + else if (instance->twin_msgr_state == TWIN_MESSENGER_STATE_STARTED) + { + number_of_messengers_started++; + } + + if (number_of_messengers_started == 2) + { + update_state(instance, DEVICE_STATE_STARTED); + } + } + } + else if (instance->state == DEVICE_STATE_STARTED) + { + // Codes_SRS_DEVICE_09_047: [If CBS authentication is used and authentication state is not AUTHENTICATION_STATE_STARTED, the device state shall be updated to DEVICE_STATE_ERROR_AUTH] + if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS && + instance->auth_state != AUTHENTICATION_STATE_STARTED) + { + LogError("Device '%s' is started but authentication reported unexpected state %d", instance->config->device_id, instance->auth_state); + + if (instance->auth_state != AUTHENTICATION_STATE_ERROR) + { + if (instance->auth_error_code == AUTHENTICATION_ERROR_AUTH_FAILED) + { + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + else // AUTHENTICATION_ERROR_AUTH_TIMEOUT + { + update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT); + } + } + else + { + update_state(instance, DEVICE_STATE_ERROR_AUTH); + } + } + else + { + // Codes_SRS_DEVICE_09_048: [If messenger state is not TELEMETRY_MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + if (instance->msgr_state != TELEMETRY_MESSENGER_STATE_STARTED) + { + LogError("Device '%s' is started but messenger reported unexpected state %d", instance->config->device_id, instance->msgr_state); + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + + // Codes_SRS_DEVICE_09_133: [If TWIN messenger state is not TWIN_MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_ERROR_MSG] + if (instance->twin_msgr_state != TWIN_MESSENGER_STATE_STARTED) + { + LogError("Device '%s' is started but TWIN messenger reported unexpected state %d", instance->config->device_id, instance->msgr_state); + update_state(instance, DEVICE_STATE_ERROR_MSG); + } + } + } + + // Invoking the do_works(): + if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS) + { + if (instance->auth_state != AUTHENTICATION_STATE_STOPPED && instance->auth_state != AUTHENTICATION_STATE_ERROR) + { + // Codes_SRS_DEVICE_09_049: [If CBS is used for authentication and `instance->authentication_handle` state is not STOPPED or ERROR, authentication_do_work shall be invoked] + authentication_do_work(instance->authentication_handle); + } + } + + if (instance->msgr_state != TELEMETRY_MESSENGER_STATE_STOPPED && instance->msgr_state != TELEMETRY_MESSENGER_STATE_ERROR) + { + // Codes_SRS_DEVICE_09_050: [If `instance->messenger_handle` state is not STOPPED or ERROR, telemetry_messenger_do_work shall be invoked] + telemetry_messenger_do_work(instance->messenger_handle); + } + + if (instance->twin_msgr_state != TWIN_MESSENGER_STATE_STOPPED && instance->twin_msgr_state != TWIN_MESSENGER_STATE_ERROR) + { + // Codes_SRS_DEVICE_09_134: [If `instance->twin_messenger_handle` state is not STOPPED or ERROR, twin_messenger_do_work shall be invoked] + twin_messenger_do_work(instance->twin_messenger_handle); + } + } +} + +void device_destroy(AMQP_DEVICE_HANDLE handle) +{ + // Codes_SRS_DEVICE_09_012: [If `handle` is NULL, device_destroy shall return] + if (handle == NULL) + { + LogError("Failed destroying device handle (handle is NULL)"); + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + // Codes_SRS_DEVICE_09_013: [If the device is in state DEVICE_STATE_STARTED or DEVICE_STATE_STARTING, device_stop() shall be invoked] + if (instance->state == DEVICE_STATE_STARTED || instance->state == DEVICE_STATE_STARTING) + { + (void)device_stop((AMQP_DEVICE_HANDLE)instance); + } + + // Codes_SRS_DEVICE_09_014: [`instance->messenger_handle shall be destroyed using telemetry_messenger_destroy()`] + // Codes_SRS_DEVICE_09_015: [If created, `instance->authentication_handle` shall be destroyed using authentication_destroy()`] + // Codes_SRS_DEVICE_09_016: [The contents of `instance->config` shall be detroyed and then it shall be freed] + internal_destroy_device((AMQP_DEVICE_INSTANCE*)handle); + } +} + +int device_send_event_async(AMQP_DEVICE_HANDLE handle, IOTHUB_MESSAGE_LIST* message, ON_DEVICE_D2C_EVENT_SEND_COMPLETE on_device_d2c_event_send_complete_callback, void* context) +{ + int result; + + // Codes_SRS_DEVICE_09_051: [If `handle` are `message` are NULL, device_send_event_async shall return a non-zero result] + if (handle == NULL || message == NULL) + { + LogError("Failed sending event (either handle (%p) or message (%p) are NULL)", handle, message); + result = __FAILURE__; + } + else + { + DEVICE_SEND_EVENT_TASK* send_task; + + // Codes_SRS_DEVICE_09_052: [A structure (`send_task`) shall be created to track the send state of the message] + if ((send_task = (DEVICE_SEND_EVENT_TASK*)malloc(sizeof(DEVICE_SEND_EVENT_TASK))) == NULL) + { + // Codes_SRS_DEVICE_09_053: [If `send_task` fails to be created, device_send_event_async shall return a non-zero value] + LogError("Failed sending event (failed creating task to send event)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_054: [`send_task` shall contain the user callback and the context provided] + memset(send_task, 0, sizeof(DEVICE_SEND_EVENT_TASK)); + send_task->on_event_send_complete_callback = on_device_d2c_event_send_complete_callback; + send_task->on_event_send_complete_context = context; + + // Codes_SRS_DEVICE_09_055: [The message shall be sent using telemetry_messenger_send_async, passing `on_event_send_complete_messenger_callback` and `send_task`] + if (telemetry_messenger_send_async(instance->messenger_handle, message, on_event_send_complete_messenger_callback, (void*)send_task) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_056: [If telemetry_messenger_send_async fails, device_send_event_async shall return a non-zero value] + LogError("Failed sending event (telemetry_messenger_send_async failed)"); + // Codes_SRS_DEVICE_09_057: [If any failures occur, device_send_event_async shall release all memory it has allocated] + free(send_task); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_058: [If no failures occur, device_send_event_async shall return 0] + result = RESULT_OK; + } + } + } + + return result; +} + +int device_get_send_status(AMQP_DEVICE_HANDLE handle, DEVICE_SEND_STATUS *send_status) +{ + int result; + + + // Codes_SRS_DEVICE_09_105: [If `handle` or `send_status` is NULL, device_get_send_status shall return a non-zero result] + if (handle == NULL || send_status == NULL) + { + LogError("Failed getting the device messenger send status (NULL parameter received; handle=%p, send_status=%p)", handle, send_status); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + TELEMETRY_MESSENGER_SEND_STATUS messenger_send_status; + + // Codes_SRS_DEVICE_09_106: [The status of `instance->messenger_handle` shall be obtained using telemetry_messenger_get_send_status] + if (telemetry_messenger_get_send_status(instance->messenger_handle, &messenger_send_status) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_107: [If telemetry_messenger_get_send_status fails, device_get_send_status shall return a non-zero result] + LogError("Failed getting the device messenger send status (telemetry_messenger_get_send_status failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_108: [If telemetry_messenger_get_send_status returns TELEMETRY_MESSENGER_SEND_STATUS_IDLE, device_get_send_status return status DEVICE_SEND_STATUS_IDLE] + if (messenger_send_status == TELEMETRY_MESSENGER_SEND_STATUS_IDLE) + { + *send_status = DEVICE_SEND_STATUS_IDLE; + } + // Codes_SRS_DEVICE_09_109: [If telemetry_messenger_get_send_status returns TELEMETRY_MESSENGER_SEND_STATUS_BUSY, device_get_send_status return status DEVICE_SEND_STATUS_BUSY] + else // i.e., messenger_send_status == TELEMETRY_MESSENGER_SEND_STATUS_BUSY + { + *send_status = DEVICE_SEND_STATUS_BUSY; + } + + // Codes_SRS_DEVICE_09_110: [If device_get_send_status succeeds, it shall return zero as result] + result = RESULT_OK; + } + } + + return result; +} + +int device_subscribe_message(AMQP_DEVICE_HANDLE handle, ON_DEVICE_C2D_MESSAGE_RECEIVED on_message_received_callback, void* context) +{ + int result; + + // Codes_SRS_DEVICE_09_066: [If `handle` or `on_message_received_callback` or `context` is NULL, device_subscribe_message shall return a non-zero result] + if (handle == NULL || on_message_received_callback == NULL || context == NULL) + { + LogError("Failed subscribing to C2D messages (either handle (%p), on_message_received_callback (%p) or context (%p) is NULL)", + handle, on_message_received_callback, context); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_067: [telemetry_messenger_subscribe_for_messages shall be invoked passing `on_messenger_message_received_callback` and the user callback and context] + if (telemetry_messenger_subscribe_for_messages(instance->messenger_handle, on_messenger_message_received_callback, handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_068: [If telemetry_messenger_subscribe_for_messages fails, device_subscribe_message shall return a non-zero result] + LogError("Failed subscribing to C2D messages (telemetry_messenger_subscribe_for_messages failed)"); + result = __FAILURE__; + } + else + { + instance->on_message_received_callback = on_message_received_callback; + instance->on_message_received_context = context; + + // Codes_SRS_DEVICE_09_069: [If no failures occur, device_subscribe_message shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int device_unsubscribe_message(AMQP_DEVICE_HANDLE handle) +{ + int result; + + // Codes_SRS_DEVICE_09_076: [If `handle` is NULL, device_unsubscribe_message shall return a non-zero result] + if (handle == NULL) + { + LogError("Failed unsubscribing to C2D messages (handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_077: [telemetry_messenger_unsubscribe_for_messages shall be invoked passing `instance->messenger_handle`] + if (telemetry_messenger_unsubscribe_for_messages(instance->messenger_handle) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_078: [If telemetry_messenger_unsubscribe_for_messages fails, device_unsubscribe_message shall return a non-zero result] + LogError("Failed unsubscribing to C2D messages (telemetry_messenger_unsubscribe_for_messages failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_079: [If no failures occur, device_unsubscribe_message shall return 0] + result = RESULT_OK; + } + } + return result; +} + +int device_send_message_disposition(AMQP_DEVICE_HANDLE device_handle, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, DEVICE_MESSAGE_DISPOSITION_RESULT disposition_result) +{ + int result; + + // Codes_SRS_DEVICE_09_111: [If `device_handle` or `disposition_info` are NULL, device_send_message_disposition() shall fail and return __FAILURE__] + if (device_handle == NULL || disposition_info == NULL) + { + LogError("Failed sending message disposition (either device_handle (%p) or disposition_info (%p) are NULL)", device_handle, disposition_info); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_112: [If `disposition_info->source` is NULL, device_send_message_disposition() shall fail and return __FAILURE__] + else if (disposition_info->source == NULL) + { + LogError("Failed sending message disposition (disposition_info->source is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* device = (AMQP_DEVICE_INSTANCE*)device_handle; + TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info; + + // Codes_SRS_DEVICE_09_113: [A TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be created with a copy of the `source` and `message_id` contained in `disposition_info`] + if ((messenger_disposition_info = create_messenger_disposition_info(disposition_info)) == NULL) + { + // Codes_SRS_DEVICE_09_114: [If the TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO fails to be created, device_send_message_disposition() shall fail and return __FAILURE__] + LogError("Failed sending message disposition (failed to create TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO)"); + result = __FAILURE__; + } + else + { + TELEMETRY_MESSENGER_DISPOSITION_RESULT messenger_disposition_result = get_messenger_message_disposition_result_from(disposition_result); + + // Codes_SRS_DEVICE_09_115: [`telemetry_messenger_send_message_disposition()` shall be invoked passing the TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance and the corresponding TELEMETRY_MESSENGER_DISPOSITION_RESULT] + if (telemetry_messenger_send_message_disposition(device->messenger_handle, messenger_disposition_info, messenger_disposition_result) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_116: [If `telemetry_messenger_send_message_disposition()` fails, device_send_message_disposition() shall fail and return __FAILURE__] + LogError("Failed sending message disposition (telemetry_messenger_send_message_disposition failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_118: [If no failures occurr, device_send_message_disposition() shall return 0] + result = RESULT_OK; + } + + // Codes_SRS_DEVICE_09_117: [device_send_message_disposition() shall destroy the TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance] + destroy_messenger_disposition_info(messenger_disposition_info); + } + } + + return result; +} + +int device_set_retry_policy(AMQP_DEVICE_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY policy, size_t retry_timeout_limit_in_seconds) +{ + (void)retry_timeout_limit_in_seconds; + (void)policy; + int result; + + // Codes_SRS_DEVICE_09_080: [If `handle` is NULL, device_set_retry_policy shall return a non-zero result] + if (handle == NULL) + { + LogError("Failed setting retry policy (handle is NULL)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_081: [device_set_retry_policy shall return a non-zero result] + LogError("Failed setting retry policy (functionality not supported)"); + result = __FAILURE__; + } + + return result; +} + +int device_set_option(AMQP_DEVICE_HANDLE handle, const char* name, void* value) +{ + int result; + + // Codes_SRS_DEVICE_09_082: [If `handle` or `name` or `value` are NULL, device_set_option shall return a non-zero result] + if (handle == NULL || name == NULL || value == NULL) + { + LogError("failed setting device option (one of the followin are NULL: _handle=%p, name=%p, value=%p)", + handle, name, value); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + if (strcmp(DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0 || + strcmp(DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0 || + strcmp(DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0) + { + // Codes_SRS_DEVICE_09_083: [If `name` refers to authentication but CBS authentication is not used, device_set_option shall return a non-zero result] + if (instance->authentication_handle == NULL) + { + LogError("failed setting option for device '%s' (cannot set authentication option '%s'; not using CBS authentication)", instance->config->device_id, name); + result = __FAILURE__; + } + // Codes_SRS_DEVICE_09_084: [If `name` refers to authentication, it shall be passed along with `value` to authentication_set_option] + else if(authentication_set_option(instance->authentication_handle, name, value) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_085: [If authentication_set_option fails, device_set_option shall return a non-zero result] + LogError("failed setting option for device '%s' (failed setting authentication option '%s')", instance->config->device_id, name); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0) + { + // Codes_SRS_DEVICE_09_086: [If `name` refers to messenger module, it shall be passed along with `value` to telemetry_messenger_set_option] + if (telemetry_messenger_set_option(instance->messenger_handle, TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, value) != RESULT_OK) + { + // Codes_SRS_DEVICE_09_087: [If telemetry_messenger_set_option fails, device_set_option shall return a non-zero result] + LogError("failed setting option for device '%s' (failed setting messenger option '%s')", instance->config->device_id, name); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(DEVICE_OPTION_SAVED_AUTH_OPTIONS, name) == 0) + { + // Codes_SRS_DEVICE_09_088: [If `name` is DEVICE_OPTION_SAVED_AUTH_OPTIONS but CBS authentication is not being used, device_set_option shall return a non-zero result] + if (instance->authentication_handle == NULL) + { + LogError("failed setting option for device '%s' (cannot set authentication option '%s'; not using CBS authentication)", instance->config->device_id, name); + result = __FAILURE__; + } + else if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, instance->authentication_handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result] + LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed for authentication instance)", instance->config->device_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, name) == 0) + { + // Codes_SRS_DEVICE_09_089: [If `name` is DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, `value` shall be fed to `instance->messenger_handle` using OptionHandler_FeedOptions] + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, instance->messenger_handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result] + LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed for messenger instance)", instance->config->device_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(DEVICE_OPTION_SAVED_OPTIONS, name) == 0) + { + // Codes_SRS_DEVICE_09_090: [If `name` is DEVICE_OPTION_SAVED_OPTIONS, `value` shall be fed to `instance` using OptionHandler_FeedOptions] + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result] + LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed)", instance->config->device_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + // Codes_SRS_DEVICE_09_092: [If no failures occur, device_set_option shall return 0] + LogError("failed setting option for device '%s' (option with name '%s' is not suppported)", instance->config->device_id, name); + result = __FAILURE__; + } + } + + return result; +} + +OPTIONHANDLER_HANDLE device_retrieve_options(AMQP_DEVICE_HANDLE handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_DEVICE_09_093: [If `handle` is NULL, device_retrieve_options shall return NULL] + if (handle == NULL) + { + LogError("Failed to retrieve options from device instance (handle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_DEVICE_09_094: [A OPTIONHANDLER_HANDLE instance, aka `options` shall be created using OptionHandler_Create] + OPTIONHANDLER_HANDLE options = OptionHandler_Create(device_clone_option, device_destroy_option, (pfSetOption)device_set_option); + + if (options == NULL) + { + // Codes_SRS_DEVICE_09_095: [If OptionHandler_Create fails, device_retrieve_options shall return NULL] + LogError("Failed to retrieve options from device instance (OptionHandler_Create failed)"); + result = NULL; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + OPTIONHANDLER_HANDLE dependency_options = NULL; + + // Codes_SRS_DEVICE_09_096: [If CBS authentication is used, `instance->authentication_handle` options shall be retrieved using authentication_retrieve_options] + if (instance->authentication_handle != NULL && + (dependency_options = authentication_retrieve_options(instance->authentication_handle)) == NULL) + { + // Codes_SRS_DEVICE_09_097: [If authentication_retrieve_options fails, device_retrieve_options shall return NULL] + LogError("Failed to retrieve options from device '%s' (failed to retrieve options from authentication instance)", instance->config->device_id); + result = NULL; + } + // Codes_SRS_DEVICE_09_098: [The authentication options shall be added to `options` using OptionHandler_AddOption as DEVICE_OPTION_SAVED_AUTH_OPTIONS] + else if (instance->authentication_handle != NULL && + OptionHandler_AddOption(options, DEVICE_OPTION_SAVED_AUTH_OPTIONS, (const void*)dependency_options) != OPTIONHANDLER_OK) + { + // Codes_SRS_DEVICE_09_102: [If any call to OptionHandler_AddOption fails, device_retrieve_options shall return NULL] + LogError("Failed to retrieve options from device '%s' (OptionHandler_AddOption failed for option '%s')", instance->config->device_id, DEVICE_OPTION_SAVED_AUTH_OPTIONS); + result = NULL; + } + // Codes_SRS_DEVICE_09_099: [`instance->messenger_handle` options shall be retrieved using telemetry_messenger_retrieve_options] + else if ((dependency_options = telemetry_messenger_retrieve_options(instance->messenger_handle)) == NULL) + { + // Codes_SRS_DEVICE_09_100: [If telemetry_messenger_retrieve_options fails, device_retrieve_options shall return NULL] + LogError("Failed to retrieve options from device '%s' (failed to retrieve options from messenger instance)", instance->config->device_id); + result = NULL; + } + // Codes_SRS_DEVICE_09_101: [The messenger options shall be added to `options` using OptionHandler_AddOption as DEVICE_OPTION_SAVED_MESSENGER_OPTIONS] + else if (OptionHandler_AddOption(options, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, (const void*)dependency_options) != OPTIONHANDLER_OK) + { + // Codes_SRS_DEVICE_09_102: [If any call to OptionHandler_AddOption fails, device_retrieve_options shall return NULL] + LogError("Failed to retrieve options from device '%s' (OptionHandler_AddOption failed for option '%s')", instance->config->device_id, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS); + result = NULL; + } + else + { + // Codes_SRS_DEVICE_09_104: [If no failures occur, a handle to `options` shall be return] + result = options; + } + + if (result == NULL) + { + // Codes_SRS_DEVICE_09_103: [If any failure occurs, any memory allocated by device_retrieve_options shall be destroyed] + OptionHandler_Destroy(options); + } + } + } + + return result; +} + +int device_send_twin_update_async(AMQP_DEVICE_HANDLE handle, CONSTBUFFER_HANDLE data, DEVICE_SEND_TWIN_UPDATE_COMPLETE_CALLBACK on_send_twin_update_complete_callback, void* context) +{ + int result; + + // Codes_SRS_DEVICE_09_135: [If `handle` or `data` are NULL, device_send_twin_update_async shall return a non-zero result] + if (handle == NULL || data == NULL) + { + LogError("Invalid argument (handle=%p, data=%p)", handle, data); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + DEVICE_SEND_TWIN_UPDATE_CONTEXT* twin_ctx; + + // Codes_SRS_DEVICE_09_136: [A structure (`twin_ctx`) shall be created to track the send state of the twin report] + if ((twin_ctx = (DEVICE_SEND_TWIN_UPDATE_CONTEXT*)malloc(sizeof(DEVICE_SEND_TWIN_UPDATE_CONTEXT))) == NULL) + { + // Codes_SRS_DEVICE_09_137: [If `twin_ctx` fails to be created, device_send_twin_update_async shall return a non-zero value] + LogError("Cannot send twin update (failed creating TWIN context)"); + result = __FAILURE__; + } + else + { + twin_ctx->on_send_twin_update_complete_callback = on_send_twin_update_complete_callback; + twin_ctx->context = context; + + // Codes_SRS_DEVICE_09_138: [The twin report shall be sent using twin_messenger_report_state_async, passing `on_report_state_complete_callback` and `twin_ctx`] + if (twin_messenger_report_state_async(instance->twin_messenger_handle, data, on_report_state_complete_callback, (const void*)twin_ctx) != 0) + { + // Codes_SRS_DEVICE_09_139: [If twin_messenger_report_state_async fails, device_send_twin_update_async shall return a non-zero value] + LogError("Cannot send twin update (failed creating TWIN messenger)"); + free(twin_ctx); + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_140: [If no failures occur, device_send_twin_update_async shall return 0] + result = RESULT_OK; + } + } + } + + return result; +} + +int device_subscribe_for_twin_updates(AMQP_DEVICE_HANDLE handle, DEVICE_TWIN_UPDATE_RECEIVED_CALLBACK on_device_twin_update_received_callback, void* context) +{ + int result; + + // Codes_SRS_DEVICE_09_143: [If `handle` or `on_device_twin_update_received_callback` are NULL, device_subscribe_for_twin_updates shall return a non-zero result] + if (handle == NULL || on_device_twin_update_received_callback == NULL) + { + LogError("Invalid argument (handle=%p, on_device_twin_update_received_callback=%p)", handle, on_device_twin_update_received_callback); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + DEVICE_TWIN_UPDATE_RECEIVED_CALLBACK previous_callback = instance->on_device_twin_update_received_callback; + void* previous_context = instance->on_device_twin_update_received_context; + + instance->on_device_twin_update_received_callback = on_device_twin_update_received_callback; + instance->on_device_twin_update_received_context = context; + + // Codes_SRS_DEVICE_09_144: [twin_messenger_subscribe shall be invoked passing `on_twin_state_update_callback`] + if (twin_messenger_subscribe(instance->twin_messenger_handle, on_twin_state_update_callback, (void*)instance) != 0) + { + // Codes_SRS_DEVICE_09_145: [If twin_messenger_subscribe fails, device_subscribe_for_twin_updates shall return a non-zero value] + LogError("Failed subscribing for device twin updates"); + instance->on_device_twin_update_received_callback = previous_callback; + instance->on_device_twin_update_received_context = previous_context; + result = __FAILURE__; + } + else + { + // Codes_SRS_DEVICE_09_146: [If no failures occur, device_subscribe_for_twin_updates shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int device_unsubscribe_for_twin_updates(AMQP_DEVICE_HANDLE handle) +{ + int result; + + + // Codes_SRS_DEVICE_09_147: [If `handle` is NULL, device_unsubscribe_for_twin_updates shall return a non-zero result] + if (handle == NULL) + { + LogError("Invalid argument (handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_DEVICE_INSTANCE* instance = (AMQP_DEVICE_INSTANCE*)handle; + + // Codes_SRS_DEVICE_09_148: [twin_messenger_unsubscribe shall be invoked passing `on_twin_state_update_callback`] + if (twin_messenger_unsubscribe(instance->twin_messenger_handle) != 0) + { + // Codes_SRS_DEVICE_09_149: [If twin_messenger_unsubscribe fails, device_unsubscribe_for_twin_updates shall return a non-zero value] + LogError("Failed unsubscribing for device twin updates"); + result = __FAILURE__; + } + else + { + instance->on_device_twin_update_received_callback = NULL; + instance->on_device_twin_update_received_context = NULL; + // Codes_SRS_DEVICE_09_150: [If no failures occur, device_unsubscribe_for_twin_updates shall return 0] + result = RESULT_OK; + } + } + + return result; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_messenger.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1714 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/message_receiver.h" +#include "internal/message_queue.h" +#include "internal/iothub_client_retry_control.h" +#include "internal/iothubtransport_amqp_messenger.h" + +DEFINE_ENUM_STRINGS(AMQP_MESSENGER_SEND_STATUS, AMQP_MESSENGER_SEND_STATUS_VALUES); +DEFINE_ENUM_STRINGS(AMQP_MESSENGER_SEND_RESULT, AMQP_MESSENGER_SEND_RESULT_VALUES); +DEFINE_ENUM_STRINGS(AMQP_MESSENGER_REASON, AMQP_MESSENGER_REASON_VALUES); +DEFINE_ENUM_STRINGS(AMQP_MESSENGER_DISPOSITION_RESULT, AMQP_MESSENGER_DISPOSITION_RESULT_VALUES); +DEFINE_ENUM_STRINGS(AMQP_MESSENGER_STATE, AMQP_MESSENGER_STATE_VALUES); + + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) + +// AMQP Link address format: "amqps://<iot hub fqdn>/devices/<device-id>/<suffix>" +#define LINK_ADDRESS_FORMAT "amqps://%s/devices/%s/%s" +#define LINK_ADDRESS_MODULE_FORMAT "amqps://%s/devices/%s/modules/%s/%s" +#define SEND_LINK_NAME_PREFIX "link-snd" +#define MESSAGE_SENDER_MAX_LINK_SIZE UINT64_MAX +#define RECEIVE_LINK_NAME_PREFIX "link-rcv" +#define MESSAGE_RECEIVER_MAX_LINK_SIZE 65536 +#define DEFAULT_EVENT_SEND_RETRY_LIMIT 0 +#define DEFAULT_EVENT_SEND_TIMEOUT_SECS 600 +#define DEFAULT_MAX_SEND_ERROR_COUNT 10 +#define MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS 300 +#define MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS 300 +#define UNIQUE_ID_BUFFER_SIZE 37 + +static const char* MESSENGER_SAVED_MQ_OPTIONS = "amqp_message_queue_options"; + +typedef struct AMQP_MESSENGER_INSTANCE_TAG +{ + AMQP_MESSENGER_CONFIG* config; + + bool receive_messages; + ON_AMQP_MESSENGER_MESSAGE_RECEIVED on_message_received_callback; + void* on_message_received_context; + + MESSAGE_QUEUE_HANDLE send_queue; + AMQP_MESSENGER_STATE state; + + SESSION_HANDLE session_handle; + + LINK_HANDLE sender_link; + MESSAGE_SENDER_HANDLE message_sender; + MESSAGE_SENDER_STATE message_sender_current_state; + MESSAGE_SENDER_STATE message_sender_previous_state; + + LINK_HANDLE receiver_link; + MESSAGE_RECEIVER_HANDLE message_receiver; + MESSAGE_RECEIVER_STATE message_receiver_current_state; + MESSAGE_RECEIVER_STATE message_receiver_previous_state; + + size_t send_error_count; + size_t max_send_error_count; + + time_t last_message_sender_state_change_time; + time_t last_message_receiver_state_change_time; +} AMQP_MESSENGER_INSTANCE; + +typedef struct MESSAGE_SEND_CONTEXT_TAG +{ + MESSAGE_HANDLE message; + bool is_destroyed; + + AMQP_MESSENGER_INSTANCE* messenger; + + AMQP_MESSENGER_SEND_COMPLETE_CALLBACK on_send_complete_callback; + void* user_context; + + PROCESS_MESSAGE_COMPLETED_CALLBACK on_process_message_completed_callback; +} MESSAGE_SEND_CONTEXT; + + + +static MESSAGE_SEND_CONTEXT* create_message_send_context() +{ + MESSAGE_SEND_CONTEXT* result; + + if ((result = (MESSAGE_SEND_CONTEXT*)malloc(sizeof(MESSAGE_SEND_CONTEXT))) == NULL) + { + LogError("Failed creating the message send context"); + } + else + { + memset(result, 0, sizeof(MESSAGE_SEND_CONTEXT)); + } + + return result; +} + +static bool is_valid_configuration(const AMQP_MESSENGER_CONFIG* config) +{ + bool result; + + if (config == NULL) + { + LogError("Invalid configuration (NULL)"); + result = false; + } + else if (config->client_version == NULL || + config->device_id == NULL || + config->iothub_host_fqdn == NULL || + config->receive_link.source_suffix == NULL || + config->send_link.target_suffix == NULL) + { + LogError("Invalid configuration (client_version=%p, device_id=%p, iothub_host_fqdn=%p, receive_link (source_suffix=%p), send_link (target_suffix=%p))", + config->client_version, config->device_id, config->iothub_host_fqdn, + config->receive_link.source_suffix, config->send_link.target_suffix); + result = false; + } + else + { + result = true; + } + + return result; +} + +static void destroy_link_configuration(AMQP_MESSENGER_LINK_CONFIG* link_config) +{ + if (link_config->target_suffix != NULL) + { + free((void*)link_config->target_suffix); + link_config->target_suffix = NULL; + } + + if (link_config->source_suffix != NULL) + { + free((void*)link_config->source_suffix); + link_config->source_suffix = NULL; + } + + if (link_config->attach_properties != NULL) + { + Map_Destroy(link_config->attach_properties); + link_config->attach_properties = NULL; + } +} + +static void destroy_configuration(AMQP_MESSENGER_CONFIG* config) +{ + if (config != NULL) + { + if (config->client_version != NULL) + { + free((void*)config->client_version); + } + + if (config->device_id != NULL) + { + free((void*)config->device_id); + } + + if (config->module_id != NULL) + { + free((void*)config->module_id); + } + + if (config->iothub_host_fqdn != NULL) + { + free((void*)config->iothub_host_fqdn); + } + + destroy_link_configuration(&config->send_link); + destroy_link_configuration(&config->receive_link); + + free(config); + } +} + +static int clone_link_configuration(role link_role, AMQP_MESSENGER_LINK_CONFIG* dst_config, const AMQP_MESSENGER_LINK_CONFIG* src_config) +{ + int result; + + if (link_role == role_sender && + mallocAndStrcpy_s(&dst_config->target_suffix, src_config->target_suffix) != 0) + { + LogError("Failed copying send_link_target_suffix"); + result = __FAILURE__; + } + else if (link_role == role_receiver && + mallocAndStrcpy_s(&dst_config->source_suffix, src_config->source_suffix) != 0) + { + LogError("Failed copying receive_link_source_suffix"); + destroy_link_configuration(dst_config); + result = __FAILURE__; + } + else if (src_config->attach_properties != NULL && + (dst_config->attach_properties = Map_Clone(src_config->attach_properties)) == NULL) + { + LogError("Failed copying link attach properties"); + destroy_link_configuration(dst_config); + result = __FAILURE__; + } + else + { + dst_config->snd_settle_mode = src_config->snd_settle_mode; + dst_config->rcv_settle_mode = src_config->rcv_settle_mode; + + result = RESULT_OK; + } + + return result; +} + +static AMQP_MESSENGER_CONFIG* clone_configuration(const AMQP_MESSENGER_CONFIG* config) +{ + AMQP_MESSENGER_CONFIG* result; + + if ((result = (AMQP_MESSENGER_CONFIG*)malloc(sizeof(AMQP_MESSENGER_CONFIG))) == NULL) + { + LogError("Failed allocating AMQP_MESSENGER_CONFIG"); + } + else + { + memset(result, 0, sizeof(AMQP_MESSENGER_CONFIG)); + + if (mallocAndStrcpy_s(&result->client_version, config->client_version) != 0) + { + LogError("Failed copying device_id"); + destroy_configuration(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->device_id, config->device_id) != 0) + { + LogError("Failed copying device_id"); + destroy_configuration(result); + result = NULL; + } + else if ((config->module_id != NULL) && (mallocAndStrcpy_s(&result->module_id, config->module_id) != 0)) + { + LogError("Failed copying module_id"); + destroy_configuration(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->iothub_host_fqdn, config->iothub_host_fqdn) != 0) + { + LogError("Failed copying iothub_host_fqdn"); + destroy_configuration(result); + result = NULL; + } + else if (clone_link_configuration(role_sender, &result->send_link, &config->send_link) != RESULT_OK) + { + LogError("Failed copying send link configuration"); + destroy_configuration(result); + result = NULL; + } + else if (clone_link_configuration(role_receiver, &result->receive_link, &config->receive_link) != RESULT_OK) + { + LogError("Failed copying receive link configuration"); + destroy_configuration(result); + result = NULL; + } + else + { + result->on_state_changed_callback = config->on_state_changed_callback; + result->on_state_changed_context = config->on_state_changed_context; + result->on_subscription_changed_callback = config->on_subscription_changed_callback; + result->on_subscription_changed_context = config->on_subscription_changed_context; + } + } + + return result; +} + +static void destroy_message_send_context(MESSAGE_SEND_CONTEXT* context) +{ + free(context); +} + +static STRING_HANDLE create_link_address(const char* host_fqdn, const char* device_id, const char* module_id, const char* address_suffix) +{ + STRING_HANDLE link_address; + + if ((link_address = STRING_new()) == NULL) + { + LogError("failed creating link_address (STRING_new failed)"); + } + else + { + if (module_id != NULL) + { + if (STRING_sprintf(link_address, LINK_ADDRESS_MODULE_FORMAT, host_fqdn, device_id, module_id, address_suffix) != RESULT_OK) + { + LogError("Failed creating the link_address for a module (STRING_sprintf failed)"); + STRING_delete(link_address); + link_address = NULL; + } + } + else + { + if (STRING_sprintf(link_address, LINK_ADDRESS_FORMAT, host_fqdn, device_id, address_suffix) != RESULT_OK) + { + LogError("Failed creating the link_address (STRING_sprintf failed)"); + STRING_delete(link_address); + link_address = NULL; + } + } + } + return link_address; +} + +static STRING_HANDLE create_link_terminus_name(STRING_HANDLE link_name, const char* suffix) +{ + STRING_HANDLE terminus_name; + + if ((terminus_name = STRING_new()) == NULL) + { + LogError("Failed creating the terminus name (STRING_new failed; %s)", suffix); + } + else + { + const char* link_name_char_ptr = STRING_c_str(link_name); + + if (STRING_sprintf(terminus_name, "%s-%s", link_name_char_ptr, suffix) != RESULT_OK) + { + STRING_delete(terminus_name); + terminus_name = NULL; + LogError("Failed creating the terminus name (STRING_sprintf failed; %s)", suffix); + } + } + + return terminus_name; +} + +static STRING_HANDLE create_link_name(role link_role, const char* device_id) +{ + char* unique_id; + STRING_HANDLE result; + + if ((unique_id = (char*)malloc(sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1)) == NULL) + { + LogError("Failed generating an unique tag (malloc failed)"); + result = NULL; + } + else + { + memset(unique_id, 0, sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1); + + if (UniqueId_Generate(unique_id, UNIQUE_ID_BUFFER_SIZE) != UNIQUEID_OK) + { + LogError("Failed generating an unique tag (UniqueId_Generate failed)"); + result = NULL; + } + else if ((result = STRING_new()) == NULL) + { + LogError("Failed generating an unique tag (STRING_new failed)"); + } + else if (STRING_sprintf(result, "%s-%s-%s", (link_role == role_sender ? SEND_LINK_NAME_PREFIX : RECEIVE_LINK_NAME_PREFIX), device_id, unique_id) != 0) + { + LogError("Failed generating an unique tag (STRING_sprintf failed)"); + STRING_delete(result); + result = NULL; + } + + free(unique_id); + } + + return result; +} + +static void update_messenger_state(AMQP_MESSENGER_INSTANCE* instance, AMQP_MESSENGER_STATE new_state) +{ + if (new_state != instance->state) + { + AMQP_MESSENGER_STATE previous_state = instance->state; + instance->state = new_state; + + if (instance->config != NULL && instance->config->on_state_changed_callback != NULL) + { + instance->config->on_state_changed_callback(instance->config->on_state_changed_context, previous_state, new_state); + } + } +} + +static int add_link_attach_properties(LINK_HANDLE link, MAP_HANDLE user_defined_properties) +{ + int result; + fields attach_properties; + + if ((attach_properties = amqpvalue_create_map()) == NULL) + { + LogError("Failed to create the map for attach properties."); + result = __FAILURE__; + } + else + { + const char* const* keys; + const char* const* values; + size_t count; + + if (Map_GetInternals(user_defined_properties, &keys, &values, &count) != MAP_OK) + { + LogError("failed getting user defined properties details."); + result = __FAILURE__; + } + else + { + size_t i; + result = RESULT_OK; + + for (i = 0; i < count && result == RESULT_OK; i++) + { + AMQP_VALUE key; + AMQP_VALUE value; + + if ((key = amqpvalue_create_symbol(keys[i])) == NULL) + { + LogError("Failed creating AMQP_VALUE For key %s.", keys[i]); + result = __FAILURE__; + } + else + { + if ((value = amqpvalue_create_string(values[i])) == NULL) + { + LogError("Failed creating AMQP_VALUE For key %s value", keys[i]); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_map_value(attach_properties, key, value) != 0) + { + LogError("Failed adding property %s to map", keys[i]); + result = __FAILURE__; + } + + amqpvalue_destroy(value); + } + + amqpvalue_destroy(key); + } + } + + if (result == RESULT_OK) + { + if (link_set_attach_properties(link, attach_properties) != 0) + { + LogError("Failed attaching properties to link"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + + amqpvalue_destroy(attach_properties); + } + + return result; +} + +static int create_link_terminus(role link_role, STRING_HANDLE link_name, STRING_HANDLE link_address, AMQP_VALUE* source, AMQP_VALUE* target) +{ + int result; + STRING_HANDLE terminus_name; + const char* source_name; + const char* target_name; + + if (link_role == role_sender) + { + if ((terminus_name = create_link_terminus_name(link_name, "source")) == NULL) + { + LogError("Failed creating terminus name"); + source_name = NULL; + target_name = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_077: [The AMQP link source shall be defined as "<link name>-source"] + source_name = STRING_c_str(terminus_name); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_094: [The AMQP link source shall be defined as <link address>] + target_name = STRING_c_str(link_address); + } + } + else + { + if ((terminus_name = create_link_terminus_name(link_name, "target")) == NULL) + { + LogError("Failed creating terminus name"); + source_name = NULL; + target_name = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_078: [The AMQP link target shall be defined as <link address>] + source_name = STRING_c_str(link_address); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_095: [The AMQP link target shall be defined as "<link name>-target"] + target_name = STRING_c_str(terminus_name); + } + } + + if (source_name == NULL || target_name == NULL) + { + LogError("Failed creating link source and/or target name (source=%p, target=%p)", source_name, target_name); + result = __FAILURE__; + } + else + { + if ((*source = messaging_create_source(source_name)) == NULL) + { + LogError("Failed creating link source"); + result = __FAILURE__; + } + else + { + if ((*target = messaging_create_target(target_name)) == NULL) + { + LogError("Failed creating link target"); + amqpvalue_destroy(*source); + *source = NULL; + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + + + STRING_delete(terminus_name); + + return result; +} + +static LINK_HANDLE create_link(role link_role, SESSION_HANDLE session_handle, AMQP_MESSENGER_LINK_CONFIG* link_config, const char* iothub_host_fqdn, const char* device_id, const char* module_id) +{ + LINK_HANDLE result = NULL; + STRING_HANDLE link_address; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_075: [The AMQP link address shall be defined as "amqps://<`iothub_host_fqdn`>/devices/<`device_id`>/<`instance-config->send_link.source_suffix`>"] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_092: [The AMQP link address shall be defined as "amqps://<`iothub_host_fqdn`>/devices/<`device_id`>/<`instance-config->receive_link.target_suffix`>"] + if ((link_address = create_link_address(iothub_host_fqdn, device_id, module_id, (link_role == role_sender ? link_config->target_suffix : link_config->source_suffix))) == NULL) + { + LogError("Failed creating the message sender (failed creating the 'link_address')"); + result = NULL; + } + else + { + STRING_HANDLE link_name; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_076: [The AMQP link name shall be defined as "link-snd-<`device_id`>-<locally generated UUID>"] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_093: [The AMQP link name shall be defined as "link-rcv-<`device_id`>-<locally generated UUID>"] + if ((link_name = create_link_name(link_role, device_id)) == NULL) + { + LogError("Failed creating the link name"); + result = NULL; + } + else + { + AMQP_VALUE source = NULL; + AMQP_VALUE target = NULL; + + if (create_link_terminus(link_role, link_name, link_address, &source, &target) == RESULT_OK) + { + if ((result = link_create(session_handle, STRING_c_str(link_name), link_role, source, target)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_079: [If the link fails to be created, amqp_messenger_do_work() shall change the state to AMQP_MESSENGER_STATE_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_096: [If the link fails to be created, amqp_messenger_do_work() shall change the state to AMQP_MESSENGER_STATE_ERROR] + LogError("Failed creating the AMQP link"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_082: [The AMQP link maximum message size shall be set to UINT64_MAX using link_set_max_message_size()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_099: [The AMQP link maximum message size shall be set to UINT64_MAX using link_set_max_message_size()] + if (link_set_max_message_size(result, MESSAGE_SENDER_MAX_LINK_SIZE) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_083: [If link_set_max_message_size() fails, it shall be logged and ignored.] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_100: [If link_set_max_message_size() fails, it shall be logged and ignored.] + LogError("Failed setting link max message size."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_080: [The AMQP link shall have its ATTACH properties set using `instance->config->send_link.attach_properties`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_097: [The AMQP link shall have its ATTACH properties set using `instance->config->receive_link.attach_properties`] + if (link_config->attach_properties != NULL && + add_link_attach_properties(result, link_config->attach_properties) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_081: [If the AMQP link attach properties fail to be set, amqp_messenger_do_work() shall change the state to AMQP_MESSENGER_STATE_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_098: [If the AMQP link attach properties fail to be set, amqp_messenger_do_work() shall change the state to AMQP_MESSENGER_STATE_ERROR] + LogError("Failed setting link attach properties"); + link_destroy(result); + result = NULL; + } + } + + amqpvalue_destroy(source); + amqpvalue_destroy(target); + } + + STRING_delete(link_name); + } + + STRING_delete(link_address); + } + + return result; +} + +static void destroy_message_sender(AMQP_MESSENGER_INSTANCE* instance) +{ + if (instance->message_sender != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_090: [`instance->message_sender` shall be destroyed using messagesender_destroy()] + messagesender_destroy(instance->message_sender); + instance->message_sender = NULL; + } + + instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE; + instance->last_message_sender_state_change_time = INDEFINITE_TIME; + + if (instance->sender_link != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_091: [`instance->sender_link` shall be destroyed using link_destroy()] + link_destroy(instance->sender_link); + instance->sender_link = NULL; + } +} + +static void on_message_sender_state_changed_callback(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state) +{ + if (context == NULL) + { + LogError("on_message_sender_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored"); + } + else if (new_state != previous_state) + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_088: [`new_state`, `previous_state` shall be saved into `instance->message_sender_previous_state` and `instance->message_sender_current_state`] + instance->message_sender_current_state = new_state; + instance->message_sender_previous_state = previous_state; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_089: [`instance->last_message_sender_state_change_time` shall be set using get_time()] + instance->last_message_sender_state_change_time = get_time(NULL); + } +} + +static int create_message_sender(AMQP_MESSENGER_INSTANCE* instance) +{ + int result; + + if ((instance->sender_link = create_link(role_sender, + instance->session_handle, &instance->config->send_link, instance->config->iothub_host_fqdn, instance->config->device_id, instance->config->module_id)) == NULL) + { + LogError("Failed creating the message sender link"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_084: [`instance->message_sender` shall be created using messagesender_create(), passing the `instance->sender_link` and `on_message_sender_state_changed_callback`] + else if ((instance->message_sender = messagesender_create(instance->sender_link, on_message_sender_state_changed_callback, (void*)instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_085: [If messagesender_create() fails, amqp_messenger_do_work() shall fail and return] + LogError("Failed creating the message sender (messagesender_create failed)"); + destroy_message_sender(instance); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_086: [`instance->message_sender` shall be opened using messagesender_open()] + if (messagesender_open(instance->message_sender) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_087: [If messagesender_open() fails, amqp_messenger_do_work() shall fail and return] + LogError("Failed opening the AMQP message sender."); + destroy_message_sender(instance); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + + return result; +} + +static void destroy_message_receiver(AMQP_MESSENGER_INSTANCE* instance) +{ + if (instance->message_receiver != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_113: [`instance->message_receiver` shall be closed using messagereceiver_close()] + if (messagereceiver_close(instance->message_receiver) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_114: [If messagereceiver_close() fails, it shall be logged and ignored] + LogError("Failed closing the AMQP message receiver (this failure will be ignored)."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_115: [`instance->message_receiver` shall be destroyed using messagereceiver_destroy()] + messagereceiver_destroy(instance->message_receiver); + + instance->message_receiver = NULL; + } + + instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->last_message_receiver_state_change_time = INDEFINITE_TIME; + + if (instance->receiver_link != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_116: [`instance->receiver_link` shall be destroyed using link_destroy()] + link_destroy(instance->receiver_link); + instance->receiver_link = NULL; + } +} + +static void on_message_receiver_state_changed_callback(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state) +{ + if (context == NULL) + { + LogError("on_message_receiver_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored"); + } + else + { + if (new_state != previous_state) + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_105: [`new_state`, `previous_state` shall be saved into `instance->message_receiver_previous_state` and `instance->message_receiver_current_state`] + instance->message_receiver_current_state = new_state; + instance->message_receiver_previous_state = previous_state; + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_106: [`instance->last_message_receiver_state_change_time` shall be set using get_time()] + instance->last_message_receiver_state_change_time = get_time(NULL); + + if (new_state == MESSAGE_RECEIVER_STATE_OPEN) + { + if (instance->config->on_subscription_changed_callback != NULL) + { + instance->config->on_subscription_changed_callback(instance->config->on_subscription_changed_context, true); + } + } + else if (previous_state == MESSAGE_RECEIVER_STATE_OPEN && new_state != MESSAGE_RECEIVER_STATE_OPEN) + { + if (instance->config->on_subscription_changed_callback != NULL) + { + instance->config->on_subscription_changed_callback(instance->config->on_subscription_changed_context, false); + } + } + } + } +} + +static AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* create_message_disposition_info(AMQP_MESSENGER_INSTANCE* messenger) +{ + AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* result; + + if ((result = (AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO))) == NULL) + { + LogError("Failed creating AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO container (malloc failed)"); + result = NULL; + } + else + { + delivery_number message_id; + + if (messagereceiver_get_received_message_id(messenger->message_receiver, &message_id) != RESULT_OK) + { + LogError("Failed creating AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_received_message_id failed)"); + free(result); + result = NULL; + } + else + { + const char* link_name; + + if (messagereceiver_get_link_name(messenger->message_receiver, &link_name) != RESULT_OK) + { + LogError("Failed creating AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_link_name failed)"); + free(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->source, link_name) != RESULT_OK) + { + LogError("Failed creating AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO container (failed copying link name)"); + free(result); + result = NULL; + } + else + { + result->message_id = message_id; + } + } + } + + return result; +} + +static void destroy_message_disposition_info(AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info) +{ + free(disposition_info->source); + free(disposition_info); +} + +static AMQP_VALUE create_uamqp_disposition_result_from(AMQP_MESSENGER_DISPOSITION_RESULT disposition_result) +{ + AMQP_VALUE uamqp_disposition_result; + + if (disposition_result == AMQP_MESSENGER_DISPOSITION_RESULT_NONE) + { + uamqp_disposition_result = NULL; // intentionally not sending an answer. + } + else if (disposition_result == AMQP_MESSENGER_DISPOSITION_RESULT_ACCEPTED) + { + uamqp_disposition_result = messaging_delivery_accepted(); + } + else if (disposition_result == AMQP_MESSENGER_DISPOSITION_RESULT_RELEASED) + { + uamqp_disposition_result = messaging_delivery_released(); + } + else // id est, if (disposition_result == AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED) + { + uamqp_disposition_result = messaging_delivery_rejected("Rejected by application", "Rejected by application"); + } + + return uamqp_disposition_result; +} + +static AMQP_VALUE on_message_received_internal_callback(const void* context, MESSAGE_HANDLE message) +{ + AMQP_VALUE result; + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)context; + AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* message_disposition_info; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_107: [A AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be created containing the source link name and message delivery ID] + if ((message_disposition_info = create_message_disposition_info(instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_108: [If the AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO instance fails to be created, on_message_received_internal_callback shall return the result of messaging_delivery_released()] + LogError("on_message_received_internal_callback failed (failed creating AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO)."); + result = messaging_delivery_released(); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_109: [`instance->on_message_received_callback` shall be invoked passing the `message` and AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO instance] + AMQP_MESSENGER_DISPOSITION_RESULT disposition_result = instance->on_message_received_callback(message, message_disposition_info, instance->on_message_received_context); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_110: [If `instance->on_message_received_callback` returns AMQP_MESSENGER_DISPOSITION_RESULT_ACCEPTED, on_message_received_internal_callback shall return the result of messaging_delivery_accepted()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_111: [If `instance->on_message_received_callback` returns AMQP_MESSENGER_DISPOSITION_RESULT_RELEASED, on_message_received_internal_callback shall return the result of messaging_delivery_released()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_112: [If `instance->on_message_received_callback` returns AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED, on_message_received_internal_callback shall return the result of messaging_delivery_rejected()] + result = create_uamqp_disposition_result_from(disposition_result); + } + + return result; +} + +static int create_message_receiver(AMQP_MESSENGER_INSTANCE* instance) +{ + int result; + + if ((instance->receiver_link = create_link(role_receiver, + instance->session_handle, &instance->config->receive_link, instance->config->iothub_host_fqdn, instance->config->device_id, instance->config->module_id)) == NULL) + { + LogError("Failed creating the message receiver link"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_101: [`instance->message_receiver` shall be created using messagereceiver_create(), passing the `instance->receiver_link` and `on_message_receiver_state_changed_callback`] + else if ((instance->message_receiver = messagereceiver_create(instance->receiver_link, on_message_receiver_state_changed_callback, (void*)instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_102: [If messagereceiver_create() fails, amqp_messenger_do_work() shall fail and return] + LogError("Failed creating the message receiver (messagereceiver_create failed)"); + destroy_message_receiver(instance); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_103: [`instance->message_receiver` shall be opened using messagereceiver_open() passing `on_message_received_internal_callback`] + else if (messagereceiver_open(instance->message_receiver, on_message_received_internal_callback, (void*)instance) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_104: [If messagereceiver_open() fails, amqp_messenger_do_work() shall fail and return] + LogError("Failed opening the AMQP message receiver."); + destroy_message_receiver(instance); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static void on_send_complete_callback(void* context, MESSAGE_SEND_RESULT send_result) +{ + if (context != NULL) + { + MESSAGE_QUEUE_RESULT mq_result; + MESSAGE_SEND_CONTEXT* msg_ctx = (MESSAGE_SEND_CONTEXT*)context; + + if (send_result == MESSAGE_SEND_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_121: [If no failure occurs, `context->on_process_message_completed_callback` shall be invoked with result MESSAGE_QUEUE_SUCCESS] + mq_result = MESSAGE_QUEUE_SUCCESS; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_122: [If a failure occurred, `context->on_process_message_completed_callback` shall be invoked with result MESSAGE_QUEUE_ERROR] + mq_result = MESSAGE_QUEUE_ERROR; + } + + msg_ctx->on_process_message_completed_callback(msg_ctx->messenger->send_queue, (MQ_MESSAGE_HANDLE)msg_ctx->message, mq_result, NULL); + } +} + +static void on_process_message_callback(MESSAGE_QUEUE_HANDLE message_queue, MQ_MESSAGE_HANDLE message, PROCESS_MESSAGE_COMPLETED_CALLBACK on_process_message_completed_callback, void* context) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_117: [If any argument is NULL, `on_process_message_callback` shall return immediatelly] + if (message_queue == NULL || message == NULL || on_process_message_completed_callback == NULL || context == NULL) + { + LogError("Invalid argument (message_queue=%p, message=%p, on_process_message_completed_callback=%p, context=%p)", message_queue, message, on_process_message_completed_callback, context); + } + else + { + MESSAGE_SEND_CONTEXT* message_context = (MESSAGE_SEND_CONTEXT*)context; + message_context->on_process_message_completed_callback = on_process_message_completed_callback; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_118: [The MESSAGE_HANDLE shall be submitted for sending using messagesender_send(), passing `on_send_complete_callback`] + if (messagesender_send_async(message_context->messenger->message_sender, (MESSAGE_HANDLE)message, on_send_complete_callback, context, 0) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_119: [If messagesender_send() fails, `on_process_message_completed_callback` shall be invoked with result MESSAGE_QUEUE_ERROR] + LogError("Failed sending AMQP message"); + on_process_message_completed_callback(message_queue, message, MESSAGE_QUEUE_ERROR, NULL); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_120: [The MESSAGE_HANDLE shall be destroyed using message_destroy() and marked as destroyed in the context provided] + message_destroy((MESSAGE_HANDLE)message); + message_context->is_destroyed = true; + } +} + +static void on_message_processing_completed_callback(MQ_MESSAGE_HANDLE message, MESSAGE_QUEUE_RESULT result, USER_DEFINED_REASON reason, void* message_context) +{ + (void)reason; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_020: [If `messenger_context` is NULL, `on_message_processing_completed_callback` shall return immediately] + if (message_context == NULL) + { + LogError("on_message_processing_completed_callback invoked with NULL context"); + } + else + { + MESSAGE_SEND_CONTEXT* msg_ctx = (MESSAGE_SEND_CONTEXT*)message_context; + AMQP_MESSENGER_SEND_RESULT messenger_send_result; + AMQP_MESSENGER_REASON messenger_send_reason; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_021: [If `result` is MESSAGE_QUEUE_SUCCESS, `send_ctx->on_send_complete_callback` shall be invoked with AMQP_MESSENGER_SEND_RESULT_SUCCESS and AMQP_MESSENGER_REASON_NONE] + if (result == MESSAGE_QUEUE_SUCCESS) + { + messenger_send_result = AMQP_MESSENGER_SEND_RESULT_SUCCESS; + messenger_send_reason = AMQP_MESSENGER_REASON_NONE; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_025: [If `result` is MESSAGE_QUEUE_SUCCESS or MESSAGE_QUEUE_CANCELLED, `instance->send_error_count` shall be set to 0] + msg_ctx->messenger->send_error_count = 0; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_022: [If `result` is MESSAGE_QUEUE_TIMEOUT, `send_ctx->on_send_complete_callback` shall be invoked with AMQP_MESSENGER_SEND_RESULT_ERROR and AMQP_MESSENGER_REASON_TIMEOUT] + else if (result == MESSAGE_QUEUE_TIMEOUT) + { + messenger_send_result = AMQP_MESSENGER_SEND_RESULT_ERROR; + messenger_send_reason = AMQP_MESSENGER_REASON_TIMEOUT; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_026: [Otherwise `instance->send_error_count` shall be incremented by 1] + msg_ctx->messenger->send_error_count++; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_023: [If `result` is MESSAGE_QUEUE_CANCELLED and the messenger is STOPPED, `send_ctx->on_send_complete_callback` shall be invoked with AMQP_MESSENGER_SEND_RESULT_CANCELLED and AMQP_MESSENGER_REASON_MESSENGER_DESTROYED] + else if (result == MESSAGE_QUEUE_CANCELLED && msg_ctx->messenger->state == AMQP_MESSENGER_STATE_STOPPED) + { + messenger_send_result = AMQP_MESSENGER_SEND_RESULT_CANCELLED; + messenger_send_reason = AMQP_MESSENGER_REASON_MESSENGER_DESTROYED; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_025: [If `result` is MESSAGE_QUEUE_SUCCESS or MESSAGE_QUEUE_CANCELLED, `instance->send_error_count` shall be set to 0] + msg_ctx->messenger->send_error_count = 0; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_024: [Otherwise `send_ctx->on_send_complete_callback` shall be invoked with AMQP_MESSENGER_SEND_RESULT_ERROR and AMQP_MESSENGER_REASON_FAIL_SENDING] + else + { + messenger_send_result = AMQP_MESSENGER_SEND_RESULT_ERROR; + messenger_send_reason = AMQP_MESSENGER_REASON_FAIL_SENDING; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_026: [Otherwise `instance->send_error_count` shall be incremented by 1] + msg_ctx->messenger->send_error_count++; + } + + if (msg_ctx->on_send_complete_callback != NULL) + { + msg_ctx->on_send_complete_callback(messenger_send_result, messenger_send_reason, msg_ctx->user_context); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_027: [If `message` has not been destroyed, it shall be destroyed using message_destroy()] + if (!msg_ctx->is_destroyed) + { + message_destroy((MESSAGE_HANDLE)message); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_028: [`send_ctx` shall be destroyed] + destroy_message_send_context(msg_ctx); + } +} + + +// ---------- Set/Retrieve Options Helpers ----------// + +static void* amqp_messenger_clone_option(const char* name, const void* value) +{ + void* result; + + if (name == NULL || value == NULL) + { + LogError("invalid argument (name=%p, value=%p)", name, value); + result = NULL; + } + else + { + if (strcmp(MESSENGER_SAVED_MQ_OPTIONS, name) == 0) + { + if ((result = (void*)OptionHandler_Clone((OPTIONHANDLER_HANDLE)value)) == NULL) + { + LogError("failed cloning option '%s'", name); + } + } + else + { + LogError("Failed to clone messenger option (option with name '%s' is not suppported)", name); + result = NULL; + } + } + + return result; +} + +static void amqp_messenger_destroy_option(const char* name, const void* value) +{ + if (name == NULL || value == NULL) + { + LogError("invalid argument (name=%p, value=%p)", name, value); + } + else if (strcmp(MESSENGER_SAVED_MQ_OPTIONS, name) == 0) + { + OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value); + } + else + { + LogError("invalid argument (option '%s' is not suppported)", name); + } +} + + +// Public API: + +int amqp_messenger_subscribe_for_messages(AMQP_MESSENGER_HANDLE messenger_handle, ON_AMQP_MESSENGER_MESSAGE_RECEIVED on_message_received_callback, void* context) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_035: [If `messenger_handle` or `on_message_received_callback` are NULL, amqp_messenger_subscribe_for_messages() shall fail and return non-zero value] + if (messenger_handle == NULL || on_message_received_callback == NULL || context == NULL) + { + LogError("Invalid argument (messenger_handle=%p, on_message_received_callback=%p, context=%p)", messenger_handle, on_message_received_callback, context); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_036: [`on_message_received_callback` and `context` shall be saved on `instance->on_message_received_callback`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_037: [amqp_messenger_subscribe_for_messages() shall set `instance->receive_messages` to true] + instance->on_message_received_callback = on_message_received_callback; + instance->on_message_received_context = context; + instance->receive_messages = true; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_038: [If no failures occurr, amqp_messenger_subscribe_for_messages() shall return 0] + result = RESULT_OK; + } + + return result; +} + +int amqp_messenger_unsubscribe_for_messages(AMQP_MESSENGER_HANDLE messenger_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_039: [If `messenger_handle` is NULL, amqp_messenger_unsubscribe_for_messages() shall fail and return non-zero value] + if (messenger_handle == NULL) + { + LogError("Invalid argument (messenger_handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_040: [`instance->receive_messages` shall be saved to false] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_041: [`instance->on_message_received_callback` and `instance->on_message_received_context` shall be set to NULL] + instance->receive_messages = false; + instance->on_message_received_callback = NULL; + instance->on_message_received_context = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_042: [If no failures occurr, amqp_messenger_unsubscribe_for_messages() shall return 0] + result = RESULT_OK; + } + + return result; +} + +int amqp_messenger_send_message_disposition(AMQP_MESSENGER_HANDLE messenger_handle, AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, AMQP_MESSENGER_DISPOSITION_RESULT disposition_result) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_043: [If `messenger_handle` or `disposition_info` are NULL, amqp_messenger_send_message_disposition() shall fail and return non-zero value] + if (messenger_handle == NULL || disposition_info == NULL) + { + LogError("Invalid argument (messenger_handle=%p, disposition_info=%p)", messenger_handle, disposition_info); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_044: [If `disposition_info->source` is NULL, amqp_messenger_send_message_disposition() shall fail and return non-zero value] + else if (disposition_info->source == NULL) + { + LogError("Failed sending message disposition (disposition_info->source is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* messenger = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + if (messenger->message_receiver == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_045: [If `messenger_handle->message_receiver` is NULL, amqp_messenger_send_message_disposition() shall fail and return non-zero value] + LogError("Failed sending message disposition (message_receiver is not created; check if it is subscribed)"); + result = __FAILURE__; + } + else + { + AMQP_VALUE uamqp_disposition_result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_046: [An AMQP_VALUE disposition result shall be created corresponding to the `disposition_result` provided] + if ((uamqp_disposition_result = create_uamqp_disposition_result_from(disposition_result)) == NULL) + { + LogError("Failed sending message disposition (disposition result %d is not supported)", disposition_result); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_047: [`messagereceiver_send_message_disposition()` shall be invoked passing `disposition_info->source`, `disposition_info->message_id` and the AMQP_VALUE disposition result] + if (messagereceiver_send_message_disposition(messenger->message_receiver, disposition_info->source, disposition_info->message_id, uamqp_disposition_result) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_048: [If `messagereceiver_send_message_disposition()` fails, amqp_messenger_send_message_disposition() shall fail and return non-zero value] + LogError("Failed sending message disposition (messagereceiver_send_message_disposition failed)"); + result = __FAILURE__; + } + else + { + destroy_message_disposition_info(disposition_info); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_050: [If no failures occurr, amqp_messenger_send_message_disposition() shall return 0] + result = RESULT_OK; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_049: [amqp_messenger_send_message_disposition() shall destroy the AMQP_VALUE disposition result] + amqpvalue_destroy(uamqp_disposition_result); + } + } + } + + return result; +} + +int amqp_messenger_send_async(AMQP_MESSENGER_HANDLE messenger_handle, MESSAGE_HANDLE message, AMQP_MESSENGER_SEND_COMPLETE_CALLBACK on_user_defined_send_complete_callback, void* user_context) +{ + int result; + + if (messenger_handle == NULL || message == NULL || on_user_defined_send_complete_callback == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_010: [If `messenger_handle`, `message` or `on_event_send_complete_callback` are NULL, amqp_messenger_send_async() shall fail and return a non-zero value] + LogError("Invalid argument (messenger_handle=%p, message=%p, on_user_defined_send_complete_callback=%p)", messenger_handle, message, on_user_defined_send_complete_callback); + result = __FAILURE__; + } + else + { + MESSAGE_HANDLE cloned_message; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_011: [`message` shall be cloned using message_clone()] + if ((cloned_message = message_clone(message)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_012: [If message_clone() fails, amqp_messenger_send_async() shall fail and return a non-zero value] + LogError("Failed cloning AMQP message"); + result = __FAILURE__; + } + else + { + MESSAGE_SEND_CONTEXT* message_context; + AMQP_MESSENGER_INSTANCE *instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_013: [amqp_messenger_send_async() shall allocate memory for a MESSAGE_SEND_CONTEXT structure (aka `send_ctx`)] + if ((message_context = create_message_send_context()) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_014: [If malloc() fails, amqp_messenger_send_async() shall fail and return a non-zero value] + LogError("Failed creating context for sending message"); + message_destroy(cloned_message); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_015: [The cloned `message`, callback and context shall be saved in ``send_ctx`] + message_context->message = cloned_message; + message_context->messenger = instance; + message_context->on_send_complete_callback = on_user_defined_send_complete_callback; + message_context->user_context = user_context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_016: [`send_ctx` shall be added to `instance->send_queue` using message_queue_add(), passing `on_message_processing_completed_callback`] + if (message_queue_add(instance->send_queue, (MQ_MESSAGE_HANDLE)cloned_message, on_message_processing_completed_callback, (void*)message_context) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_017: [If message_queue_add() fails, amqp_messenger_send_async() shall fail and return a non-zero value] + LogError("Failed adding message to send queue"); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_018: [If any failure occurs, amqp_messenger_send_async() shall free any memory it has allocated] + destroy_message_send_context(message_context); + message_destroy(cloned_message); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_019: [If no failures occur, amqp_messenger_send_async() shall return zero] + result = RESULT_OK; + } + } + } + } + + return result; +} + +int amqp_messenger_get_send_status(AMQP_MESSENGER_HANDLE messenger_handle, AMQP_MESSENGER_SEND_STATUS* send_status) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_029: [If `messenger_handle` or `send_status` are NULL, amqp_messenger_get_send_status() shall fail and return a non-zero value] + if (messenger_handle == NULL || send_status == NULL) + { + LogError("Invalid argument (messenger_handle=%p, send_status=%p)", messenger_handle, send_status); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + bool is_empty; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_030: [message_queue_is_empty() shall be invoked for `instance->send_queue`] + if (message_queue_is_empty(instance->send_queue, &is_empty) != 0) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_031: [If message_queue_is_empty() fails, amqp_messenger_get_send_status() shall fail and return a non-zero value] + LogError("Failed verifying if send queue is empty"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_032: [If message_queue_is_empty() returns `true`, `send_status` shall be set to AMQP_MESSENGER_SEND_STATUS_IDLE] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_033: [Otherwise, `send_status` shall be set to AMQP_MESSENGER_SEND_STATUS_BUSY] + *send_status = (is_empty ? AMQP_MESSENGER_SEND_STATUS_IDLE : AMQP_MESSENGER_SEND_STATUS_BUSY); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_034: [If no failures occur, amqp_messenger_get_send_status() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int amqp_messenger_start(AMQP_MESSENGER_HANDLE messenger_handle, SESSION_HANDLE session_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_051: [If `messenger_handle` or `session_handle` are NULL, amqp_messenger_start() shall fail and return non-zero value] + if (messenger_handle == NULL || session_handle == NULL) + { + LogError("Invalid argument (session_handle is NULL)"); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_052: [If `instance->state` is not AMQP_MESSENGER_STATE_STOPPED, amqp_messenger_start() shall fail and return non-zero value] + if (instance->state != AMQP_MESSENGER_STATE_STOPPED) + { + result = __FAILURE__; + LogError("amqp_messenger_start failed (current state is %d; expected AMQP_MESSENGER_STATE_STOPPED)", instance->state); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_053: [`session_handle` shall be saved on `instance->session_handle`] + instance->session_handle = session_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_054: [If no failures occurr, `instance->state` shall be set to AMQP_MESSENGER_STATE_STARTING, and `instance->on_state_changed_callback` invoked if provided] + update_messenger_state(instance, AMQP_MESSENGER_STATE_STARTING); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_055: [If no failures occurr, amqp_messenger_start() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int amqp_messenger_stop(AMQP_MESSENGER_HANDLE messenger_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_056: [If `messenger_handle` is NULL, amqp_messenger_stop() shall fail and return non-zero value] + if (messenger_handle == NULL) + { + result = __FAILURE__; + LogError("Invalid argument (messenger_handle is NULL)"); + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_057: [If `instance->state` is AMQP_MESSENGER_STATE_STOPPED, amqp_messenger_stop() shall fail and return non-zero value] + if (instance->state == AMQP_MESSENGER_STATE_STOPPED) + { + result = __FAILURE__; + LogError("amqp_messenger_stop failed (messenger is already stopped)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_058: [`instance->state` shall be set to AMQP_MESSENGER_STATE_STOPPING, and `instance->on_state_changed_callback` invoked if provided] + update_messenger_state(instance, AMQP_MESSENGER_STATE_STOPPING); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_059: [`instance->message_sender` and `instance->message_receiver` shall be destroyed along with all its links] + destroy_message_sender(instance); + destroy_message_receiver(instance); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_060: [`instance->send_queue` items shall be rolled back using message_queue_move_all_back_to_pending()] + if (message_queue_move_all_back_to_pending(instance->send_queue) != RESULT_OK) + { + LogError("Messenger failed to move events in progress back to wait_to_send list"); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_061: [If message_queue_move_all_back_to_pending() fails, amqp_messenger_stop() shall change the messenger state to AMQP_MESSENGER_STATE_ERROR and return a non-zero value] + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_062: [`instance->state` shall be set to AMQP_MESSENGER_STATE_STOPPED, and `instance->on_state_changed_callback` invoked if provided] + update_messenger_state(instance, AMQP_MESSENGER_STATE_STOPPED); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_063: [If no failures occurr, amqp_messenger_stop() shall return 0] + result = RESULT_OK; + } + } + } + + return result; +} + +// @brief +// Sets the messenger module state based on the state changes from messagesender and messagereceiver +static void process_state_changes(AMQP_MESSENGER_INSTANCE* instance) +{ + // Note: messagesender and messagereceiver are still not created or already destroyed + // when state is AMQP_MESSENGER_STATE_STOPPED, so no checking is needed there. + + if (instance->state == AMQP_MESSENGER_STATE_STARTED) + { + if (instance->message_sender_current_state != MESSAGE_SENDER_STATE_OPEN) + { + LogError("messagesender reported unexpected state %d while messenger was started", instance->message_sender_current_state); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + else if (instance->message_receiver != NULL && instance->message_receiver_current_state != MESSAGE_RECEIVER_STATE_OPEN) + { + if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_OPENING) + { + bool is_timed_out; + if (is_timeout_reached(instance->last_message_receiver_state_change_time, MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK) + { + LogError("messenger got an error (failed to verify messagereceiver start timeout)"); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + else if (is_timed_out) + { + LogError("messenger got an error (messagereceiver failed to start within expected timeout (%d secs))", MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + } + else if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_ERROR || + instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_IDLE) + { + LogError("messagereceiver reported unexpected state %d while messenger is starting", instance->message_receiver_current_state); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + } + } + else + { + if (instance->state == AMQP_MESSENGER_STATE_STARTING) + { + if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPEN) + { + update_messenger_state(instance, AMQP_MESSENGER_STATE_STARTED); + } + else if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPENING) + { + bool is_timed_out; + if (is_timeout_reached(instance->last_message_sender_state_change_time, MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK) + { + LogError("messenger failed to start (failed to verify messagesender start timeout)"); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + else if (is_timed_out) + { + LogError("messenger failed to start (messagesender failed to start within expected timeout (%d secs))", MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + } + // For this module, the only valid scenario where messagesender state is IDLE is if + // the messagesender hasn't been created yet or already destroyed. + else if ((instance->message_sender_current_state == MESSAGE_SENDER_STATE_ERROR) || + (instance->message_sender_current_state == MESSAGE_SENDER_STATE_CLOSING) || + (instance->message_sender_current_state == MESSAGE_SENDER_STATE_IDLE && instance->message_sender != NULL)) + { + LogError("messagesender reported unexpected state %d while messenger is starting", instance->message_sender_current_state); + update_messenger_state(instance, AMQP_MESSENGER_STATE_ERROR); + } + } + // message sender and receiver are stopped/destroyed synchronously, so no need for state control. + } +} + +static void manage_amqp_messengers(AMQP_MESSENGER_INSTANCE* msgr) +{ + if (msgr->state == AMQP_MESSENGER_STATE_STARTING) + { + if (msgr->message_sender == NULL) + { + if (create_message_sender(msgr) != RESULT_OK) + { + update_messenger_state(msgr, AMQP_MESSENGER_STATE_ERROR); + } + } + } + else if (msgr->state == AMQP_MESSENGER_STATE_STARTED) + { + if (msgr->receive_messages == true && + msgr->message_receiver == NULL && + create_message_receiver(msgr) != RESULT_OK) + { + LogError("amqp_messenger_do_work warning (failed creating the message receiver [%s])", msgr->config->device_id); + } + else if (msgr->receive_messages == false && msgr->message_receiver != NULL) + { + destroy_message_receiver(msgr); + } + } +} + +static void handle_errors_and_timeouts(AMQP_MESSENGER_INSTANCE* msgr) +{ + if (msgr->send_error_count >= msgr->max_send_error_count) + { + LogError("Reached max number of consecutive send failures (%d, %d)", msgr->config->device_id, msgr->max_send_error_count); + update_messenger_state(msgr, AMQP_MESSENGER_STATE_ERROR); + } +} + +void amqp_messenger_do_work(AMQP_MESSENGER_HANDLE messenger_handle) +{ + if (messenger_handle == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_064: [If `messenger_handle` is NULL, amqp_messenger_do_work() shall fail and return] + LogError("Invalid argument (messenger_handle is NULL)"); + } + else + { + AMQP_MESSENGER_INSTANCE* msgr = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_065: [amqp_messenger_do_work() shall update the current state according to the states of message sender and receiver] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_068: [If the message sender state changes to MESSAGE_SENDER_STATE_OPEN, amqp_messenger_do_work() shall set the state to AMQP_MESSENGER_STATE_STARTED] + process_state_changes(msgr); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_066: [If the current state is AMQP_MESSENGER_STARTING, amqp_messenger_do_work() shall create and start `instance->message_sender`] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_067: [If the `instance->message_sender` fails to be created/started, amqp_messenger_do_work() shall set the state to AMQP_MESSENGER_STATE_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_069: [If the `instance->receive_messages` is true, amqp_messenger_do_work() shall create and start `instance->message_receiver` if not done before] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_070: [If the `instance->message_receiver` fails to be created/started, amqp_messenger_do_work() shall set the state to AMQP_MESSENGER_STATE_ERROR] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_071: [If the `instance->receive_messages` is false, amqp_messenger_do_work() shall stop and destroy `instance->message_receiver` if not done before] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_072: [If the `instance->message_receiver` fails to be stopped/destroyed, amqp_messenger_do_work() shall set the state to AMQP_MESSENGER_STATE_ERROR] + manage_amqp_messengers(msgr); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_073: [amqp_messenger_do_work() shall invoke message_queue_do_work() on `instance->send_queue`] + if (msgr->state == AMQP_MESSENGER_STATE_STARTED) + { + message_queue_do_work(msgr->send_queue); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_074: [If `instance->send_error_count` DEFAULT_MAX_SEND_ERROR_COUNT (10), amqp_messenger_do_work() shall change the state to AMQP_MESSENGER_STATE_ERROR] + handle_errors_and_timeouts(msgr); + } +} + +void amqp_messenger_destroy(AMQP_MESSENGER_HANDLE messenger_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_123: [If `messenger_handle` is NULL, amqp_messenger_destroy() shall fail and return] + if (messenger_handle == NULL) + { + LogError("invalid argument (messenger_handle is NULL)"); + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_124: [If `instance->state` is not AMQP_MESSENGER_STATE_STOPPED, amqp_messenger_destroy() shall invoke amqp_messenger_stop()] + if (instance->state != AMQP_MESSENGER_STATE_STOPPED) + { + (void)amqp_messenger_stop(messenger_handle); + } + + if (instance->send_queue != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_125: [message_queue_destroy() shall be invoked for `instance->send_queue`] + message_queue_destroy(instance->send_queue); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_126: [amqp_messenger_destroy() shall release all the memory allocated for `instance`] + destroy_configuration(instance->config); + + free((void*)instance); + } +} + +AMQP_MESSENGER_HANDLE amqp_messenger_create(const AMQP_MESSENGER_CONFIG* messenger_config) +{ + AMQP_MESSENGER_HANDLE handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_001: [If `messenger_config` is NULL, amqp_messenger_create() shall return NULL] + if (!is_valid_configuration(messenger_config)) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_002: [If `messenger_config`'s `device_id`, `iothub_host_fqdn`, `receive_link.source_suffix` or `send_link.target_suffix` are NULL, amqp_messenger_create() shall return NULL] + handle = NULL; + } + else + { + AMQP_MESSENGER_INSTANCE* instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_003: [amqp_messenger_create() shall allocate memory for the messenger instance structure (aka `instance`)] + if ((instance = (AMQP_MESSENGER_INSTANCE*)malloc(sizeof(AMQP_MESSENGER_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_004: [If malloc() fails, amqp_messenger_create() shall fail and return NULL] + LogError("Failed allocating AMQP_MESSENGER_INSTANCE"); + handle = NULL; + } + else + { + memset(instance, 0, sizeof(AMQP_MESSENGER_INSTANCE)); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_005: [amqp_messenger_create() shall save a copy of `messenger_config` into `instance`] + if ((instance->config = clone_configuration(messenger_config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_006: [If the copy fails, amqp_messenger_create() shall fail and return NULL] + LogError("Failed copying AMQP messenger configuration"); + handle = NULL; + } + else + { + MESSAGE_QUEUE_CONFIG mq_config; + mq_config.max_retry_count = DEFAULT_EVENT_SEND_RETRY_LIMIT; + mq_config.max_message_enqueued_time_secs = DEFAULT_EVENT_SEND_TIMEOUT_SECS; + mq_config.max_message_processing_time_secs = 0; + mq_config.on_process_message_callback = on_process_message_callback; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_007: [`instance->send_queue` shall be set using message_queue_create(), passing `on_process_message_callback`] + if ((instance->send_queue = message_queue_create(&mq_config)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_008: [If message_queue_create() fails, amqp_messenger_create() shall fail and return NULL] + LogError("Failed creating message queue"); + handle = NULL; + } + else + { + instance->state = AMQP_MESSENGER_STATE_STOPPED; + instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->last_message_sender_state_change_time = INDEFINITE_TIME; + instance->last_message_receiver_state_change_time = INDEFINITE_TIME; + instance->max_send_error_count = DEFAULT_MAX_SEND_ERROR_COUNT; + instance->receive_messages = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_009: [If no failures occurr, amqp_messenger_create() shall return a handle to `instance`] + handle = (AMQP_MESSENGER_HANDLE)instance; + } + } + } + + if (handle == NULL) + { + amqp_messenger_destroy((AMQP_MESSENGER_HANDLE)instance); + } + } + + return handle; +} + +int amqp_messenger_set_option(AMQP_MESSENGER_HANDLE messenger_handle, const char* name, void* value) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_127: [If `messenger_handle` or `name` or `value` is NULL, amqp_messenger_set_option shall fail and return a non-zero value] + if (messenger_handle == NULL || name == NULL || value == NULL) + { + LogError("Invalid argument (messenger_handle=%p, name=%p, value=%p)", + messenger_handle, name, value); + result = __FAILURE__; + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_128: [If name matches AMQP_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, `value` shall be set on `instance->send_queue` using message_queue_set_max_message_enqueued_time_secs()] + if (strcmp(AMQP_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0) + { + if (message_queue_set_max_message_enqueued_time_secs(instance->send_queue, *(size_t*)value) != 0) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_129: [If message_queue_set_max_message_enqueued_time_secs() fails, amqp_messenger_set_option() shall fail and return a non-zero value] + LogError("Failed setting option %s", AMQP_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_131: [If no errors occur, amqp_messenger_set_option shall return 0] + result = RESULT_OK; + } + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_130: [If `name` does not match any supported option, amqp_messenger_set_option() shall fail and return a non-zero value] + LogError("Invalid argument (option '%s' is not valid)", name); + result = __FAILURE__; + } + } + + return result; +} + +OPTIONHANDLER_HANDLE amqp_messenger_retrieve_options(AMQP_MESSENGER_HANDLE messenger_handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_132: [If `messenger_handle` is NULL, amqp_messenger_retrieve_options shall fail and return NULL] + if (messenger_handle == NULL) + { + LogError("Invalid argument (messenger_handle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_133: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create] + result = OptionHandler_Create(amqp_messenger_clone_option, amqp_messenger_destroy_option, (pfSetOption)amqp_messenger_set_option); + + if (result == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_134: [If an OPTIONHANDLER_HANDLE instance fails to be created, amqp_messenger_retrieve_options shall fail and return NULL] + LogError("Failed to retrieve options from messenger instance (OptionHandler_Create failed)"); + } + else + { + AMQP_MESSENGER_INSTANCE* instance = (AMQP_MESSENGER_INSTANCE*)messenger_handle; + OPTIONHANDLER_HANDLE mq_options; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_135: [`instance->send_queue` options shall be retrieved using message_queue_retrieve_options()] + if ((mq_options = message_queue_retrieve_options(instance->send_queue)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_136: [If message_queue_retrieve_options() fails, amqp_messenger_retrieve_options shall fail and return NULL] + LogError("failed to retrieve options from send queue)"); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_139: [If amqp_messenger_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(result); + result = NULL; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_137: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption] + else if (OptionHandler_AddOption(result, MESSENGER_SAVED_MQ_OPTIONS, (void*)mq_options) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_138: [If OptionHandler_AddOption fails, amqp_messenger_retrieve_options shall fail and return NULL] + LogError("failed adding option '%s'", MESSENGER_SAVED_MQ_OPTIONS); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_139: [If amqp_messenger_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(mq_options); + OptionHandler_Destroy(result); + result = NULL; + } + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_140: [If no failures occur, amqp_messenger_retrieve_options shall return the OPTIONHANDLER_HANDLE instance] + return result; +} + +void amqp_messenger_destroy_disposition_info(AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_132: [If `disposition_info` is NULL, amqp_messenger_destroy_disposition_info shall return immediately] + if (disposition_info == NULL) + { + LogError("Invalid argument (disposition_info is NULL)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_132: [All memory allocated for `disposition_info` shall be released] + destroy_message_disposition_info(disposition_info); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_telemetry_messenger.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2171 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/message_receiver.h" +#include "internal/uamqp_messaging.h" +#include "internal/iothub_client_private.h" +#include "iothub_client_version.h" +#include "internal/iothubtransport_amqp_telemetry_messenger.h" + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) + +#define IOTHUB_DEVICES_PATH_FMT "%s/devices/%s" +#define IOTHUB_DEVICES_MODULE_PATH_FMT "%s/devices/%s/modules/%s" +#define IOTHUB_EVENT_SEND_ADDRESS_FMT "amqps://%s/messages/events" +#define IOTHUB_MESSAGE_RECEIVE_ADDRESS_FMT "amqps://%s/messages/devicebound" +#define MESSAGE_SENDER_LINK_NAME_PREFIX "link-snd" +#define MESSAGE_SENDER_MAX_LINK_SIZE UINT64_MAX +#define MESSAGE_RECEIVER_LINK_NAME_PREFIX "link-rcv" +#define MESSAGE_RECEIVER_MAX_LINK_SIZE 65536 +#define DEFAULT_EVENT_SEND_RETRY_LIMIT 10 +#define DEFAULT_EVENT_SEND_TIMEOUT_SECS 600 +#define MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS 300 +#define MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS 300 +#define UNIQUE_ID_BUFFER_SIZE 37 +#define STRING_NULL_TERMINATOR '\0' + +#define AMQP_BATCHING_FORMAT_CODE 0x80013700 + +typedef struct TELEMETRY_MESSENGER_INSTANCE_TAG +{ + STRING_HANDLE device_id; + STRING_HANDLE module_id; + STRING_HANDLE product_info; + STRING_HANDLE iothub_host_fqdn; + SINGLYLINKEDLIST_HANDLE waiting_to_send; // List of MESSENGER_SEND_EVENT_CALLER_INFORMATION's + SINGLYLINKEDLIST_HANDLE in_progress_list; // List of MESSENGER_SEND_EVENT_TASK's + TELEMETRY_MESSENGER_STATE state; + + ON_TELEMETRY_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_context; + + bool receive_messages; + ON_TELEMETRY_MESSENGER_MESSAGE_RECEIVED on_message_received_callback; + void* on_message_received_context; + + SESSION_HANDLE session_handle; + LINK_HANDLE sender_link; + MESSAGE_SENDER_HANDLE message_sender; + MESSAGE_SENDER_STATE message_sender_current_state; + MESSAGE_SENDER_STATE message_sender_previous_state; + LINK_HANDLE receiver_link; + MESSAGE_RECEIVER_HANDLE message_receiver; + MESSAGE_RECEIVER_STATE message_receiver_current_state; + MESSAGE_RECEIVER_STATE message_receiver_previous_state; + + size_t event_send_retry_limit; + size_t event_send_error_count; + size_t event_send_timeout_secs; + time_t last_message_sender_state_change_time; + time_t last_message_receiver_state_change_time; +} TELEMETRY_MESSENGER_INSTANCE; + +// MESSENGER_SEND_EVENT_CALLER_INFORMATION corresponds to a message sent from the API, including +// the message and callback information to alert caller about what happened. +typedef struct MESSENGER_SEND_EVENT_CALLER_INFORMATION_TAG +{ + IOTHUB_MESSAGE_LIST* message; + ON_TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE on_event_send_complete_callback; + void* context; +} MESSENGER_SEND_EVENT_CALLER_INFORMATION; + +// MESSENGER_SEND_EVENT_TASK interfaces with underlying uAMQP layer. It receives the callback +// from this lower layer which is used to pass the results back to the API via the callback_list. +typedef struct MESSENGER_SEND_EVENT_TASK_TAG +{ + SINGLYLINKEDLIST_HANDLE callback_list; // List of MESSENGER_SEND_EVENT_CALLER_INFORMATION's + time_t send_time; + TELEMETRY_MESSENGER_INSTANCE *messenger; + bool is_timed_out; +} MESSENGER_SEND_EVENT_TASK; + + +// @brief +// Evaluates if the ammount of time since start_time is greater or lesser than timeout_in_secs. +// @param is_timed_out +// Set to 1 if a timeout has been reached, 0 otherwise. Not set if any failure occurs. +// @returns +// 0 if no failures occur, non-zero otherwise. +static int is_timeout_reached(time_t start_time, size_t timeout_in_secs, int *is_timed_out) +{ + int result; + + if (start_time == INDEFINITE_TIME) + { + LogError("Failed to verify timeout (start_time is INDEFINITE)"); + result = __FAILURE__; + } + else + { + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed to verify timeout (get_time failed)"); + result = __FAILURE__; + } + else + { + if (get_difftime(current_time, start_time) >= timeout_in_secs) + { + *is_timed_out = 1; + } + else + { + *is_timed_out = 0; + } + + result = RESULT_OK; + } + } + + return result; +} + +static STRING_HANDLE create_devices_and_modules_path(STRING_HANDLE iothub_host_fqdn, STRING_HANDLE device_id, STRING_HANDLE module_id) +{ + STRING_HANDLE devices_and_modules_path; + + if ((devices_and_modules_path = STRING_new()) == NULL) + { + LogError("Failed creating devices_and_modules_path (STRING_new failed)"); + } + else + { + const char* iothub_host_fqdn_char_ptr = STRING_c_str(iothub_host_fqdn); + const char* device_id_char_ptr = STRING_c_str(device_id); + const char* module_id_char_ptr = STRING_c_str(module_id); + + if (module_id_char_ptr != NULL) + { + if (STRING_sprintf(devices_and_modules_path, IOTHUB_DEVICES_MODULE_PATH_FMT, iothub_host_fqdn_char_ptr, device_id_char_ptr, module_id_char_ptr) != RESULT_OK) + { + STRING_delete(devices_and_modules_path); + devices_and_modules_path = NULL; + LogError("Failed creating devices_and_modules_path (STRING_sprintf failed)"); + } + } + else + { + if (STRING_sprintf(devices_and_modules_path, IOTHUB_DEVICES_PATH_FMT, iothub_host_fqdn_char_ptr, device_id_char_ptr) != RESULT_OK) + { + STRING_delete(devices_and_modules_path); + devices_and_modules_path = NULL; + LogError("Failed creating devices_and_modules_path (STRING_sprintf failed)"); + } + } + } + + return devices_and_modules_path; +} + +static STRING_HANDLE create_event_send_address(STRING_HANDLE devices_and_modules_path) +{ + STRING_HANDLE event_send_address; + + if ((event_send_address = STRING_new()) == NULL) + { + LogError("Failed creating the event_send_address (STRING_new failed)"); + } + else + { + const char* devices_and_modules_path_char_ptr = STRING_c_str(devices_and_modules_path); + if (STRING_sprintf(event_send_address, IOTHUB_EVENT_SEND_ADDRESS_FMT, devices_and_modules_path_char_ptr) != RESULT_OK) + { + STRING_delete(event_send_address); + event_send_address = NULL; + LogError("Failed creating the event_send_address (STRING_sprintf failed)"); + } + } + + return event_send_address; +} + +static STRING_HANDLE create_event_sender_source_name(STRING_HANDLE link_name) +{ + STRING_HANDLE source_name; + + if ((source_name = STRING_new()) == NULL) + { + LogError("Failed creating the source_name (STRING_new failed)"); + } + else + { + const char* link_name_char_ptr = STRING_c_str(link_name); + if (STRING_sprintf(source_name, "%s-source", link_name_char_ptr) != RESULT_OK) + { + STRING_delete(source_name); + source_name = NULL; + LogError("Failed creating the source_name (STRING_sprintf failed)"); + } + } + + return source_name; +} + +static STRING_HANDLE create_message_receive_address(STRING_HANDLE devices_and_modules_path) +{ + STRING_HANDLE message_receive_address; + + if ((message_receive_address = STRING_new()) == NULL) + { + LogError("Failed creating the message_receive_address (STRING_new failed)"); + } + else + { + const char* devices_and_modules_path_char_ptr = STRING_c_str(devices_and_modules_path); + if (STRING_sprintf(message_receive_address, IOTHUB_MESSAGE_RECEIVE_ADDRESS_FMT, devices_and_modules_path_char_ptr) != RESULT_OK) + { + STRING_delete(message_receive_address); + message_receive_address = NULL; + LogError("Failed creating the message_receive_address (STRING_sprintf failed)"); + } + } + + return message_receive_address; +} + +static STRING_HANDLE create_message_receiver_target_name(STRING_HANDLE link_name) +{ + STRING_HANDLE target_name; + + if ((target_name = STRING_new()) == NULL) + { + LogError("Failed creating the target_name (STRING_new failed)"); + } + else + { + const char* link_name_char_ptr = STRING_c_str(link_name); + if (STRING_sprintf(target_name, "%s-target", link_name_char_ptr) != RESULT_OK) + { + STRING_delete(target_name); + target_name = NULL; + LogError("Failed creating the target_name (STRING_sprintf failed)"); + } + } + + return target_name; +} + +static STRING_HANDLE create_link_name(const char* prefix, const char* infix) +{ + char* unique_id; + STRING_HANDLE tag = NULL; + + if ((unique_id = (char*)malloc(sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1)) == NULL) + { + LogError("Failed generating an unique tag (malloc failed)"); + } + else + { + memset(unique_id, 0, sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1); + + if (UniqueId_Generate(unique_id, UNIQUE_ID_BUFFER_SIZE) != UNIQUEID_OK) + { + LogError("Failed generating an unique tag (UniqueId_Generate failed)"); + } + else if ((tag = STRING_new()) == NULL) + { + LogError("Failed generating an unique tag (STRING_new failed)"); + } + else if (STRING_sprintf(tag, "%s-%s-%s", prefix, infix, unique_id) != RESULT_OK) + { + STRING_delete(tag); + tag = NULL; + LogError("Failed generating an unique tag (STRING_sprintf failed)"); + } + + free(unique_id); + } + + return tag; +} + +static void update_messenger_state(TELEMETRY_MESSENGER_INSTANCE* instance, TELEMETRY_MESSENGER_STATE new_state) +{ + if (new_state != instance->state) + { + TELEMETRY_MESSENGER_STATE previous_state = instance->state; + instance->state = new_state; + + if (instance->on_state_changed_callback != NULL) + { + instance->on_state_changed_callback(instance->on_state_changed_context, previous_state, new_state); + } + } +} + +static void attach_device_client_type_to_link(LINK_HANDLE link, STRING_HANDLE product_info) +{ + fields attach_properties; + AMQP_VALUE device_client_type_key_name; + AMQP_VALUE device_client_type_value; + int result; + + if ((attach_properties = amqpvalue_create_map()) == NULL) + { + LogError("Failed to create the map for device client type."); + } + else + { + if ((device_client_type_key_name = amqpvalue_create_symbol("com.microsoft:client-version")) == NULL) + { + LogError("Failed to create the key name for the device client type."); + } + else + { + if ((device_client_type_value = amqpvalue_create_string(STRING_c_str(product_info))) == NULL) + { + LogError("Failed to create the key value for the device client type."); + } + else + { + if ((result = amqpvalue_set_map_value(attach_properties, device_client_type_key_name, device_client_type_value)) != 0) + { + LogError("Failed to set the property map for the device client type (error code is: %d)", result); + } + else if ((result = link_set_attach_properties(link, attach_properties)) != 0) + { + LogError("Unable to attach the device client type to the link properties (error code is: %d)", result); + } + + amqpvalue_destroy(device_client_type_value); + } + + amqpvalue_destroy(device_client_type_key_name); + } + + amqpvalue_destroy(attach_properties); + } +} + +static void destroy_event_sender(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + if (instance->message_sender != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_060: [`instance->message_sender` shall be destroyed using messagesender_destroy()] + messagesender_destroy(instance->message_sender); + instance->message_sender = NULL; + } + + instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE; + instance->last_message_sender_state_change_time = INDEFINITE_TIME; + + if (instance->sender_link != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_063: [`instance->sender_link` shall be destroyed using link_destroy()] + link_destroy(instance->sender_link); + instance->sender_link = NULL; + } +} + +static void on_event_sender_state_changed_callback(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state) +{ + if (context == NULL) + { + LogError("on_event_sender_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored"); + } + else + { + if (new_state != previous_state) + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)context; + instance->message_sender_current_state = new_state; + instance->message_sender_previous_state = previous_state; + instance->last_message_sender_state_change_time = get_time(NULL); + } + } +} + +static int create_event_sender(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + int result; + + STRING_HANDLE link_name = NULL; + STRING_HANDLE source_name = NULL; + AMQP_VALUE source = NULL; + AMQP_VALUE target = NULL; + STRING_HANDLE devices_and_modules_path = NULL; + STRING_HANDLE event_send_address = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_033: [A variable, named `devices_and_modules_path`, shall be created concatenating `instance->iothub_host_fqdn`, "/devices/" and `instance->device_id` (and "/modules/" and `instance->module_id` if modules are present)] + if ((devices_and_modules_path = create_devices_and_modules_path(instance->iothub_host_fqdn, instance->device_id, instance->module_id)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_034: [If `devices_and_modules_path` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (failed creating the 'devices_and_modules_path')"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_035: [A variable, named `event_send_address`, shall be created concatenating "amqps://", `devices_and_modules_path` and "/messages/events"] + else if ((event_send_address = create_event_send_address(devices_and_modules_path)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_036: [If `event_send_address` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (failed creating the 'event_send_address')"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_037: [A `link_name` variable shall be created using an unique string label per AMQP session] + else if ((link_name = create_link_name(MESSAGE_SENDER_LINK_NAME_PREFIX, STRING_c_str(instance->device_id))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_038: [If `link_name` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (failed creating an unique link name)"); + } + else if ((source_name = create_event_sender_source_name(link_name)) == NULL) + { + result = __FAILURE__; + LogError("Failed creating the message sender (failed creating an unique source name)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_039: [A `source` variable shall be created with messaging_create_source() using an unique string label per AMQP session] + else if ((source = messaging_create_source(STRING_c_str(source_name))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_040: [If `source` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (messaging_create_source failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_041: [A `target` variable shall be created with messaging_create_target() using `event_send_address`] + else if ((target = messaging_create_target(STRING_c_str(event_send_address))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_042: [If `target` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (messaging_create_target failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_043: [`instance->sender_link` shall be set using link_create(), passing `instance->session_handle`, `link_name`, "role_sender", `source` and `target` as parameters] + else if ((instance->sender_link = link_create(instance->session_handle, STRING_c_str(link_name), role_sender, source, target)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_044: [If link_create() fails, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message sender (link_create failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_047: [`instance->sender_link` maximum message size shall be set to UINT64_MAX using link_set_max_message_size()] + if (link_set_max_message_size(instance->sender_link, MESSAGE_SENDER_MAX_LINK_SIZE) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_048: [If link_set_max_message_size() fails, it shall be logged and ignored.] + LogError("Failed setting message sender link max message size."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_049: [`instance->sender_link` should have a property "com.microsoft:client-version" set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`, using amqpvalue_set_map_value() and link_set_attach_properties()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_050: [If amqpvalue_set_map_value() or link_set_attach_properties() fail, the failure shall be ignored] + attach_device_client_type_to_link(instance->sender_link, instance->product_info); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_051: [`instance->message_sender` shall be created using messagesender_create(), passing the `instance->sender_link` and `on_event_sender_state_changed_callback`] + if ((instance->message_sender = messagesender_create(instance->sender_link, on_event_sender_state_changed_callback, (void*)instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_052: [If messagesender_create() fails, telemetry_messenger_do_work() shall fail and return] + LogError("Failed creating the message sender (messagesender_create failed)"); + destroy_event_sender(instance); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_053: [`instance->message_sender` shall be opened using messagesender_open()] + if (messagesender_open(instance->message_sender) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_054: [If messagesender_open() fails, telemetry_messenger_do_work() shall fail and return] + LogError("Failed opening the AMQP message sender."); + destroy_event_sender(instance); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_055: [Before returning, telemetry_messenger_do_work() shall release all the temporary memory it has allocated] + if (link_name != NULL) + STRING_delete(link_name); + if (source_name != NULL) + STRING_delete(source_name); + if (source != NULL) + amqpvalue_destroy(source); + if (target != NULL) + amqpvalue_destroy(target); + if (devices_and_modules_path != NULL) + STRING_delete(devices_and_modules_path); + if (event_send_address != NULL) + STRING_delete(event_send_address); + + return result; +} + +static void destroy_message_receiver(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + if (instance->message_receiver != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_061: [`instance->message_receiver` shall be closed using messagereceiver_close()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_093: [`instance->message_receiver` shall be closed using messagereceiver_close()] + if (messagereceiver_close(instance->message_receiver) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_094: [If messagereceiver_close() fails, it shall be logged and ignored] + LogError("Failed closing the AMQP message receiver (this failure will be ignored)."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_062: [`instance->message_receiver` shall be destroyed using messagereceiver_destroy()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_095: [`instance->message_receiver` shall be destroyed using messagereceiver_destroy()] + messagereceiver_destroy(instance->message_receiver); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_096: [`instance->message_receiver` shall be set to NULL] + instance->message_receiver = NULL; + } + + instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->last_message_receiver_state_change_time = INDEFINITE_TIME; + + if (instance->receiver_link != NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_064: [`instance->receiver_link` shall be destroyed using link_destroy()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_097: [`instance->receiver_link` shall be destroyed using link_destroy()] + link_destroy(instance->receiver_link); + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_098: [`instance->receiver_link` shall be set to NULL] + instance->receiver_link = NULL; + } +} + +static void on_message_receiver_state_changed_callback(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state) +{ + if (context == NULL) + { + LogError("on_message_receiver_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored"); + } + else + { + if (new_state != previous_state) + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)context; + instance->message_receiver_current_state = new_state; + instance->message_receiver_previous_state = previous_state; + instance->last_message_receiver_state_change_time = get_time(NULL); + } + } +} + +static TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* create_message_disposition_info(TELEMETRY_MESSENGER_INSTANCE* messenger) +{ + TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* result; + + if ((result = (TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO))) == NULL) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO container (malloc failed)"); + result = NULL; + } + else + { + delivery_number message_id; + + if (messagereceiver_get_received_message_id(messenger->message_receiver, &message_id) != RESULT_OK) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_received_message_id failed)"); + free(result); + result = NULL; + } + else + { + const char* link_name; + + if (messagereceiver_get_link_name(messenger->message_receiver, &link_name) != RESULT_OK) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_link_name failed)"); + free(result); + result = NULL; + } + else if (mallocAndStrcpy_s(&result->source, link_name) != RESULT_OK) + { + LogError("Failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO container (failed copying link name)"); + free(result); + result = NULL; + } + else + { + result->message_id = message_id; + } + } + } + + return result; +} + +static void destroy_message_disposition_info(TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info) +{ + free(disposition_info->source); + free(disposition_info); +} + +static AMQP_VALUE create_uamqp_disposition_result_from(TELEMETRY_MESSENGER_DISPOSITION_RESULT disposition_result) +{ + AMQP_VALUE uamqp_disposition_result; + + if (disposition_result == TELEMETRY_MESSENGER_DISPOSITION_RESULT_NONE) + { + uamqp_disposition_result = NULL; // intentionally not sending an answer. + } + else if (disposition_result == TELEMETRY_MESSENGER_DISPOSITION_RESULT_ACCEPTED) + { + uamqp_disposition_result = messaging_delivery_accepted(); + } + else if (disposition_result == TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED) + { + uamqp_disposition_result = messaging_delivery_released(); + } + else if (disposition_result == TELEMETRY_MESSENGER_DISPOSITION_RESULT_REJECTED) + { + uamqp_disposition_result = messaging_delivery_rejected("Rejected by application", "Rejected by application"); + } + else + { + LogError("Failed creating a disposition result for messagereceiver (result %d is not supported)", disposition_result); + uamqp_disposition_result = NULL; + } + + return uamqp_disposition_result; +} + +static AMQP_VALUE on_message_received_internal_callback(const void* context, MESSAGE_HANDLE message) +{ + AMQP_VALUE result; + int api_call_result; + IOTHUB_MESSAGE_HANDLE iothub_message; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_121: [An IOTHUB_MESSAGE_HANDLE shall be obtained from MESSAGE_HANDLE using message_create_IoTHubMessage_from_uamqp_message()] + if ((api_call_result = message_create_IoTHubMessage_from_uamqp_message(message, &iothub_message)) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_122: [If message_create_IoTHubMessage_from_uamqp_message() fails, on_message_received_internal_callback shall return the result of messaging_delivery_rejected()] + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed reading AMQP message"); + + LogError("on_message_received_internal_callback failed (message_create_IoTHubMessage_from_uamqp_message; error = %d).", api_call_result); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)context; + TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* message_disposition_info; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_186: [A TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be created containing the source link name and message delivery ID] + if ((message_disposition_info = create_message_disposition_info(instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_187: [**If the TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance fails to be created, on_message_received_internal_callback shall return messaging_delivery_released()] + LogError("on_message_received_internal_callback failed (failed creating TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO)."); + result = messaging_delivery_released(); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_123: [`instance->on_message_received_callback` shall be invoked passing the IOTHUB_MESSAGE_HANDLE and TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance] + TELEMETRY_MESSENGER_DISPOSITION_RESULT disposition_result = instance->on_message_received_callback(iothub_message, message_disposition_info, instance->on_message_received_context); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_188: [The memory allocated for the TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be released] + destroy_message_disposition_info(message_disposition_info); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_125: [If `instance->on_message_received_callback` returns TELEMETRY_MESSENGER_DISPOSITION_RESULT_ACCEPTED, on_message_received_internal_callback shall return the result of messaging_delivery_accepted()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_126: [If `instance->on_message_received_callback` returns TELEMETRY_MESSENGER_DISPOSITION_RESULT_RELEASED, on_message_received_internal_callback shall return the result of messaging_delivery_released()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_127: [If `instance->on_message_received_callback` returns TELEMETRY_MESSENGER_DISPOSITION_RESULT_REJECTED, on_message_received_internal_callback shall return the result of messaging_delivery_rejected()] + result = create_uamqp_disposition_result_from(disposition_result); + } + } + + return result; +} + +static int create_message_receiver(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + int result; + + STRING_HANDLE devices_and_modules_path = NULL; + STRING_HANDLE message_receive_address = NULL; + STRING_HANDLE link_name = NULL; + STRING_HANDLE target_name = NULL; + AMQP_VALUE source = NULL; + AMQP_VALUE target = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_068: [A variable, named `devices_and_modules_path`, shall be created concatenating `instance->iothub_host_fqdn`, "/devices/" and `instance->device_id` (and "/modules/" and `instance->module_id` if modules are present)] + if ((devices_and_modules_path = create_devices_and_modules_path(instance->iothub_host_fqdn, instance->device_id, instance->module_id)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_069: [If `devices_and_modules_path` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (failed creating the 'devices_and_modules_path')"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_070: [A variable, named `message_receive_address`, shall be created concatenating "amqps://", `devices_and_modules_path` and "/messages/devicebound"] + else if ((message_receive_address = create_message_receive_address(devices_and_modules_path)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_071: [If `message_receive_address` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (failed creating the 'message_receive_address')"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_072: [A `link_name` variable shall be created using an unique string label per AMQP session] + else if ((link_name = create_link_name(MESSAGE_RECEIVER_LINK_NAME_PREFIX, STRING_c_str(instance->device_id))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_073: [If `link_name` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (failed creating an unique link name)"); + } + else if ((target_name = create_message_receiver_target_name(link_name)) == NULL) + { + result = __FAILURE__; + LogError("Failed creating the message receiver (failed creating an unique target name)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_074: [A `target` variable shall be created with messaging_create_target() using an unique string label per AMQP session] + else if ((target = messaging_create_target(STRING_c_str(target_name))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_075: [If `target` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (messaging_create_target failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_076: [A `source` variable shall be created with messaging_create_source() using `message_receive_address`] + else if ((source = messaging_create_source(STRING_c_str(message_receive_address))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_077: [If `source` fails to be created, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (messaging_create_source failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_078: [`instance->receiver_link` shall be set using link_create(), passing `instance->session_handle`, `link_name`, "role_receiver", `source` and `target` as parameters] + else if ((instance->receiver_link = link_create(instance->session_handle, STRING_c_str(link_name), role_receiver, source, target)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_079: [If link_create() fails, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (link_create failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_080: [`instance->receiver_link` settle mode shall be set to "receiver_settle_mode_first" using link_set_rcv_settle_mode(), ] + else if (link_set_rcv_settle_mode(instance->receiver_link, receiver_settle_mode_first) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_081: [If link_set_rcv_settle_mode() fails, telemetry_messenger_do_work() shall fail and return] + result = __FAILURE__; + LogError("Failed creating the message receiver (link_set_rcv_settle_mode failed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_082: [`instance->receiver_link` maximum message size shall be set to 65536 using link_set_max_message_size()] + if (link_set_max_message_size(instance->receiver_link, MESSAGE_RECEIVER_MAX_LINK_SIZE) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_083: [If link_set_max_message_size() fails, it shall be logged and ignored.] + LogError("Failed setting message receiver link max message size."); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_084: [`instance->receiver_link` should have a property "com.microsoft:client-version" set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`, using amqpvalue_set_map_value() and link_set_attach_properties()] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_085: [If amqpvalue_set_map_value() or link_set_attach_properties() fail, the failure shall be ignored] + attach_device_client_type_to_link(instance->receiver_link, instance->product_info); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_086: [`instance->message_receiver` shall be created using messagereceiver_create(), passing the `instance->receiver_link` and `on_messagereceiver_state_changed_callback`] + if ((instance->message_receiver = messagereceiver_create(instance->receiver_link, on_message_receiver_state_changed_callback, (void*)instance)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_087: [If messagereceiver_create() fails, telemetry_messenger_do_work() shall fail and return] + LogError("Failed creating the message receiver (messagereceiver_create failed)"); + destroy_message_receiver(instance); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_088: [`instance->message_receiver` shall be opened using messagereceiver_open(), passing `on_message_received_internal_callback`] + if (messagereceiver_open(instance->message_receiver, on_message_received_internal_callback, (void*)instance) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_089: [If messagereceiver_open() fails, telemetry_messenger_do_work() shall fail and return] + LogError("Failed opening the AMQP message receiver."); + destroy_message_receiver(instance); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + } + + if (devices_and_modules_path != NULL) + STRING_delete(devices_and_modules_path); + if (message_receive_address != NULL) + STRING_delete(message_receive_address); + if (link_name != NULL) + STRING_delete(link_name); + if (target_name != NULL) + STRING_delete(target_name); + if (source != NULL) + amqpvalue_destroy(source); + if (target != NULL) + amqpvalue_destroy(target); + + return result; +} + +static int move_event_to_in_progress_list(MESSENGER_SEND_EVENT_TASK* task) +{ + int result; + + if (singlylinkedlist_add(task->messenger->in_progress_list, (void*)task) == NULL) + { + result = __FAILURE__; + LogError("Failed moving event to in_progress list (singlylinkedlist_add failed)"); + } + else + { + result = RESULT_OK; + } + + return result; +} + +static bool find_MESSENGER_SEND_EVENT_TASK_on_list(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + return (list_item != NULL && singlylinkedlist_item_get_value(list_item) == match_context); +} + +static void remove_event_from_in_progress_list(MESSENGER_SEND_EVENT_TASK *task) +{ + LIST_ITEM_HANDLE list_item = singlylinkedlist_find(task->messenger->in_progress_list, find_MESSENGER_SEND_EVENT_TASK_on_list, (void*)task); + + if (list_item != NULL) + { + if (singlylinkedlist_remove(task->messenger->in_progress_list, list_item) != RESULT_OK) + { + LogError("Failed removing event from in_progress list (singlylinkedlist_remove failed)"); + } + } +} + +// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_201: [Freeing a `task` will free callback items associated with it and free the data itself] +static void free_task(MESSENGER_SEND_EVENT_TASK* task) +{ + LIST_ITEM_HANDLE list_node; + + if (NULL != task->callback_list) + { + while ((list_node = singlylinkedlist_get_head_item(task->callback_list)) != NULL) + { + MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_info = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)singlylinkedlist_item_get_value(list_node); + (void)singlylinkedlist_remove(task->callback_list, list_node); + free(caller_info); + } + singlylinkedlist_destroy(task->callback_list); + } + + free(task); +} + +static int copy_events_to_list(SINGLYLINKEDLIST_HANDLE from_list, SINGLYLINKEDLIST_HANDLE to_list) +{ + int result; + LIST_ITEM_HANDLE list_item; + + result = RESULT_OK; + list_item = singlylinkedlist_get_head_item(from_list); + + while (list_item != NULL) + { + MESSENGER_SEND_EVENT_TASK *task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item); + + if (singlylinkedlist_add(to_list, task) == NULL) + { + LogError("Failed copying event to destination list (singlylinkedlist_add failed)"); + result = __FAILURE__; + break; + } + else + { + list_item = singlylinkedlist_get_next_item(list_item); + } + } + + return result; +} + +static int copy_events_from_in_progress_to_waiting_list(TELEMETRY_MESSENGER_INSTANCE* instance, SINGLYLINKEDLIST_HANDLE to_list) +{ + int result; + LIST_ITEM_HANDLE list_task_item; + LIST_ITEM_HANDLE list_task_item_next; + + result = RESULT_OK; + list_task_item = singlylinkedlist_get_head_item(instance->in_progress_list); + + while (list_task_item != NULL) + { + MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_task_item); + + LIST_ITEM_HANDLE list_caller_item; + + list_caller_item = singlylinkedlist_get_head_item(task->callback_list); + + while (list_caller_item != NULL) + { + MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_information = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)singlylinkedlist_item_get_value(list_caller_item); + + if (singlylinkedlist_add(to_list, caller_information) == NULL) + { + LogError("Failed copying event to destination list (singlylinkedlist_add failed)"); + result = __FAILURE__; + break; + } + + list_caller_item = singlylinkedlist_get_next_item(list_caller_item); + } + + list_task_item_next = singlylinkedlist_get_next_item(list_task_item); + + singlylinkedlist_destroy(task->callback_list); + task->callback_list = NULL; + + free_task(task); + singlylinkedlist_remove(instance->in_progress_list, list_task_item); + list_task_item = list_task_item_next; + } + + return result; +} + + +static int move_events_to_wait_to_send_list(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + int result; + LIST_ITEM_HANDLE list_item; + + if ((list_item = singlylinkedlist_get_head_item(instance->in_progress_list)) == NULL) + { + result = RESULT_OK; + } + else + { + SINGLYLINKEDLIST_HANDLE new_wait_to_send_list; + + if ((new_wait_to_send_list = singlylinkedlist_create()) == NULL) + { + LogError("Failed moving events back to wait_to_send list (singlylinkedlist_create failed to create new wait_to_send_list)"); + result = __FAILURE__; + } + else + { + SINGLYLINKEDLIST_HANDLE new_in_progress_list; + + if (copy_events_from_in_progress_to_waiting_list(instance, new_wait_to_send_list) != RESULT_OK) + { + LogError("Failed moving events back to wait_to_send list (failed adding in_progress_list items to new_wait_to_send_list)"); + singlylinkedlist_destroy(new_wait_to_send_list); + result = __FAILURE__; + } + else if (copy_events_to_list(instance->waiting_to_send, new_wait_to_send_list) != RESULT_OK) + { + LogError("Failed moving events back to wait_to_send list (failed adding wait_to_send items to new_wait_to_send_list)"); + singlylinkedlist_destroy(new_wait_to_send_list); + result = __FAILURE__; + } + else if ((new_in_progress_list = singlylinkedlist_create()) == NULL) + { + LogError("Failed moving events back to wait_to_send list (singlylinkedlist_create failed to create new in_progress_list)"); + singlylinkedlist_destroy(new_wait_to_send_list); + result = __FAILURE__; + } + else + { + singlylinkedlist_destroy(instance->waiting_to_send); + singlylinkedlist_destroy(instance->in_progress_list); + instance->waiting_to_send = new_wait_to_send_list; + instance->in_progress_list = new_in_progress_list; + result = RESULT_OK; + } + } + } + + return result; +} + +static MESSENGER_SEND_EVENT_TASK* create_task(TELEMETRY_MESSENGER_INSTANCE *messenger) +{ + MESSENGER_SEND_EVENT_TASK* task = NULL; + + if (NULL == (task = (MESSENGER_SEND_EVENT_TASK *)malloc(sizeof(MESSENGER_SEND_EVENT_TASK)))) + { + LogError("malloc of MESSENGER_SEND_EVENT_TASK failed"); + } + else + { + memset(task, 0, sizeof(*task )); + task->messenger = messenger; + task->send_time = INDEFINITE_TIME; + if (NULL == (task->callback_list = singlylinkedlist_create())) + { + LogError("singlylinkedlist_create failed to create callback_list"); + free_task(task); + task = NULL; + } + else if (RESULT_OK != move_event_to_in_progress_list(task)) + { + LogError("move_event_to_in_progress_list failed"); + free_task(task); + task = NULL; + } + } + return task; +} + + + +static void invoke_callback(const void* item, const void* action_context, bool* continue_processing) +{ + MESSENGER_SEND_EVENT_CALLER_INFORMATION *caller_info = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)item; + + if (NULL != caller_info->on_event_send_complete_callback) + { +#pragma warning(push) +#pragma warning(disable:4305) // Allow typecasting to smaller type on 64 bit systems, since we control ultimate caller. + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_send_result = (TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT)action_context; +#pragma warning(pop) + caller_info->on_event_send_complete_callback(caller_info->message, messenger_send_result, caller_info->context); + } + *continue_processing = true; +} + +static void internal_on_event_send_complete_callback(void* context, MESSAGE_SEND_RESULT send_result, AMQP_VALUE delivery_state) +{ + (void)delivery_state; + if (context != NULL) + { + MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)context; + + if (task->messenger->message_sender_current_state != MESSAGE_SENDER_STATE_ERROR) + { + if (task->is_timed_out == false) + { + TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_send_result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_189: [If no failure occurs, `on_event_send_complete_callback` shall be invoked with result TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK for all callers associated with this task] + if (send_result == MESSAGE_SEND_OK) + { + messenger_send_result = TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_190: [If a failure occured, `on_event_send_complete_callback` shall be invoked with result TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING for all callers associated with this task] + else + { + messenger_send_result = TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING; + } + + // Initially typecast to a size_t to avoid 64 bit compiler warnings on casting of void* to larger type. + singlylinkedlist_foreach(task->callback_list, invoke_callback, (void*)((size_t)messenger_send_result)); + } + else + { + LogInfo("messenger on_event_send_complete_callback invoked for timed out event %p; not firing upper layer callback.", task); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_128: [`task` shall be removed from `instance->in_progress_list`] + remove_event_from_in_progress_list(task); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_130: [`task` shall be destroyed()] + free_task(task); + } + } +} + +static MESSENGER_SEND_EVENT_CALLER_INFORMATION* get_next_caller_message_to_send(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_info; + LIST_ITEM_HANDLE list_item; + + if ((list_item = singlylinkedlist_get_head_item(instance->waiting_to_send)) == NULL) + { + caller_info = NULL; + } + else + { + caller_info = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)singlylinkedlist_item_get_value(list_item); + + if (singlylinkedlist_remove(instance->waiting_to_send, list_item) != RESULT_OK) + { + LogError("Failed removing item from waiting_to_send list (singlylinkedlist_remove failed)"); + } + } + + return caller_info; +} + +typedef struct SEND_PENDING_EVENTS_STATE_TAG +{ + MESSENGER_SEND_EVENT_TASK* task; + MESSAGE_HANDLE message_batch_container; + uint64_t bytes_pending; +} SEND_PENDING_EVENTS_STATE; + + +// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_191: [Creates an AMQP message, sets it to be batch mode, and creates an associated task for its callbacks. Errors cause the send events loop to break.] +static int create_send_pending_events_state(TELEMETRY_MESSENGER_INSTANCE* instance, SEND_PENDING_EVENTS_STATE *send_pending_events_state) +{ + int result; + memset(send_pending_events_state, 0, sizeof(*send_pending_events_state)); + + if ((send_pending_events_state->message_batch_container = message_create()) == NULL) + { + LogError("messageBatchContainer = message_create() failed"); + result = __FAILURE__; + } + else if (message_set_message_format(send_pending_events_state->message_batch_container, AMQP_BATCHING_FORMAT_CODE) != 0) + { + LogError("Failed setting the message format to batching format"); + result = __FAILURE__; + } + else if ((send_pending_events_state->task = create_task(instance)) == NULL) + { + LogError("create_task() failed"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static void invoke_callback_on_error(MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_event_send_complete_result) +{ + caller_info->on_event_send_complete_callback(caller_info->message, messenger_event_send_complete_result, (void*)caller_info->context); +} + +// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_194: [When message is ready to send, invoke AMQP's messagesender_send and free temporary values associated with this batch.] +static int send_batched_message_and_reset_state(TELEMETRY_MESSENGER_INSTANCE* instance, SEND_PENDING_EVENTS_STATE *send_pending_events_state) +{ + int result; + + if (messagesender_send_async(instance->message_sender, send_pending_events_state->message_batch_container, internal_on_event_send_complete_callback, send_pending_events_state->task, 0) == NULL) + { + LogError("messagesender_send failed"); + result = __FAILURE__; + } + else + { + send_pending_events_state->task->send_time = get_time(NULL); + result = RESULT_OK; + } + + message_destroy(send_pending_events_state->message_batch_container); + memset(send_pending_events_state, 0, sizeof(*send_pending_events_state)); + + return result; +} + +// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_196: [Determine the maximum message size we can send over this link from AMQP, then remove AMQP_BATCHING_RESERVE_SIZE (1024) bytes as reserve buffer.] +static int get_max_message_size_for_batching(TELEMETRY_MESSENGER_INSTANCE* instance, uint64_t* max_messagesize) +{ + int result; + + if (link_get_peer_max_message_size(instance->sender_link, max_messagesize) != 0) + { + LogError("link_get_peer_max_message_size failed"); + result = __FAILURE__; + } + // Reserve AMQP_BATCHING_RESERVE_SIZE bytes for AMQP overhead of the "main" message itself. + else if (*max_messagesize <= AMQP_BATCHING_RESERVE_SIZE) + { + LogError("link_get_peer_max_message_size (%d) is less than the reserve size (%d)", max_messagesize, AMQP_BATCHING_RESERVE_SIZE); + result = __FAILURE__; + } + else + { + *max_messagesize -= AMQP_BATCHING_RESERVE_SIZE; + result = 0; + } + + return result; +} + +static int send_pending_events(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + int result = RESULT_OK; + + MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_info; + BINARY_DATA body_binary_data; + + SEND_PENDING_EVENTS_STATE send_pending_events_state; + memset(&send_pending_events_state, 0, sizeof(send_pending_events_state)); + memset(&body_binary_data, 0, sizeof(body_binary_data)); + + uint64_t max_messagesize = 0; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_192: [Enumerate through all messages waiting to send, building up AMQP message to send and sending when size will be greater than link max size.] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_198: [While processing pending messages, errors shall result in user callback being invoked.] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_199: [Errors specific to a message (e.g. failure to encode) are NOT fatal but we'll keep processing. More general errors (e.g. out of memory) will stop processing.] + while ((caller_info = get_next_caller_message_to_send(instance)) != NULL) + { + if (body_binary_data.bytes != NULL) + { + // Free here as this may have been set during previous run through loop. + free((unsigned char*)body_binary_data.bytes); + } + memset(&body_binary_data, 0, sizeof(body_binary_data)); + + if ((0 == max_messagesize) && (get_max_message_size_for_batching(instance, &max_messagesize)) != 0) + { + LogError("get_max_message_size_for_batching failed"); + invoke_callback_on_error(caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING); + free(caller_info); + result = __FAILURE__; + break; + } + else if ((send_pending_events_state.task == 0) && (create_send_pending_events_state(instance, &send_pending_events_state) != 0)) + { + LogError("create_send_pending_events_state failed"); + invoke_callback_on_error(caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING); + free(caller_info); + result = __FAILURE__; + break; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_200: [Retrieve an AMQP encoded representation of this message for later appending to main batched message. On error, invoke callback but continue send loop; this is NOT a fatal error.] + else if (message_create_uamqp_encoding_from_iothub_message(send_pending_events_state.message_batch_container, caller_info->message->messageHandle, &body_binary_data) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_201: [If message_create_uamqp_encoding_from_iothub_message fails, invoke callback with TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE] + LogError("message_create_uamqp_encoding_from_iothub_message() failed. Will continue to try to process messages, result"); + invoke_callback_on_error(caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE); + free(caller_info); + continue; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_197: [If a single message is greater than our maximum AMQP send size, ignore the message. Invoke the callback but continue send loop; this is NOT a fatal error.] + else if (body_binary_data.length > (int)max_messagesize) + { + LogError("a single message will encode to be %d bytes, larger than max we will send the link %lld. Will continue to try to process messages", body_binary_data.length, max_messagesize); + invoke_callback_on_error(caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING); + free(caller_info); + continue; + } + else if (singlylinkedlist_add(send_pending_events_state.task->callback_list, (void*)caller_info) == NULL) + { + LogError("singlylinkedlist_add failed"); + invoke_callback_on_error(caller_info, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING); + free(caller_info); + result = __FAILURE__; + break; + } + + // Once we've added the caller_info to the callback_list, don't directly 'invoke_callback_on_error' anymore directly. + // The task is responsible for running through its callers for callbacks, even for errors in this function. + // Similarly, responsibility for freeing this memory falls on the 'task' cleanup also. + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_193: [If (length of current user AMQP message) + (length of user messages pending for this batched message) + (1KB reserve buffer) > maximum link send, send pending messages and create new batched message.] + if (body_binary_data.length + send_pending_events_state.bytes_pending > max_messagesize) + { + // If we tried to add the current message, we would overflow. Send what we've queued immediately. + if (send_batched_message_and_reset_state(instance, &send_pending_events_state) != RESULT_OK) + { + LogError("send_batched_message_and_reset_state failed"); + result = __FAILURE__; + break; + } + + // Now that we've given off the task to uAMQP layer, allocate a new task. + if (create_send_pending_events_state(instance, &send_pending_events_state) != 0) + { + LogError("create_send_pending_events_state failed, result"); + result = __FAILURE__; + break; + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_31_195: [Append the current message's encoded data to the batched message tracked by uAMQP layer.] + if (message_add_body_amqp_data(send_pending_events_state.message_batch_container, body_binary_data) != 0) + { + LogError("message_add_body_amqp_data failed"); + result = __FAILURE__; + break; + } + + send_pending_events_state.bytes_pending += body_binary_data.length; + } + + if ((result == 0) && (send_pending_events_state.bytes_pending != 0)) + { + if (send_batched_message_and_reset_state(instance, &send_pending_events_state) != RESULT_OK) + { + LogError("send_batched_message_and_reset_state failed"); + result = __FAILURE__; + } + } + + if (body_binary_data.bytes != NULL) + { + free((unsigned char*)body_binary_data.bytes); + } + + // A non-NULL task indicates error, since otherwise send_batched_message_and_reset_state would've sent off messages and reset send_pending_events_state + if (send_pending_events_state.task != NULL) + { + singlylinkedlist_foreach(send_pending_events_state.task->callback_list, invoke_callback, (void*)TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING); + remove_event_from_in_progress_list(send_pending_events_state.task); + free_task(send_pending_events_state.task); + } + + if (send_pending_events_state.message_batch_container != NULL) + { + message_destroy(send_pending_events_state.message_batch_container); + } + + return result; +} + +// @brief +// Goes through each task in in_progress_list and checks if the events timed out to be sent. +// @remarks +// If an event is timed out, it is marked as such but not removed, and the upper layer callback is invoked. +// @returns +// 0 if no failures occur, non-zero otherwise. +static int process_event_send_timeouts(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + int result = RESULT_OK; + + if (instance->event_send_timeout_secs > 0) + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->in_progress_list); + + while (list_item != NULL) + { + MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item); + + if (task->is_timed_out == false) + { + int is_timed_out; + + if (is_timeout_reached(task->send_time, instance->event_send_timeout_secs, &is_timed_out) == RESULT_OK) + { + if (is_timed_out) + { + task->is_timed_out = true; + singlylinkedlist_foreach(task->callback_list, invoke_callback, (void*)TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT); + } + } + else + { + LogError("messenger failed to evaluate event send timeout of event %d", task); + result = __FAILURE__; + } + } + + list_item = singlylinkedlist_get_next_item(list_item); + } + } + + return result; +} + +// @brief +// Removes all the timed out events from the in_progress_list, without invoking callbacks or detroying the messages. +static void remove_timed_out_events(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->in_progress_list); + + while (list_item != NULL) + { + MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item); + + if (task->is_timed_out == true) + { + remove_event_from_in_progress_list(task); + + free_task(task); + } + + list_item = singlylinkedlist_get_next_item(list_item); + } +} + + +// ---------- Set/Retrieve Options Helpers ----------// + +static void* telemetry_messenger_clone_option(const char* name, const void* value) +{ + void* result; + + if (name == NULL) + { + LogError("Failed to clone messenger option (name is NULL)"); + result = NULL; + } + else if (value == NULL) + { + LogError("Failed to clone messenger option (value is NULL)"); + result = NULL; + } + else + { + if (strcmp(TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0 || + strcmp(TELEMETRY_MESSENGER_OPTION_SAVED_OPTIONS, name) == 0) + { + result = (void*)value; + } + else + { + LogError("Failed to clone messenger option (option with name '%s' is not suppported)", name); + result = NULL; + } + } + + return result; +} + +static void telemetry_messenger_destroy_option(const char* name, const void* value) +{ + if (name == NULL) + { + LogError("Failed to destroy messenger option (name is NULL)"); + } + else if (value == NULL) + { + LogError("Failed to destroy messenger option (value is NULL)"); + } + else + { + // Nothing to be done for the supported options. + } +} + + +// Public API: + +int telemetry_messenger_subscribe_for_messages(TELEMETRY_MESSENGER_HANDLE messenger_handle, ON_TELEMETRY_MESSENGER_MESSAGE_RECEIVED on_message_received_callback, void* context) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_016: [If `messenger_handle` is NULL, telemetry_messenger_subscribe_for_messages() shall fail and return __FAILURE__] + if (messenger_handle == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_subscribe_for_messages failed (messenger_handle is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_017: [If `instance->receive_messages` is already true, telemetry_messenger_subscribe_for_messages() shall fail and return __FAILURE__] + if (instance->receive_messages) + { + result = __FAILURE__; + LogError("telemetry_messenger_subscribe_for_messages failed (messenger already subscribed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_018: [If `on_message_received_callback` is NULL, telemetry_messenger_subscribe_for_messages() shall fail and return __FAILURE__] + else if (on_message_received_callback == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_subscribe_for_messages failed (on_message_received_callback is NULL)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_019: [`on_message_received_callback` shall be saved on `instance->on_message_received_callback`] + instance->on_message_received_callback = on_message_received_callback; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_020: [`context` shall be saved on `instance->on_message_received_context`] + instance->on_message_received_context = context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_021: [telemetry_messenger_subscribe_for_messages() shall set `instance->receive_messages` to true] + instance->receive_messages = true; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_022: [If no failures occurr, telemetry_messenger_subscribe_for_messages() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int telemetry_messenger_unsubscribe_for_messages(TELEMETRY_MESSENGER_HANDLE messenger_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_023: [If `messenger_handle` is NULL, telemetry_messenger_unsubscribe_for_messages() shall fail and return __FAILURE__] + if (messenger_handle == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_unsubscribe_for_messages failed (messenger_handle is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_024: [If `instance->receive_messages` is already false, telemetry_messenger_unsubscribe_for_messages() shall fail and return __FAILURE__] + if (instance->receive_messages == false) + { + result = __FAILURE__; + LogError("telemetry_messenger_unsubscribe_for_messages failed (messenger is not subscribed)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_025: [telemetry_messenger_unsubscribe_for_messages() shall set `instance->receive_messages` to false] + instance->receive_messages = false; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_026: [telemetry_messenger_unsubscribe_for_messages() shall set `instance->on_message_received_callback` to NULL] + instance->on_message_received_callback = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_027: [telemetry_messenger_unsubscribe_for_messages() shall set `instance->on_message_received_context` to NULL] + instance->on_message_received_context = NULL; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_028: [If no failures occurr, telemetry_messenger_unsubscribe_for_messages() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int telemetry_messenger_send_message_disposition(TELEMETRY_MESSENGER_HANDLE messenger_handle, TELEMETRY_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, TELEMETRY_MESSENGER_DISPOSITION_RESULT disposition_result) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_179: [If `messenger_handle` or `disposition_info` are NULL, telemetry_messenger_send_message_disposition() shall fail and return __FAILURE__] + if (messenger_handle == NULL || disposition_info == NULL) + { + LogError("Failed sending message disposition (either messenger_handle (%p) or disposition_info (%p) are NULL)", messenger_handle, disposition_info); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_180: [If `disposition_info->source` is NULL, telemetry_messenger_send_message_disposition() shall fail and return __FAILURE__] + else if (disposition_info->source == NULL) + { + LogError("Failed sending message disposition (disposition_info->source is NULL)"); + result = __FAILURE__; + } + else + { + TELEMETRY_MESSENGER_INSTANCE* messenger = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_189: [If `messenger_handle->message_receiver` is NULL, telemetry_messenger_send_message_disposition() shall fail and return __FAILURE__] + if (messenger->message_receiver == NULL) + { + LogError("Failed sending message disposition (message_receiver is not created; check if it is subscribed)"); + result = __FAILURE__; + } + else + { + AMQP_VALUE uamqp_disposition_result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_181: [An AMQP_VALUE disposition result shall be created corresponding to the `disposition_result` provided] + if ((uamqp_disposition_result = create_uamqp_disposition_result_from(disposition_result)) == NULL) + { + LogError("Failed sending message disposition (disposition result %d is not supported)", disposition_result); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_182: [`messagereceiver_send_message_disposition()` shall be invoked passing `disposition_info->source`, `disposition_info->message_id` and the corresponding AMQP_VALUE disposition result] + if (messagereceiver_send_message_disposition(messenger->message_receiver, disposition_info->source, disposition_info->message_id, uamqp_disposition_result) != RESULT_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_183: [If `messagereceiver_send_message_disposition()` fails, telemetry_messenger_send_message_disposition() shall fail and return __FAILURE__] + LogError("Failed sending message disposition (messagereceiver_send_message_disposition failed)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_185: [If no failures occurr, telemetry_messenger_send_message_disposition() shall return 0] + result = RESULT_OK; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_184: [telemetry_messenger_send_message_disposition() shall destroy the AMQP_VALUE disposition result] + amqpvalue_destroy(uamqp_disposition_result); + } + } + } + + return result; +} + +int telemetry_messenger_send_async(TELEMETRY_MESSENGER_HANDLE messenger_handle, IOTHUB_MESSAGE_LIST* message, ON_TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE on_messenger_event_send_complete_callback, void* context) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_134: [If `messenger_handle` is NULL, telemetry_messenger_send_async() shall fail and return a non-zero value] + if (messenger_handle == NULL) + { + LogError("Failed sending event (messenger_handle is NULL)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_135: [If `message` is NULL, telemetry_messenger_send_async() shall fail and return a non-zero value] + else if (message == NULL) + { + LogError("Failed sending event (message is NULL)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_136: [If `on_event_send_complete_callback` is NULL, telemetry_messenger_send_async() shall fail and return a non-zero value] + else if (on_messenger_event_send_complete_callback == NULL) + { + LogError("Failed sending event (on_event_send_complete_callback is NULL)"); + result = __FAILURE__; + } + else + { + MESSENGER_SEND_EVENT_CALLER_INFORMATION *caller_info; + TELEMETRY_MESSENGER_INSTANCE *instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_137: [telemetry_messenger_send_async() shall allocate memory for a MESSENGER_SEND_EVENT_CALLER_INFORMATION structure] + if ((caller_info = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)malloc(sizeof(MESSENGER_SEND_EVENT_CALLER_INFORMATION))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_138: [If malloc() fails, telemetry_messenger_send_async() shall fail and return a non-zero value] + LogError("Failed sending event (failed to create struct for task; malloc failed)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_100: [`task` shall be added to `instance->waiting_to_send` using singlylinkedlist_add()] + else if (singlylinkedlist_add(instance->waiting_to_send, caller_info) == NULL) + { + LogError("Failed sending event (singlylinkedlist_add failed)"); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_139: [If singlylinkedlist_add() fails, telemetry_messenger_send_async() shall fail and return a non-zero value] + result = __FAILURE__; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_142: [If any failure occurs, telemetry_messenger_send_async() shall free any memory it has allocated] + free(caller_info); + } + else + { + memset(caller_info, 0, sizeof(MESSENGER_SEND_EVENT_CALLER_INFORMATION)); + caller_info->message = message; + caller_info->on_event_send_complete_callback = on_messenger_event_send_complete_callback; + caller_info->context = context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_143: [If no failures occur, telemetry_messenger_send_async() shall return zero] + result = RESULT_OK; + } + } + + return result; +} + +int telemetry_messenger_get_send_status(TELEMETRY_MESSENGER_HANDLE messenger_handle, TELEMETRY_MESSENGER_SEND_STATUS* send_status) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_144: [If `messenger_handle` is NULL, telemetry_messenger_get_send_status() shall fail and return a non-zero value] + if (messenger_handle == NULL) + { + LogError("telemetry_messenger_get_send_status failed (messenger_handle is NULL)"); + result = __FAILURE__; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_145: [If `send_status` is NULL, telemetry_messenger_get_send_status() shall fail and return a non-zero value] + else if (send_status == NULL) + { + LogError("telemetry_messenger_get_send_status failed (send_status is NULL)"); + result = __FAILURE__; + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + LIST_ITEM_HANDLE wts_list_head = singlylinkedlist_get_head_item(instance->waiting_to_send); + LIST_ITEM_HANDLE ip_list_head = singlylinkedlist_get_head_item(instance->in_progress_list); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_147: [If `instance->in_progress_list` and `instance->wait_to_send_list` are empty, send_status shall be set to TELEMETRY_MESSENGER_SEND_STATUS_IDLE] + if (wts_list_head == NULL && ip_list_head == NULL) + { + *send_status = TELEMETRY_MESSENGER_SEND_STATUS_IDLE; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_148: [Otherwise, send_status shall be set to TELEMETRY_MESSENGER_SEND_STATUS_BUSY] + else + { + *send_status = TELEMETRY_MESSENGER_SEND_STATUS_BUSY; + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_149: [If no failures occur, telemetry_messenger_get_send_status() shall return 0] + result = RESULT_OK; + } + + return result; +} + +int telemetry_messenger_start(TELEMETRY_MESSENGER_HANDLE messenger_handle, SESSION_HANDLE session_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_029: [If `messenger_handle` is NULL, telemetry_messenger_start() shall fail and return __FAILURE__] + if (messenger_handle == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_start failed (messenger_handle is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_030: [If `session_handle` is NULL, telemetry_messenger_start() shall fail and return __FAILURE__] + else if (session_handle == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_start failed (session_handle is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_031: [If `instance->state` is not TELEMETRY_MESSENGER_STATE_STOPPED, telemetry_messenger_start() shall fail and return __FAILURE__] + if (instance->state != TELEMETRY_MESSENGER_STATE_STOPPED) + { + result = __FAILURE__; + LogError("telemetry_messenger_start failed (current state is %d; expected TELEMETRY_MESSENGER_STATE_STOPPED)", instance->state); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_032: [`session_handle` shall be saved on `instance->session_handle`] + instance->session_handle = session_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_115: [If no failures occurr, `instance->state` shall be set to TELEMETRY_MESSENGER_STATE_STARTING, and `instance->on_state_changed_callback` invoked if provided] + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_STARTING); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_056: [If no failures occurr, telemetry_messenger_start() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int telemetry_messenger_stop(TELEMETRY_MESSENGER_HANDLE messenger_handle) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_057: [If `messenger_handle` is NULL, telemetry_messenger_stop() shall fail and return a non-zero value] + if (messenger_handle == NULL) + { + result = __FAILURE__; + LogError("telemetry_messenger_stop failed (messenger_handle is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_058: [If `instance->state` is TELEMETRY_MESSENGER_STATE_STOPPED, telemetry_messenger_stop() shall fail and return a non-zero value] + if (instance->state == TELEMETRY_MESSENGER_STATE_STOPPED) + { + result = __FAILURE__; + LogError("telemetry_messenger_stop failed (messenger is already stopped)"); + } + else + { + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_STOPPING); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_152: [telemetry_messenger_stop() shall close and destroy `instance->message_sender` and `instance->message_receiver`] + destroy_event_sender(instance); + destroy_message_receiver(instance); + + remove_timed_out_events(instance); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_162: [telemetry_messenger_stop() shall move all items from `instance->in_progress_list` to the beginning of `instance->wait_to_send_list`] + if (move_events_to_wait_to_send_list(instance) != RESULT_OK) + { + LogError("Messenger failed to move events in progress back to wait_to_send list"); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_163: [If not all items from `instance->in_progress_list` can be moved back to `instance->wait_to_send_list`, `instance->state` shall be set to TELEMETRY_MESSENGER_STATE_ERROR, and `instance->on_state_changed_callback` invoked] + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_164: [If all items get successfuly moved back to `instance->wait_to_send_list`, `instance->state` shall be set to TELEMETRY_MESSENGER_STATE_STOPPED, and `instance->on_state_changed_callback` invoked] + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_STOPPED); + result = RESULT_OK; + } + } + } + + return result; +} + +// @brief +// Sets the messenger module state based on the state changes from messagesender and messagereceiver +static void process_state_changes(TELEMETRY_MESSENGER_INSTANCE* instance) +{ + // Note: messagesender and messagereceiver are still not created or already destroyed + // when state is TELEMETRY_MESSENGER_STATE_STOPPED, so no checking is needed there. + + if (instance->state == TELEMETRY_MESSENGER_STATE_STARTED) + { + if (instance->message_sender_current_state != MESSAGE_SENDER_STATE_OPEN) + { + LogError("messagesender reported unexpected state %d while messenger was started", instance->message_sender_current_state); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + else if (instance->message_receiver != NULL && instance->message_receiver_current_state != MESSAGE_RECEIVER_STATE_OPEN) + { + if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_OPENING) + { + int is_timed_out; + if (is_timeout_reached(instance->last_message_receiver_state_change_time, MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK) + { + LogError("messenger got an error (failed to verify messagereceiver start timeout)"); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + else if (is_timed_out == 1) + { + LogError("messenger got an error (messagereceiver failed to start within expected timeout (%d secs))", MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + else if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_ERROR || + instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_IDLE) + { + LogError("messagereceiver reported unexpected state %d while messenger is starting", instance->message_receiver_current_state); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + } + else + { + if (instance->state == TELEMETRY_MESSENGER_STATE_STARTING) + { + if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPEN) + { + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_STARTED); + } + else if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPENING) + { + int is_timed_out; + if (is_timeout_reached(instance->last_message_sender_state_change_time, MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK) + { + LogError("messenger failed to start (failed to verify messagesender start timeout)"); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + else if (is_timed_out == 1) + { + LogError("messenger failed to start (messagesender failed to start within expected timeout (%d secs))", MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + // For this module, the only valid scenario where messagesender state is IDLE is if + // the messagesender hasn't been created yet or already destroyed. + else if ((instance->message_sender_current_state == MESSAGE_SENDER_STATE_ERROR) || + (instance->message_sender_current_state == MESSAGE_SENDER_STATE_CLOSING) || + (instance->message_sender_current_state == MESSAGE_SENDER_STATE_IDLE && instance->message_sender != NULL)) + { + LogError("messagesender reported unexpected state %d while messenger is starting", instance->message_sender_current_state); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + // message sender and receiver are stopped/destroyed synchronously, so no need for state control. + } +} + +void telemetry_messenger_do_work(TELEMETRY_MESSENGER_HANDLE messenger_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_065: [If `messenger_handle` is NULL, telemetry_messenger_do_work() shall fail and return] + if (messenger_handle == NULL) + { + LogError("telemetry_messenger_do_work failed (messenger_handle is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + process_state_changes(instance); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_151: [If `instance->state` is TELEMETRY_MESSENGER_STATE_STARTING, telemetry_messenger_do_work() shall create and open `instance->message_sender`] + if (instance->state == TELEMETRY_MESSENGER_STATE_STARTING) + { + if (instance->message_sender == NULL) + { + if (create_event_sender(instance) != RESULT_OK) + { + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_066: [If `instance->state` is not TELEMETRY_MESSENGER_STATE_STARTED, telemetry_messenger_do_work() shall return] + else if (instance->state == TELEMETRY_MESSENGER_STATE_STARTED) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_067: [If `instance->receive_messages` is true and `instance->message_receiver` is NULL, a message_receiver shall be created] + if (instance->receive_messages == true && + instance->message_receiver == NULL && + create_message_receiver(instance) != RESULT_OK) + { + LogError("telemetry_messenger_do_work warning (failed creating the message receiver [%s])", STRING_c_str(instance->device_id)); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_092: [If `instance->receive_messages` is false and `instance->message_receiver` is not NULL, it shall be destroyed] + else if (instance->receive_messages == false && instance->message_receiver != NULL) + { + destroy_message_receiver(instance); + } + + if (process_event_send_timeouts(instance) != RESULT_OK) + { + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + else if (send_pending_events(instance) != RESULT_OK && instance->event_send_retry_limit > 0) + { + instance->event_send_error_count++; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_161: [If telemetry_messenger_do_work() fail sending events for `instance->event_send_retry_limit` times in a row, it shall invoke `instance->on_state_changed_callback`, if provided, with error code TELEMETRY_MESSENGER_STATE_ERROR] + if (instance->event_send_error_count >= instance->event_send_retry_limit) + { + LogError("telemetry_messenger_do_work failed (failed sending events; reached max number of consecutive attempts)"); + update_messenger_state(instance, TELEMETRY_MESSENGER_STATE_ERROR); + } + } + else + { + instance->event_send_error_count = 0; + } + } + } +} + +void telemetry_messenger_destroy(TELEMETRY_MESSENGER_HANDLE messenger_handle) +{ + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_109: [If `messenger_handle` is NULL, telemetry_messenger_destroy() shall fail and return] + if (messenger_handle == NULL) + { + LogError("telemetry_messenger_destroy failed (messenger_handle is NULL)"); + } + else + { + LIST_ITEM_HANDLE list_node; + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_110: [If the `instance->state` is not TELEMETRY_MESSENGER_STATE_STOPPED, telemetry_messenger_destroy() shall invoke telemetry_messenger_stop()] + if (instance->state != TELEMETRY_MESSENGER_STATE_STOPPED) + { + (void)telemetry_messenger_stop(messenger_handle); + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_111: [All elements of `instance->in_progress_list` and `instance->wait_to_send_list` shall be removed, invoking `task->on_event_send_complete_callback` for each with EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED] + + // Note: yes telemetry_messenger_stop() tried to move all events from in_progress_list to wait_to_send_list, + // but we need to iterate through in case any events failed to be moved. + while ((list_node = singlylinkedlist_get_head_item(instance->in_progress_list)) != NULL) + { + MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_node); + + (void)singlylinkedlist_remove(instance->in_progress_list, list_node); + + if (task != NULL) + { + singlylinkedlist_foreach(task->callback_list, invoke_callback, (void*)TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED); + free_task(task); + } + } + + while ((list_node = singlylinkedlist_get_head_item(instance->waiting_to_send)) != NULL) + { + MESSENGER_SEND_EVENT_CALLER_INFORMATION* caller_info = (MESSENGER_SEND_EVENT_CALLER_INFORMATION*)singlylinkedlist_item_get_value(list_node); + + (void)singlylinkedlist_remove(instance->waiting_to_send, list_node); + + if (caller_info != NULL) + { + caller_info->on_event_send_complete_callback(caller_info->message, TELEMETRY_MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED, (void*)caller_info->context); + free(caller_info); + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_150: [`instance->in_progress_list` and `instance->wait_to_send_list` shall be destroyed using singlylinkedlist_destroy()] + singlylinkedlist_destroy(instance->waiting_to_send); + singlylinkedlist_destroy(instance->in_progress_list); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_112: [`instance->iothub_host_fqdn` shall be destroyed using STRING_delete()] + STRING_delete(instance->iothub_host_fqdn); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_113: [`instance->device_id` shall be destroyed using STRING_delete()] + STRING_delete(instance->device_id); + + STRING_delete(instance->module_id); + + STRING_delete(instance->product_info); + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_114: [telemetry_messenger_destroy() shall destroy `instance` with free()] + (void)free(instance); + } +} + +TELEMETRY_MESSENGER_HANDLE telemetry_messenger_create(const TELEMETRY_MESSENGER_CONFIG* messenger_config, const char* product_info) +{ + TELEMETRY_MESSENGER_HANDLE handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_001: [If parameter `messenger_config` is NULL, telemetry_messenger_create() shall return NULL] + if (messenger_config == NULL) + { + handle = NULL; + LogError("telemetry_messenger_create failed (messenger_config is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_002: [If `messenger_config->device_id` is NULL, telemetry_messenger_create() shall return NULL] + else if (messenger_config->device_id == NULL) + { + handle = NULL; + LogError("telemetry_messenger_create failed (messenger_config->device_id is NULL)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_003: [If `messenger_config->iothub_host_fqdn` is NULL, telemetry_messenger_create() shall return NULL] + else if (messenger_config->iothub_host_fqdn == NULL) + { + handle = NULL; + LogError("telemetry_messenger_create failed (messenger_config->iothub_host_fqdn is NULL)"); + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_006: [telemetry_messenger_create() shall allocate memory for the messenger instance structure (aka `instance`)] + if ((instance = (TELEMETRY_MESSENGER_INSTANCE*)malloc(sizeof(TELEMETRY_MESSENGER_INSTANCE))) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_007: [If malloc() fails, telemetry_messenger_create() shall fail and return NULL] + handle = NULL; + LogError("telemetry_messenger_create failed (messenger_config->wait_to_send_list is NULL)"); + } + else + { + memset(instance, 0, sizeof(TELEMETRY_MESSENGER_INSTANCE)); + instance->state = TELEMETRY_MESSENGER_STATE_STOPPED; + instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE; + instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE; + instance->event_send_retry_limit = DEFAULT_EVENT_SEND_RETRY_LIMIT; + instance->event_send_timeout_secs = DEFAULT_EVENT_SEND_TIMEOUT_SECS; + instance->last_message_sender_state_change_time = INDEFINITE_TIME; + instance->last_message_receiver_state_change_time = INDEFINITE_TIME; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_008: [telemetry_messenger_create() shall save a copy of `messenger_config->device_id` into `instance->device_id`] + if ((instance->device_id = STRING_construct(messenger_config->device_id)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_009: [If STRING_construct() fails, telemetry_messenger_create() shall fail and return NULL] + handle = NULL; + LogError("telemetry_messenger_create failed (device_id could not be copied; STRING_construct failed)"); + } + else if ((messenger_config->module_id != NULL) && ((instance->module_id = STRING_construct(messenger_config->module_id)) == NULL)) + { + handle = NULL; + LogError("telemetry_messenger_create failed (module_id could not be copied; STRING_construct failed)"); + } + else if ((instance->product_info = STRING_construct(product_info)) == NULL) + { + handle = NULL; + LogError("telemetry_messenger_create failed (product_info could not be copied; STRING_construct failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_010: [telemetry_messenger_create() shall save a copy of `messenger_config->iothub_host_fqdn` into `instance->iothub_host_fqdn`] + else if ((instance->iothub_host_fqdn = STRING_construct(messenger_config->iothub_host_fqdn)) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_011: [If STRING_construct() fails, telemetry_messenger_create() shall fail and return NULL] + handle = NULL; + LogError("telemetry_messenger_create failed (iothub_host_fqdn could not be copied; STRING_construct failed)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_165: [`instance->wait_to_send_list` shall be set using singlylinkedlist_create()] + else if ((instance->waiting_to_send = singlylinkedlist_create()) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_166: [If singlylinkedlist_create() fails, telemetry_messenger_create() shall fail and return NULL] + handle = NULL; + LogError("telemetry_messenger_create failed (singlylinkedlist_create failed to create wait_to_send_list)"); + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_132: [`instance->in_progress_list` shall be set using singlylinkedlist_create()] + else if ((instance->in_progress_list = singlylinkedlist_create()) == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_133: [If singlylinkedlist_create() fails, telemetry_messenger_create() shall fail and return NULL] + handle = NULL; + LogError("telemetry_messenger_create failed (singlylinkedlist_create failed to create in_progress_list)"); + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_013: [`messenger_config->on_state_changed_callback` shall be saved into `instance->on_state_changed_callback`] + instance->on_state_changed_callback = messenger_config->on_state_changed_callback; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_014: [`messenger_config->on_state_changed_context` shall be saved into `instance->on_state_changed_context`] + instance->on_state_changed_context = messenger_config->on_state_changed_context; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_015: [If no failures occurr, telemetry_messenger_create() shall return a handle to `instance`] + handle = (TELEMETRY_MESSENGER_HANDLE)instance; + } + } + + if (handle == NULL) + { + telemetry_messenger_destroy((TELEMETRY_MESSENGER_HANDLE)instance); + } + } + + return handle; +} + +int telemetry_messenger_set_option(TELEMETRY_MESSENGER_HANDLE messenger_handle, const char* name, void* value) +{ + int result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_167: [If `messenger_handle` or `name` or `value` is NULL, telemetry_messenger_set_option shall fail and return a non-zero value] + if (messenger_handle == NULL || name == NULL || value == NULL) + { + LogError("telemetry_messenger_set_option failed (one of the followin are NULL: messenger_handle=%p, name=%p, value=%p)", + messenger_handle, name, value); + result = __FAILURE__; + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_168: [If name matches TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, `value` shall be saved on `instance->event_send_timeout_secs`] + if (strcmp(TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0) + { + instance->event_send_timeout_secs = *((size_t*)value); + result = RESULT_OK; + } + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_169: [If name matches TELEMETRY_MESSENGER_OPTION_SAVED_OPTIONS, `value` shall be applied using OptionHandler_FeedOptions] + else if (strcmp(TELEMETRY_MESSENGER_OPTION_SAVED_OPTIONS, name) == 0) + { + if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, messenger_handle) != OPTIONHANDLER_OK) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_170: [If OptionHandler_FeedOptions fails, telemetry_messenger_set_option shall fail and return a non-zero value] + LogError("telemetry_messenger_set_option failed (OptionHandler_FeedOptions failed)"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_171: [If name does not match any supported option, authentication_set_option shall fail and return a non-zero value] + LogError("telemetry_messenger_set_option failed (option with name '%s' is not suppported)", name); + result = __FAILURE__; + } + } + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_172: [If no errors occur, telemetry_messenger_set_option shall return 0] + return result; +} + +OPTIONHANDLER_HANDLE telemetry_messenger_retrieve_options(TELEMETRY_MESSENGER_HANDLE messenger_handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_173: [If `messenger_handle` is NULL, telemetry_messenger_retrieve_options shall fail and return NULL] + if (messenger_handle == NULL) + { + LogError("Failed to retrieve options from messenger instance (messenger_handle is NULL)"); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_174: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create] + OPTIONHANDLER_HANDLE options = OptionHandler_Create(telemetry_messenger_clone_option, telemetry_messenger_destroy_option, (pfSetOption)telemetry_messenger_set_option); + + if (options == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_175: [If an OPTIONHANDLER_HANDLE instance fails to be created, telemetry_messenger_retrieve_options shall fail and return NULL] + LogError("Failed to retrieve options from messenger instance (OptionHandler_Create failed)"); + result = NULL; + } + else + { + TELEMETRY_MESSENGER_INSTANCE* instance = (TELEMETRY_MESSENGER_INSTANCE*)messenger_handle; + + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_176: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption] + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_177: [If OptionHandler_AddOption fails, telemetry_messenger_retrieve_options shall fail and return NULL] + if (OptionHandler_AddOption(options, TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, (void*)&instance->event_send_timeout_secs) != OPTIONHANDLER_OK) + { + LogError("Failed to retrieve options from messenger instance (OptionHandler_Create failed for option '%s')", TELEMETRY_MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS); + result = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_179: [If no failures occur, telemetry_messenger_retrieve_options shall return the OPTIONHANDLER_HANDLE instance] + result = options; + } + + if (result == NULL) + { + // Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_178: [If telemetry_messenger_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(options); + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_amqp_twin_messenger.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2123 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_uamqp_c/amqp_definitions_fields.h" +#include "azure_uamqp_c/messaging.h" +#include "internal/iothub_client_private.h" +#include "internal/iothubtransport_amqp_messenger.h" +#include "internal/iothubtransport_amqp_twin_messenger.h" + +DEFINE_ENUM_STRINGS(TWIN_MESSENGER_SEND_STATUS, TWIN_MESSENGER_SEND_STATUS_VALUES); +DEFINE_ENUM_STRINGS(TWIN_REPORT_STATE_RESULT, TWIN_REPORT_STATE_RESULT_VALUES); +DEFINE_ENUM_STRINGS(TWIN_REPORT_STATE_REASON, TWIN_REPORT_STATE_REASON_VALUES); +DEFINE_ENUM_STRINGS(TWIN_MESSENGER_STATE, TWIN_MESSENGER_STATE_VALUES); +DEFINE_ENUM_STRINGS(TWIN_UPDATE_TYPE, TWIN_UPDATE_TYPE_VALUES); + + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) + +#define CLIENT_VERSION_PROPERTY_NAME "com.microsoft:client-version" +#define UNIQUE_ID_BUFFER_SIZE 37 + +#define EMPTY_TWIN_BODY_DATA ((const unsigned char*)" ") +#define EMPTY_TWIN_BODY_SIZE 1 + +#define TWIN_MESSAGE_PROPERTY_OPERATION "operation" +#define TWIN_MESSAGE_PROPERTY_RESOURCE "resource" +#define TWIN_MESSAGE_PROPERTY_VERSION "version" +#define TWIN_MESSAGE_PROPERTY_STATUS "status" + +#define TWIN_RESOURCE_DESIRED "/notifications/twin/properties/desired" +#define TWIN_RESOURCE_REPORTED "/properties/reported" + +#define TWIN_CORRELATION_ID_PROPERTY_NAME "com.microsoft:channel-correlation-id" +#define TWIN_API_VERSION_PROPERTY_NAME "com.microsoft:api-version" +#define TWIN_CORRELATION_ID_PROPERTY_FORMAT "twin:%s" +#define TWIN_API_VERSION_NUMBER "2016-11-14" + +#define DEFAULT_MAX_TWIN_SUBSCRIPTION_ERROR_COUNT 3 +#define DEFAULT_TWIN_OPERATION_TIMEOUT_SECS 300.0 + +static char* DEFAULT_TWIN_SEND_LINK_SOURCE_NAME = "twin"; +static char* DEFAULT_TWIN_RECEIVE_LINK_TARGET_NAME = "twin"; + +static const char* TWIN_OPERATION_PATCH = "PATCH"; +static const char* TWIN_OPERATION_GET = "GET"; +static const char* TWIN_OPERATION_PUT = "PUT"; +static const char* TWIN_OPERATION_DELETE = "DELETE"; + + +#define TWIN_OPERATION_TYPE_STRINGS \ + TWIN_OPERATION_TYPE_PATCH, \ + TWIN_OPERATION_TYPE_GET, \ + TWIN_OPERATION_TYPE_PUT, \ + TWIN_OPERATION_TYPE_DELETE + +DEFINE_LOCAL_ENUM(TWIN_OPERATION_TYPE, TWIN_OPERATION_TYPE_STRINGS); + +#define TWIN_SUBSCRIPTION_STATE_STRINGS \ + TWIN_SUBSCRIPTION_STATE_NOT_SUBSCRIBED, \ + TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES, \ + TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES, \ + TWIN_SUBSCRIPTION_STATE_SUBSCRIBE_FOR_UPDATES, \ + TWIN_SUBSCRIPTION_STATE_SUBSCRIBING, \ + TWIN_SUBSCRIPTION_STATE_SUBSCRIBED, \ + TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE, \ + TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBING + +// Suppress unused function warning for TWIN_SUBSCRIPTION_STATEstrings +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif +DEFINE_LOCAL_ENUM(TWIN_SUBSCRIPTION_STATE, TWIN_SUBSCRIPTION_STATE_STRINGS); +#ifdef __APPLE__ +#pragma clang diagnostic pop +#endif + +typedef struct TWIN_MESSENGER_INSTANCE_TAG +{ + char* client_version; + char* device_id; + char* module_id; + char* iothub_host_fqdn; + + TWIN_MESSENGER_STATE state; + + SINGLYLINKEDLIST_HANDLE pending_patches; + SINGLYLINKEDLIST_HANDLE operations; + + TWIN_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback; + void* on_state_changed_context; + + TWIN_SUBSCRIPTION_STATE subscription_state; + size_t subscription_error_count; + TWIN_STATE_UPDATE_CALLBACK on_message_received_callback; + void* on_message_received_context; + + AMQP_MESSENGER_HANDLE amqp_msgr; + AMQP_MESSENGER_STATE amqp_msgr_state; + bool amqp_msgr_is_subscribed; +} TWIN_MESSENGER_INSTANCE; + +typedef struct TWIN_PATCH_OPERATION_CONTEXT_TAG +{ + CONSTBUFFER_HANDLE data; + TWIN_MESSENGER_REPORT_STATE_COMPLETE_CALLBACK on_report_state_complete_callback; + const void* on_report_state_complete_context; + time_t time_enqueued; +} TWIN_PATCH_OPERATION_CONTEXT; + +typedef struct TWIN_OPERATION_CONTEXT_TAG +{ + TWIN_OPERATION_TYPE type; + TWIN_MESSENGER_INSTANCE* msgr; + char* correlation_id; + TWIN_MESSENGER_REPORT_STATE_COMPLETE_CALLBACK on_report_state_complete_callback; + const void* on_report_state_complete_context; + time_t time_sent; +} TWIN_OPERATION_CONTEXT; + + + + +static void update_state(TWIN_MESSENGER_INSTANCE* twin_msgr, TWIN_MESSENGER_STATE new_state) +{ + if (new_state != twin_msgr->state) + { + TWIN_MESSENGER_STATE previous_state = twin_msgr->state; + twin_msgr->state = new_state; + + if (twin_msgr->on_state_changed_callback != NULL) + { + twin_msgr->on_state_changed_callback(twin_msgr->on_state_changed_context, previous_state, new_state); + } + } +} + +//---------- AMQP Helper Functions ----------// + +static int set_message_correlation_id(MESSAGE_HANDLE message, const char* value) +{ + int result; + PROPERTIES_HANDLE properties = NULL; + + if (message_get_properties(message, &properties) != 0) + { + LogError("Failed getting the AMQP message properties"); + result = __FAILURE__; + } + else if (properties == NULL && (properties = properties_create()) == NULL) + { + LogError("Failed creating properties for AMQP message"); + result = __FAILURE__; + } + else + { + AMQP_VALUE amqp_value; + + if ((amqp_value = amqpvalue_create_string(value)) == NULL) + { + LogError("Failed creating AMQP value for correlation-id"); + result = __FAILURE__; + } + else + { + if (properties_set_correlation_id(properties, amqp_value) != RESULT_OK) + { + LogError("Failed setting the correlation id"); + result = __FAILURE__; + } + else if (message_set_properties(message, properties) != RESULT_OK) + { + LogError("Failed setting the AMQP message properties"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + amqpvalue_destroy(amqp_value); + } + + properties_destroy(properties); + } + + return result; +} + +static int get_message_correlation_id(MESSAGE_HANDLE message, char** correlation_id) +{ + int result; + + PROPERTIES_HANDLE properties; + AMQP_VALUE amqp_value; + + if (message_get_properties(message, &properties) != 0) + { + LogError("Failed getting AMQP message properties"); + result = __FAILURE__; + } + else if (properties == NULL) + { + *correlation_id = NULL; + result = RESULT_OK; + } + else + { + if (properties_get_correlation_id(properties, &amqp_value) != 0 || amqp_value == NULL) + { + *correlation_id = NULL; + result = RESULT_OK; + } + else + { + const char* value; + + if (amqpvalue_get_string(amqp_value, &value) != 0) + { + LogError("Failed retrieving string from AMQP value"); + result = __FAILURE__; + } + else if (mallocAndStrcpy_s(correlation_id, value) != 0) + { + LogError("Failed cloning correlation-id"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + + properties_destroy(properties); + } + + return result; +} + +static int add_map_item(AMQP_VALUE map, const char* name, const char* value) +{ + int result; + AMQP_VALUE amqp_value_name; + + if ((amqp_value_name = amqpvalue_create_symbol(name)) == NULL) + { + LogError("Failed creating AMQP_VALUE for name"); + result = __FAILURE__; + } + else + { + AMQP_VALUE amqp_value_value = NULL; + + if (value == NULL && (amqp_value_value = amqpvalue_create_null()) == NULL) + { + LogError("Failed creating AMQP_VALUE for NULL value"); + result = __FAILURE__; + } + else if (value != NULL && (amqp_value_value = amqpvalue_create_string(value)) == NULL) + { + LogError("Failed creating AMQP_VALUE for value"); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_map_value(map, amqp_value_name, amqp_value_value) != 0) + { + LogError("Failed adding key/value pair to map"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + amqpvalue_destroy(amqp_value_value); + } + + amqpvalue_destroy(amqp_value_name); + } + + return result; +} + +static int add_amqp_message_annotation(MESSAGE_HANDLE message, AMQP_VALUE msg_annotations_map) +{ + int result; + AMQP_VALUE msg_annotations; + + if ((msg_annotations = amqpvalue_create_message_annotations(msg_annotations_map)) == NULL) + { + LogError("Failed creating new AMQP message annotations"); + result = __FAILURE__; + } + else + { + if (message_set_message_annotations(message, (annotations)msg_annotations) != 0) + { + LogError("Failed setting AMQP message annotations"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + annotations_destroy(msg_annotations); + } + + return result; +} + + +//---------- TWIN Helpers ----------// + +static char* generate_unique_id() +{ + char* result; + + if ((result = (char*)malloc(sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1)) == NULL) + { + LogError("Failed generating an unique tag (malloc failed)"); + } + else + { + memset(result, 0, sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1); + + if (UniqueId_Generate(result, UNIQUE_ID_BUFFER_SIZE) != UNIQUEID_OK) + { + LogError("Failed generating an unique tag (UniqueId_Generate failed)"); + free(result); + result = NULL; + } + } + + return result; +} + +static char* generate_twin_correlation_id() +{ + char* result; + char* unique_id; + + if ((unique_id = generate_unique_id()) == NULL) + { + LogError("Failed generating unique ID for correlation-id"); + result = NULL; + } + else + { + if ((result = (char*)malloc(strlen(TWIN_CORRELATION_ID_PROPERTY_FORMAT) + strlen(unique_id) + 1)) == NULL) + { + LogError("Failed allocating correlation-id"); + result = NULL; + } + else + { + (void)sprintf(result, TWIN_CORRELATION_ID_PROPERTY_FORMAT, unique_id); + } + + free(unique_id); + } + + return result; +} + +static TWIN_OPERATION_CONTEXT* create_twin_operation_context(TWIN_MESSENGER_INSTANCE* twin_msgr, TWIN_OPERATION_TYPE type) +{ + TWIN_OPERATION_CONTEXT* result; + + if ((result = (TWIN_OPERATION_CONTEXT*)malloc(sizeof(TWIN_OPERATION_CONTEXT))) == NULL) + { + LogError("Failed creating context for %s (%s)", ENUM_TO_STRING(TWIN_OPERATION_TYPE, type), twin_msgr->device_id); + } + else + { + memset(result, 0, sizeof(TWIN_OPERATION_CONTEXT)); + + if ((result->correlation_id = generate_unique_id()) == NULL) + { + LogError("Failed setting context correlation-id (%s, %s)", ENUM_TO_STRING(TWIN_OPERATION_TYPE, type), twin_msgr->device_id); + free(result); + result = NULL; + } + else + { + result->type = type; + result->msgr = twin_msgr; + } + } + + return result; +} + +static bool find_twin_operation_by_correlation_id(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + TWIN_OPERATION_CONTEXT* twin_op_ctx = (TWIN_OPERATION_CONTEXT*)singlylinkedlist_item_get_value(list_item); + return (strcmp(twin_op_ctx->correlation_id, (const char*)match_context) == 0); +} + +static bool find_twin_operation_by_type(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + TWIN_OPERATION_CONTEXT* twin_op_ctx = (TWIN_OPERATION_CONTEXT*)singlylinkedlist_item_get_value(list_item); + return (twin_op_ctx->type == *(TWIN_OPERATION_TYPE*)match_context); +} + +static void destroy_twin_operation_context(TWIN_OPERATION_CONTEXT* op_ctx) +{ + free(op_ctx->correlation_id); + free(op_ctx); +} + +static int add_twin_operation_context_to_queue(TWIN_OPERATION_CONTEXT* twin_op_ctx) +{ + int result; + + if (singlylinkedlist_add(twin_op_ctx->msgr->operations, (const void*)twin_op_ctx) == NULL) + { + LogError("Failed adding TWIN operation context to queue (%s, %s)", ENUM_TO_STRING(TWIN_OPERATION_TYPE, twin_op_ctx->type), twin_op_ctx->correlation_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static int remove_twin_operation_context_from_queue(TWIN_OPERATION_CONTEXT* twin_op_ctx) +{ + int result; + LIST_ITEM_HANDLE list_item; + + if ((list_item = singlylinkedlist_find(twin_op_ctx->msgr->operations, find_twin_operation_by_correlation_id, (const void*)twin_op_ctx->correlation_id)) == NULL) + { + result = RESULT_OK; + } + else if (singlylinkedlist_remove(twin_op_ctx->msgr->operations, list_item) != 0) + { + LogError("Failed removing TWIN operation context from queue (%s, %s, %s)", + twin_op_ctx->msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, twin_op_ctx->type), twin_op_ctx->correlation_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + + +//---------- TWIN <-> AMQP Translation Functions ----------// + +static int parse_incoming_twin_message(MESSAGE_HANDLE message, + char** correlation_id, + bool* has_version, int64_t* version, + bool* has_status_code, int* status_code, + bool* has_twin_report, BINARY_DATA* twin_report) +{ + int result; + + if (get_message_correlation_id(message, correlation_id) != 0) + { + LogError("Failed retrieving correlation ID from received TWIN message."); + result = __FAILURE__; + } + else + { + annotations message_annotations; + + if (message_get_message_annotations(message, &message_annotations) != 0) + { + LogError("Failed getting TWIN message annotations"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + + if (message_annotations == NULL) + { + *has_version = false; + *has_status_code = false; + } + else + { + uint32_t pair_count; + if (amqpvalue_get_map_pair_count((AMQP_VALUE)message_annotations, &pair_count) != 0) + { + LogError("Failed getting TWIN message annotations count"); + result = __FAILURE__; + } + else + { + uint32_t i; + + *has_status_code = false; + *has_version = false; + + for (i = 0; i < pair_count; i++) + { + AMQP_VALUE amqp_map_key; + AMQP_VALUE amqp_map_value; + + if (amqpvalue_get_map_key_value_pair((AMQP_VALUE)message_annotations, i, &amqp_map_key, &amqp_map_value) != 0) + { + LogError("Failed getting AMQP map key/value pair (%d)", i); + result = __FAILURE__; + } + else + { + const char* map_key_name; + + if (amqpvalue_get_symbol(amqp_map_key, &map_key_name) != 0) + { + LogError("Failed getting AMQP value symbol"); + result = __FAILURE__; + break; + } + else + { + if (strcmp(TWIN_MESSAGE_PROPERTY_STATUS, map_key_name) == 0) + { + if (amqpvalue_get_type(amqp_map_value) != AMQP_TYPE_INT) + { + LogError("TWIN message status property expected to be INT"); + result = __FAILURE__; + break; + } + else if (amqpvalue_get_int(amqp_map_value, status_code) != 0) + { + LogError("Failed getting TWIN message status code value"); + result = __FAILURE__; + break; + } + else + { + *has_status_code = true; + } + } + else if (strcmp(TWIN_MESSAGE_PROPERTY_VERSION, map_key_name) == 0) + { + if (amqpvalue_get_type(amqp_map_value) != AMQP_TYPE_LONG) + { + LogError("TWIN message version property expected to be LONG"); + result = __FAILURE__; + break; + } + else if (amqpvalue_get_long(amqp_map_value, version) != 0) + { + LogError("Failed getting TWIN message version value"); + result = __FAILURE__; + break; + } + else + { + *has_version = true; + } + } + + amqpvalue_destroy(amqp_map_value); + } + + amqpvalue_destroy(amqp_map_key); + } + } + } + + amqpvalue_destroy(message_annotations); + } + + if (result == RESULT_OK) + { + MESSAGE_BODY_TYPE body_type; + + if (message_get_body_type(message, &body_type) != 0) + { + LogError("Failed getting TWIN message body type"); + result = __FAILURE__; + } + else if (body_type == MESSAGE_BODY_TYPE_DATA) + { + size_t body_count; + + if (message_get_body_amqp_data_count(message, &body_count) != 0) + { + LogError("Failed getting TWIN message body count"); + result = __FAILURE__; + } + else if (body_count != 1) + { + LogError("Unexpected number of TWIN message bodies (%d)", body_count); + result = __FAILURE__; + } + else if (message_get_body_amqp_data_in_place(message, 0, twin_report) != 0) + { + LogError("Failed getting TWIN message body"); + result = __FAILURE__; + } + else + { + *has_twin_report = true; + } + } + else if (body_type != MESSAGE_BODY_TYPE_NONE) + { + LogError("Unexpected TWIN message body %d", body_type); + result = __FAILURE__; + } + else + { + *has_twin_report = false; + } + } + } + + if (result != RESULT_OK) + { + free(*correlation_id); + *correlation_id = NULL; + } + } + + return result; +} + +static void destroy_link_attach_properties(MAP_HANDLE properties) +{ + Map_Destroy(properties); +} + +static MAP_HANDLE create_link_attach_properties(TWIN_MESSENGER_INSTANCE* twin_msgr) +{ + MAP_HANDLE result; + + if ((result = Map_Create(NULL)) == NULL) + { + LogError("Failed creating map for AMQP link properties (%s)", twin_msgr->device_id); + } + else + { + char* correlation_id; + + if ((correlation_id = generate_twin_correlation_id()) == NULL) + { + LogError("Failed adding AMQP link property "); + destroy_link_attach_properties(result); + result = NULL; + } + else + { + if (Map_Add(result, CLIENT_VERSION_PROPERTY_NAME, twin_msgr->client_version) != MAP_OK) + { + LogError("Failed adding AMQP link property 'client version' (%s)", twin_msgr->device_id); + destroy_link_attach_properties(result); + result = NULL; + } + else if (Map_Add(result, TWIN_CORRELATION_ID_PROPERTY_NAME, correlation_id) != MAP_OK) + { + LogError("Failed adding AMQP link property 'correlation-id' (%s)", twin_msgr->device_id); + destroy_link_attach_properties(result); + result = NULL; + } + else if (Map_Add(result, TWIN_API_VERSION_PROPERTY_NAME, TWIN_API_VERSION_NUMBER) != MAP_OK) + { + LogError("Failed adding AMQP link property 'api-version' (%s)", twin_msgr->device_id); + destroy_link_attach_properties(result); + result = NULL; + } + + free(correlation_id); + } + } + + return result; +} + +static const char* get_twin_operation_name(TWIN_OPERATION_TYPE op_type) +{ + const char* result; + + switch (op_type) + { + default: + LogError("Unrecognized TWIN operation (%s)", ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_type)); + result = NULL; + break; + case TWIN_OPERATION_TYPE_PATCH: + result = TWIN_OPERATION_PATCH; + break; + case TWIN_OPERATION_TYPE_GET: + result = TWIN_OPERATION_GET; + break; + case TWIN_OPERATION_TYPE_PUT: + result = TWIN_OPERATION_PUT; + break; + case TWIN_OPERATION_TYPE_DELETE: + result = TWIN_OPERATION_DELETE; + break; + } + + return result; +} + +static MESSAGE_HANDLE create_amqp_message_for_twin_operation(TWIN_OPERATION_TYPE op_type, char* correlation_id, CONSTBUFFER_HANDLE data) +{ + MESSAGE_HANDLE result; + const char* twin_op_name; + + if ((twin_op_name = get_twin_operation_name(op_type))== NULL) + { + result = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_063: [An `amqp_message` (MESSAGE_HANDLE) shall be created using message_create()] + else if ((result = message_create()) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_064: [If message_create() fails, message_create_for_twin_operation shall fail and return NULL] + LogError("Failed creating AMQP message (%s)", twin_op_name); + } + else + { + AMQP_VALUE msg_annotations_map; + + if ((msg_annotations_map = amqpvalue_create_map()) == NULL) + { + LogError("Failed creating map for message annotations"); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_065: [`operation=<op_type>` (GET/PATCH/PUT/DELETE) must be added to the `amqp_message` annotations] + if (add_map_item(msg_annotations_map, TWIN_MESSAGE_PROPERTY_OPERATION, twin_op_name) != RESULT_OK) + { + LogError("Failed adding operation to AMQP message annotations (%s)", twin_op_name); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_066: [If `op_type` is PATCH, `resource=/properties/reported` must be added to the `amqp_message` annotations] + else if ((op_type == TWIN_OPERATION_TYPE_PATCH) && + add_map_item(msg_annotations_map, TWIN_MESSAGE_PROPERTY_RESOURCE, TWIN_RESOURCE_REPORTED) != RESULT_OK) + { + LogError("Failed adding resource to AMQP message annotations (%s)", twin_op_name); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_067: [If `op_type` is PUT or DELETE, `resource=/notifications/twin/properties/desired` must be added to the `amqp_message` annotations] + else if ((op_type == TWIN_OPERATION_TYPE_PUT || op_type == TWIN_OPERATION_TYPE_DELETE) && + add_map_item(msg_annotations_map, TWIN_MESSAGE_PROPERTY_RESOURCE, TWIN_RESOURCE_DESIRED) != RESULT_OK) + { + LogError("Failed adding resource to AMQP message annotations (%s)", twin_op_name); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + else if (add_amqp_message_annotation(result, msg_annotations_map) != RESULT_OK) + { + LogError("Failed adding annotations to AMQP message (%s)", twin_op_name); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_068: [The `correlation-id` property of `amqp_message` shall be set with an UUID string] + else if (set_message_correlation_id(result, correlation_id) != RESULT_OK) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_069: [If setting `correlation-id` fails, message_create_for_twin_operation shall fail and return NULL] + LogError("Failed AMQP message correlation-id (%s)", twin_op_name); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + result = NULL; + } + else + { + BINARY_DATA binary_data; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_070: [If `data` is not NULL, it shall be added to `amqp_message` using message_add_body_amqp_data()] + if (data != NULL) + { + const CONSTBUFFER* data_buffer; + data_buffer = CONSTBUFFER_GetContent(data); + binary_data.bytes = data_buffer->buffer; + binary_data.length = data_buffer->size; + } + else + { + binary_data.bytes = EMPTY_TWIN_BODY_DATA; + binary_data.length = EMPTY_TWIN_BODY_SIZE; + } + + if (message_add_body_amqp_data(result, binary_data) != 0) + { + LogError("Failed adding twin patch data to AMQP message body"); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_072: [If any errors occur, message_create_for_twin_operation shall release all memory it has allocated] + message_destroy(result); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_071: [If message_add_body_amqp_data() fails, message_create_for_twin_operation shall fail and return NULL] + result = NULL; + } + } + + amqpvalue_destroy(msg_annotations_map); + } + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_073: [If no errors occur, message_create_for_twin_operation shall return `amqp_message`] + return result; +} + +static TWIN_REPORT_STATE_RESULT get_twin_messenger_result_from(AMQP_MESSENGER_SEND_RESULT amqp_send_result) +{ + TWIN_REPORT_STATE_RESULT result; + + switch (amqp_send_result) + { + case AMQP_MESSENGER_SEND_RESULT_SUCCESS: + result = TWIN_REPORT_STATE_RESULT_SUCCESS; + break; + case AMQP_MESSENGER_SEND_RESULT_CANCELLED: + result = TWIN_REPORT_STATE_RESULT_CANCELLED; + break; + case AMQP_MESSENGER_SEND_RESULT_ERROR: + result = TWIN_REPORT_STATE_RESULT_ERROR; + break; + default: + LogError("Unrecognized enum value %s", ENUM_TO_STRING(AMQP_MESSENGER_SEND_RESULT, amqp_send_result)); + result = TWIN_REPORT_STATE_RESULT_ERROR; + break; + }; + + return result; +} + +static TWIN_REPORT_STATE_REASON get_twin_messenger_reason_from(AMQP_MESSENGER_REASON amqp_reason) +{ + TWIN_REPORT_STATE_REASON result; + + switch (amqp_reason) + { + case AMQP_MESSENGER_REASON_NONE: + result = TWIN_REPORT_STATE_REASON_NONE; + break; + case AMQP_MESSENGER_REASON_FAIL_SENDING: + result = TWIN_REPORT_STATE_REASON_FAIL_SENDING; + break; + case AMQP_MESSENGER_REASON_TIMEOUT: + result = TWIN_REPORT_STATE_REASON_TIMEOUT; + break; + case AMQP_MESSENGER_REASON_MESSENGER_DESTROYED: + result = TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED; + break; + case AMQP_MESSENGER_REASON_CANNOT_PARSE: + result = TWIN_REPORT_STATE_REASON_NONE; + break; + default: + LogError("Unrecognized enum value %s (%d)", ENUM_TO_STRING(AMQP_MESSENGER_REASON, amqp_reason), amqp_reason); + result = TWIN_REPORT_STATE_REASON_NONE; + break; + }; + + return result; +} + +static void on_amqp_send_complete_callback(AMQP_MESSENGER_SEND_RESULT result, AMQP_MESSENGER_REASON reason, void* context) +{ + // Applicable to TWIN requests for reported state PATCH, GET, PUT, DELETE. + + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else + { + TWIN_OPERATION_CONTEXT* twin_op_ctx = (TWIN_OPERATION_CONTEXT*)context; + + if (result != AMQP_MESSENGER_SEND_RESULT_SUCCESS) + { + if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PATCH) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_095: [If operation is a reported state PATCH, if a failure occurs `on_report_state_complete_callback` shall be invoked with TWIN_REPORT_STATE_RESULT_ERROR, status code from the AMQP response and the saved context] + if (twin_op_ctx->on_report_state_complete_callback != NULL) + { + TWIN_REPORT_STATE_RESULT callback_result; + TWIN_REPORT_STATE_REASON callback_reason; + + callback_result = get_twin_messenger_result_from(result); + callback_reason = get_twin_messenger_reason_from(reason); + + twin_op_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_NONE, 0, (void*)twin_op_ctx->on_report_state_complete_context); + } + } + else if (reason != AMQP_MESSENGER_REASON_MESSENGER_DESTROYED) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_096: [If operation is a GET/PUT/DELETE, if a failure occurs the TWIN messenger shall attempt to subscribe/unsubscribe again] + LogError("Failed sending TWIN operation request (%s, %s, %s, %s, %s)", + twin_op_ctx->msgr->device_id, + ENUM_TO_STRING(TWIN_OPERATION_TYPE, twin_op_ctx->type), + twin_op_ctx->correlation_id, + ENUM_TO_STRING(AMQP_MESSENGER_SEND_RESULT, result), ENUM_TO_STRING(AMQP_MESSENGER_REASON, reason)); + + if (twin_op_ctx->type == TWIN_OPERATION_TYPE_GET && + twin_op_ctx->msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES) + { + twin_op_ctx->msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES; + twin_op_ctx->msgr->subscription_error_count++; + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PUT && + twin_op_ctx->msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES) + { + twin_op_ctx->msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES; + twin_op_ctx->msgr->subscription_error_count++; + } + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_097: [If a failure occurred, the current operation shall be removed from `twin_msgr->operations`] + if (remove_twin_operation_context_from_queue(twin_op_ctx) != RESULT_OK) + { + update_state(twin_op_ctx->msgr, TWIN_MESSENGER_STATE_ERROR); + } + else + { + destroy_twin_operation_context(twin_op_ctx); + } + } + } +} + +static int send_twin_operation_request(TWIN_MESSENGER_INSTANCE* twin_msgr, TWIN_OPERATION_CONTEXT* op_ctx, CONSTBUFFER_HANDLE data) +{ + int result; + MESSAGE_HANDLE amqp_message; + + if ((amqp_message = create_amqp_message_for_twin_operation(op_ctx->type, op_ctx->correlation_id, data)) == NULL) + { + LogError("Failed creating request message (%s, %s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_ctx->type), op_ctx->correlation_id); + result = __FAILURE__; + } + else + { + if ((op_ctx->time_sent = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed setting TWIN operation sent time (%s, %s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_ctx->type), op_ctx->correlation_id); + result = __FAILURE__; + } + else if (amqp_messenger_send_async(twin_msgr->amqp_msgr, amqp_message, on_amqp_send_complete_callback, (void*)op_ctx) != 0) + { + LogError("Failed sending request message for (%s, %s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_ctx->type), op_ctx->correlation_id); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + message_destroy(amqp_message); + } + + return result; +} + + +//---------- Internal Helpers----------// + +static bool remove_expired_twin_patch_request(const void* item, const void* match_context, bool* continue_processing) +{ + bool remove_item; + + if (item == NULL || match_context == NULL || continue_processing == NULL) + { + LogError("Invalid argument (item=%p, match_context=%p, continue_processing=%p)", item, match_context, continue_processing); + *continue_processing = false; + remove_item = false; + } + else + { + + time_t current_time = *(time_t*)match_context; + TWIN_PATCH_OPERATION_CONTEXT* twin_patch_ctx = (TWIN_PATCH_OPERATION_CONTEXT*)item; + + if (get_difftime(current_time, twin_patch_ctx->time_enqueued) >= DEFAULT_TWIN_OPERATION_TIMEOUT_SECS) + { + remove_item = true; + *continue_processing = true; + + if (twin_patch_ctx->on_report_state_complete_callback != NULL) + { + twin_patch_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_TIMEOUT, 0, twin_patch_ctx->on_report_state_complete_context); + } + + CONSTBUFFER_Destroy(twin_patch_ctx->data); + free(twin_patch_ctx); + } + else + { + remove_item = false; + *continue_processing = false; + } + } + + return remove_item; +} + +static bool remove_expired_twin_operation_request(const void* item, const void* match_context, bool* continue_processing) +{ + bool result; + + if (item == NULL || match_context == NULL || continue_processing == NULL) + { + LogError("Invalid argument (item=%p, match_context=%p, continue_processing=%p)", item, match_context, continue_processing); + *continue_processing = false; + result = false; + } + else + { + TWIN_OPERATION_CONTEXT* twin_op_ctx = (TWIN_OPERATION_CONTEXT*)item; + TWIN_MESSENGER_INSTANCE* twin_msgr = twin_op_ctx->msgr; + time_t current_time = *(time_t*)match_context; + + if (get_difftime(current_time, twin_op_ctx->time_sent) < DEFAULT_TWIN_OPERATION_TIMEOUT_SECS) + { + result = false; + // All next elements in the list have a later time_sent, so they won't be expired, and don't need to be removed. + *continue_processing = false; + } + else + { + LogError("Twin operation timed out (%s, %s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, twin_op_ctx->type), twin_op_ctx->correlation_id); + result = true; + *continue_processing = true; + + if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PATCH) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_081: [If a timed-out item is a reported property PATCH, `on_report_state_complete_callback` shall be invoked with RESULT_ERROR and REASON_TIMEOUT] + if (twin_op_ctx->on_report_state_complete_callback != NULL) + { + twin_op_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_TIMEOUT, 0, twin_op_ctx->on_report_state_complete_context); + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_GET) + { + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES; + twin_msgr->subscription_error_count++; + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PUT) + { + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_SUBSCRIBING) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_SUBSCRIBE_FOR_UPDATES; + twin_msgr->subscription_error_count++; + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_DELETE) + { + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBING) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE; + twin_msgr->subscription_error_count++; + } + } + + destroy_twin_operation_context(twin_op_ctx); + } + } + + return result; +} + +static void process_timeouts(TWIN_MESSENGER_INSTANCE* twin_msgr) +{ + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_082: [If any failure occurs while verifying/removing timed-out items `twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_ERROR and user informed] + LogError("Failed obtaining current time (%s)", twin_msgr->device_id); + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_080: [twin_messenger_do_work() shall remove and destroy any timed out items from `twin_msgr->pending_patches` and `twin_msgr->operations`] + (void)singlylinkedlist_remove_if(twin_msgr->pending_patches, remove_expired_twin_patch_request, (const void*)¤t_time); + (void)singlylinkedlist_remove_if(twin_msgr->operations, remove_expired_twin_operation_request, (const void*)¤t_time); + } +} + +static bool send_pending_twin_patch(const void* item, const void* match_context, bool* continue_processing) +{ + bool result; + + if (item == NULL || match_context == NULL || continue_processing == NULL) + { + LogError("Invalid argument (item=%p, match_context=%p, continue_processing=%p)", item, match_context, continue_processing); + *continue_processing = false; + result = false; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)match_context; + TWIN_PATCH_OPERATION_CONTEXT* twin_patch_ctx = (TWIN_PATCH_OPERATION_CONTEXT*)item; + TWIN_OPERATION_CONTEXT* twin_op_ctx; + + if ((twin_op_ctx = create_twin_operation_context(twin_msgr, TWIN_OPERATION_TYPE_PATCH)) == NULL) + { + LogError("Failed creating context for sending reported state (%s)", twin_msgr->device_id); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_061: [If any other failure occurs sending the PATCH request, `on_report_state_complete_callback` shall be invoked with RESULT_ERROR and REASON_INTERNAL_ERROR] + if (twin_patch_ctx->on_report_state_complete_callback != NULL) + { + twin_patch_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_INTERNAL_ERROR, 0, twin_patch_ctx->on_report_state_complete_context); + } + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_062: [If amqp_send_async() succeeds, the PATCH request shall be queued into `twin_msgr->operations`] + else if (add_twin_operation_context_to_queue(twin_op_ctx) != RESULT_OK) + { + LogError("Failed adding TWIN operation context to queue (%s)", twin_msgr->device_id); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_061: [If any other failure occurs sending the PATCH request, `on_report_state_complete_callback` shall be invoked with RESULT_ERROR and REASON_INTERNAL_ERROR] + if (twin_patch_ctx->on_report_state_complete_callback != NULL) + { + twin_patch_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_INTERNAL_ERROR, 0, twin_patch_ctx->on_report_state_complete_context); + } + + destroy_twin_operation_context(twin_op_ctx); + } + else + { + twin_op_ctx->on_report_state_complete_callback = twin_patch_ctx->on_report_state_complete_callback; + twin_op_ctx->on_report_state_complete_context = twin_patch_ctx->on_report_state_complete_context; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_059: [If reported property PATCH shall be sent as an uAMQP MESSAGE_HANDLE instance using amqp_send_async() passing `on_amqp_send_complete_callback`] + if (send_twin_operation_request(twin_msgr, twin_op_ctx, twin_patch_ctx->data) != RESULT_OK) + { + LogError("Failed sending reported state (%s)", twin_msgr->device_id); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_060: [If amqp_send_async() fails, `on_report_state_complete_callback` shall be invoked with RESULT_ERROR and REASON_FAIL_SENDING] + if (twin_patch_ctx->on_report_state_complete_callback != NULL) + { + twin_patch_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_FAIL_SENDING, 0, twin_patch_ctx->on_report_state_complete_context); + } + + (void)remove_twin_operation_context_from_queue(twin_op_ctx); + destroy_twin_operation_context(twin_op_ctx); + } + } + + CONSTBUFFER_Destroy(twin_patch_ctx->data); + free(twin_patch_ctx); + + *continue_processing = true; + result = true; + } + + return result; +} + +static void process_twin_subscription(TWIN_MESSENGER_INSTANCE* twin_msgr) +{ + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_078: [If failures occur sending subscription requests to the service for more than 3 times, TWIN messenger shall set its state to TWIN_MESSENGER_STATE_ERROR and inform the user] + if (twin_msgr->subscription_error_count >= DEFAULT_MAX_TWIN_SUBSCRIPTION_ERROR_COUNT) + { + LogError("Maximum number of TWIN subscription-related failures reached (%s, %d)", twin_msgr->device_id, twin_msgr->subscription_error_count); + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + else + { + TWIN_OPERATION_TYPE op_type = TWIN_OPERATION_TYPE_PATCH; + TWIN_SUBSCRIPTION_STATE next_subscription_state = twin_msgr->subscription_state; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_074: [If subscribing, twin_messenger_do_work() shall request a complete desired properties report] + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES) + { + op_type = TWIN_OPERATION_TYPE_GET; + next_subscription_state = TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_075: [If a complete desired report has been received, the TWIN messenger shall request partial updates to desired properties] + else if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_SUBSCRIBE_FOR_UPDATES) + { + op_type = TWIN_OPERATION_TYPE_PUT; + next_subscription_state = TWIN_SUBSCRIPTION_STATE_SUBSCRIBING; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_076: [If unsubscribing, twin_messenger_do_work() shall send a DELETE request to the service] + else if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE) + { + op_type = TWIN_OPERATION_TYPE_PUT; + next_subscription_state = TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBING; + } + + if (next_subscription_state != twin_msgr->subscription_state) + { + TWIN_OPERATION_CONTEXT* twin_op_ctx; + + if ((twin_op_ctx = create_twin_operation_context(twin_msgr, op_type)) == NULL) + { + LogError("Failed creating a context for TWIN request (%s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_type)); + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + else + { + if (add_twin_operation_context_to_queue(twin_op_ctx) != RESULT_OK) + { + LogError("Failed queueing TWIN request context (%s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_type)); + destroy_twin_operation_context(twin_op_ctx); + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + else if (send_twin_operation_request(twin_msgr, twin_op_ctx, NULL) != RESULT_OK) + { + LogError("Failed sending TWIN request (%s, %s)", twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, op_type)); + + (void)remove_twin_operation_context_from_queue(twin_op_ctx); + destroy_twin_operation_context(twin_op_ctx); + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + else + { + twin_msgr->subscription_state = next_subscription_state; + } + } + } + } +} + +static bool cancel_all_pending_twin_operations(const void* item, const void* match_context, bool* continue_processing) +{ + bool result; + + if (item == NULL || continue_processing == NULL) + { + LogError("Invalid argument (item=%p, continue_processing=%p)", item, continue_processing); + *continue_processing = false; + result = false; + } + else + { + TWIN_OPERATION_CONTEXT* twin_op_ctx = (TWIN_OPERATION_CONTEXT*)item; + (void)match_context; + + if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PATCH) + { + if (twin_op_ctx->on_report_state_complete_callback != NULL) + { + twin_op_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_CANCELLED, TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED, 0, twin_op_ctx->on_report_state_complete_context); + } + } + + destroy_twin_operation_context(twin_op_ctx); + + *continue_processing = true; + result = true; + } + + return result; +} + +static bool cancel_pending_twin_patch_operation(const void* item, const void* match_context, bool* continue_processing) +{ + bool result; + + if (item == NULL) + { + LogError("Invalid argument (item is NULL)"); + *continue_processing = false; + result = false; + } + else + { + TWIN_PATCH_OPERATION_CONTEXT* twin_patch_ctx = (TWIN_PATCH_OPERATION_CONTEXT*)item; + (void)match_context; + + if (twin_patch_ctx->on_report_state_complete_callback != NULL) + { + twin_patch_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_CANCELLED, TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED, 0, twin_patch_ctx->on_report_state_complete_context); + } + + CONSTBUFFER_Destroy(twin_patch_ctx->data); + free(twin_patch_ctx); + + *continue_processing = true; + result = true; + } + + return result; +} + +static void internal_twin_messenger_destroy(TWIN_MESSENGER_INSTANCE* twin_msgr) +{ + if (twin_msgr->amqp_msgr != NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_099: [`twin_msgr->amqp_messenger` shall be destroyed using amqp_messenger_destroy()] + amqp_messenger_destroy(twin_msgr->amqp_msgr); + } + + if (twin_msgr->pending_patches != NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_100: [All elements of `twin_msgr->pending_patches` shall be removed, invoking `on_report_state_complete_callback` for each with TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED] + if (singlylinkedlist_remove_if(twin_msgr->pending_patches, cancel_pending_twin_patch_operation, twin_msgr) != 0) + { + LogError("Failed removing pending desired properties PATCH operation (%s)", twin_msgr->device_id); + } + + singlylinkedlist_destroy(twin_msgr->pending_patches); + } + + if (twin_msgr->operations != NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_101: [All elements of `twin_msgr->operations` shall be removed, invoking `on_report_state_complete_callback` for each PATCH with TWIN_REPORT_STATE_REASON_MESSENGER_DESTROYED] + singlylinkedlist_remove_if(twin_msgr->operations, cancel_all_pending_twin_operations, (const void*)twin_msgr); + singlylinkedlist_destroy(twin_msgr->operations); + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_102: [twin_messenger_destroy() shall release all memory allocated for and within `twin_msgr`] + if (twin_msgr->client_version != NULL) + { + free(twin_msgr->client_version); + } + + if (twin_msgr->device_id != NULL) + { + free(twin_msgr->device_id); + } + + if (twin_msgr->module_id != NULL) + { + free(twin_msgr->module_id); + } + + if (twin_msgr->iothub_host_fqdn != NULL) + { + free(twin_msgr->iothub_host_fqdn); + } + + free(twin_msgr); +} + + +//---------- Internal Callbacks ----------// + +static AMQP_MESSENGER_DISPOSITION_RESULT on_amqp_message_received_callback(MESSAGE_HANDLE message, AMQP_MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context) +{ + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_084: [If `message` or `context` are NULL, on_amqp_message_received_callback shall return immediately] + AMQP_MESSENGER_DISPOSITION_RESULT disposition_result; + + if (message == NULL || context == NULL) + { + LogError("Invalid argument (message=%p, context=%p)", message, context); + disposition_result = AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)context; + + char* correlation_id; + + bool has_status_code; + int status_code; + + bool has_version; + int64_t version; + + bool has_twin_report; + BINARY_DATA twin_report; + + amqp_messenger_destroy_disposition_info(disposition_info); + disposition_result = AMQP_MESSENGER_DISPOSITION_RESULT_ACCEPTED; + + if (parse_incoming_twin_message(message, &correlation_id, &has_version, &version, &has_status_code, &status_code, &has_twin_report, &twin_report) != 0) + { + LogError("Failed parsing incoming TWIN message (%s)", twin_msgr->device_id); + } + else + { + if (correlation_id != NULL) + { + // It is supposed to be a request sent previously (reported properties PATCH, GET, PUT or DELETE). + + LIST_ITEM_HANDLE list_item; + if ((list_item = singlylinkedlist_find(twin_msgr->operations, find_twin_operation_by_correlation_id, (const void*)correlation_id)) == NULL) + { + LogError("Could not find context of TWIN incoming message (%s, %s)", twin_msgr->device_id, correlation_id); + } + else + { + TWIN_OPERATION_CONTEXT* twin_op_ctx; + + if ((twin_op_ctx = (TWIN_OPERATION_CONTEXT*)singlylinkedlist_item_get_value(list_item)) == NULL) + { + LogError("Could not get context for incoming TWIN message (%s, %s)", twin_msgr->device_id, correlation_id); + } + else + { + if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PATCH) + { + if (!has_status_code) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_086: [If `message` is a failed response for a PATCH request, the `on_report_state_complete_callback` shall be invoked if provided passing RESULT_ERROR and the status_code zero] + LogError("Received an incoming TWIN message for a PATCH operation, but with no status code (%s, %s)", twin_msgr->device_id, correlation_id); + + disposition_result = AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED; + + if (twin_op_ctx->on_report_state_complete_callback != NULL) + { + twin_op_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_ERROR, TWIN_REPORT_STATE_REASON_INVALID_RESPONSE, 0, twin_op_ctx->on_report_state_complete_context); + } + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_085: [If `message` is a success response for a PATCH request, the `on_report_state_complete_callback` shall be invoked if provided passing RESULT_SUCCESS and the status_code received] + if (twin_op_ctx->on_report_state_complete_callback != NULL) + { + twin_op_ctx->on_report_state_complete_callback(TWIN_REPORT_STATE_RESULT_SUCCESS, TWIN_REPORT_STATE_REASON_NONE, status_code, twin_op_ctx->on_report_state_complete_context); + } + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_GET) + { + if (!has_twin_report) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_089: [If `message` is a failed response for a GET request, the TWIN messenger shall attempt to send another GET request] + LogError("Received an incoming TWIN message for a GET operation, but with no report (%s, %s)", twin_msgr->device_id, correlation_id); + + disposition_result = AMQP_MESSENGER_DISPOSITION_RESULT_REJECTED; + + if (twin_op_ctx->msgr->on_message_received_callback != NULL) + { + twin_op_ctx->msgr->on_message_received_callback(TWIN_UPDATE_TYPE_COMPLETE, NULL, 0, twin_op_ctx->msgr->on_message_received_context); + } + + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES; + twin_msgr->subscription_error_count++; + } + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_087: [If `message` is a success response for a GET request, `on_message_received_callback` shall be invoked with TWIN_UPDATE_TYPE_COMPLETE and the message body received] + if (twin_op_ctx->msgr->on_message_received_callback != NULL) + { + twin_op_ctx->msgr->on_message_received_callback(TWIN_UPDATE_TYPE_COMPLETE, (const char*)twin_report.bytes, twin_report.length, twin_op_ctx->msgr->on_message_received_context); + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_088: [If `message` is a success response for a GET request, the TWIN messenger shall trigger the subscription for partial updates] + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_GETTING_COMPLETE_PROPERTIES) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_SUBSCRIBE_FOR_UPDATES; + twin_msgr->subscription_error_count = 0; + } + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_PUT) + { + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_SUBSCRIBED) + { + bool subscription_succeeded = true; + + if (!has_status_code) + { + LogError("Received an incoming TWIN message for a PUT operation, but with no status code (%s, %s)", twin_msgr->device_id, correlation_id); + + subscription_succeeded = false; + } + else if (status_code < 200 || status_code >= 300) + { + LogError("Received status code %d for TWIN subscription request (%s, %s)", status_code, twin_msgr->device_id, correlation_id); + + subscription_succeeded = false; + } + + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_SUBSCRIBING) + { + if (subscription_succeeded) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_SUBSCRIBED; + twin_msgr->subscription_error_count = 0; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_090: [If `message` is a failed response for a PUT request, the TWIN messenger shall attempt to send another PUT request] + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_SUBSCRIBE_FOR_UPDATES; + twin_msgr->subscription_error_count++; + } + } + } + } + else if (twin_op_ctx->type == TWIN_OPERATION_TYPE_DELETE) + { + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_NOT_SUBSCRIBED) + { + bool unsubscription_succeeded = true; + + if (!has_status_code) + { + LogError("Received an incoming TWIN message for a DELETE operation, but with no status code (%s, %s)", twin_msgr->device_id, correlation_id); + + unsubscription_succeeded = false; + } + else if (status_code < 200 || status_code >= 300) + { + LogError("Received status code %d for TWIN unsubscription request (%s, %s)", status_code, twin_msgr->device_id, correlation_id); + + unsubscription_succeeded = false; + } + + if (twin_msgr->subscription_state == TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBING) + { + if (unsubscription_succeeded) + { + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_NOT_SUBSCRIBED; + twin_msgr->subscription_error_count = 0; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_091: [If `message` is a failed response for a DELETE request, the TWIN messenger shall attempt to send another DELETE request] + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE; + twin_msgr->subscription_error_count++; + } + } + } + } + + destroy_twin_operation_context(twin_op_ctx); + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_092: [The corresponding TWIN request shall be removed from `twin_msgr->operations` and destroyed] + if (singlylinkedlist_remove(twin_msgr->operations, list_item) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_093: [The corresponding TWIN request failed to be removed from `twin_msgr->operations`, `twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_ERROR and informed to the user] + LogError("Failed removing context for incoming TWIN message (%s, %s, %s)", + twin_msgr->device_id, ENUM_TO_STRING(TWIN_OPERATION_TYPE, twin_op_ctx->type), correlation_id); + + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + } + + free(correlation_id); + } + else if (has_twin_report) + { + // It is supposed to be a desired properties delta update. + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_094: [If `message` is not a client request, `on_message_received_callback` shall be invoked with TWIN_UPDATE_TYPE_PARTIAL and the message body received] + if (twin_msgr->on_message_received_callback != NULL) + { + twin_msgr->on_message_received_callback(TWIN_UPDATE_TYPE_PARTIAL, (const char*)twin_report.bytes, twin_report.length, twin_msgr->on_message_received_context); + } + } + else + { + LogError("Received TWIN message with no correlation-id and no report (%s)", twin_msgr->device_id); + } + } + } + + return disposition_result; +} + +static void on_amqp_messenger_state_changed_callback(void* context, AMQP_MESSENGER_STATE previous_state, AMQP_MESSENGER_STATE new_state) +{ + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else if (new_state != previous_state) + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)context; + + if (twin_msgr->state == TWIN_MESSENGER_STATE_STARTING && new_state == AMQP_MESSENGER_STATE_STARTED) + { + if (twin_msgr->amqp_msgr_is_subscribed) + { + update_state(twin_msgr, TWIN_MESSENGER_STATE_STARTED); + } + // Else, it shall wait for the moment the AMQP msgr is subscribed. + } + else if (twin_msgr->state == TWIN_MESSENGER_STATE_STOPPING && new_state == TWIN_MESSENGER_STATE_STOPPED) + { + if (!twin_msgr->amqp_msgr_is_subscribed) + { + update_state(twin_msgr, TWIN_MESSENGER_STATE_STOPPED); + } + // Else, it shall wait for the moment the AMQP msgr is unsubscribed. + } + else if ((twin_msgr->state == TWIN_MESSENGER_STATE_STARTING && new_state == AMQP_MESSENGER_STATE_STARTING) || + (twin_msgr->state == TWIN_MESSENGER_STATE_STOPPING && new_state == AMQP_MESSENGER_STATE_STOPPING)) + { + // Do nothing, this is expected. + } + else + { + LogError("Unexpected AMQP messenger state (%s, %s, %s)", + twin_msgr->device_id, ENUM_TO_STRING(TWIN_MESSENGER_STATE, twin_msgr->state), ENUM_TO_STRING(AMQP_MESSENGER_STATE, new_state)); + + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + + twin_msgr->amqp_msgr_state = new_state; + } +} + +static void on_amqp_messenger_subscription_changed_callback(void* context, bool is_subscribed) +{ + if (context == NULL) + { + LogError("Invalid argument (context is NULL)"); + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)context; + + if (twin_msgr->state == TWIN_MESSENGER_STATE_STARTING && is_subscribed) + { + if (twin_msgr->amqp_msgr_state == AMQP_MESSENGER_STATE_STARTED) + { + update_state(twin_msgr, TWIN_MESSENGER_STATE_STARTED); + } + // Else, it shall wait for the moment the AMQP msgr is STARTED. + } + else if (twin_msgr->state == TWIN_MESSENGER_STATE_STOPPING && !is_subscribed) + { + if (twin_msgr->amqp_msgr_state == AMQP_MESSENGER_STATE_STOPPED) + { + update_state(twin_msgr, TWIN_MESSENGER_STATE_STOPPED); + } + // Else, it shall wait for the moment the AMQP msgr is STOPPED. + } + else + { + LogError("Unexpected AMQP messenger state (%s, %s, %d)", + twin_msgr->device_id, ENUM_TO_STRING(TWIN_MESSENGER_STATE, twin_msgr->state), is_subscribed); + + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + } + + twin_msgr->amqp_msgr_is_subscribed = is_subscribed; + } +} + + +//---------- Public APIs ----------// + +TWIN_MESSENGER_HANDLE twin_messenger_create(const TWIN_MESSENGER_CONFIG* messenger_config) +{ + TWIN_MESSENGER_INSTANCE* twin_msgr; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_001: [If parameter `messenger_config` is NULL, twin_messenger_create() shall return NULL] + if (messenger_config == NULL) + { + LogError("Invalid argument (messenger_config is NULL)"); + twin_msgr = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_002: [If `messenger_config`'s `device_id`, `iothub_host_fqdn` or `client_version` is NULL, twin_messenger_create() shall return NULL] + else if (messenger_config->device_id == NULL || messenger_config->iothub_host_fqdn == NULL || messenger_config->client_version == NULL) + { + LogError("Invalid argument (device_id=%p, iothub_host_fqdn=%p, client_version=%p)", + messenger_config->device_id, messenger_config->iothub_host_fqdn, messenger_config->client_version); + twin_msgr = NULL; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_003: [twin_messenger_create() shall allocate memory for the messenger instance structure (aka `twin_msgr`)] + if ((twin_msgr = (TWIN_MESSENGER_INSTANCE*)malloc(sizeof(TWIN_MESSENGER_INSTANCE))) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_004: [If malloc() fails, twin_messenger_create() shall fail and return NULL] + LogError("Failed allocating TWIN_MESSENGER_INSTANCE (%s)", messenger_config->device_id); + } + else + { + MAP_HANDLE link_attach_properties; + + memset(twin_msgr, 0, sizeof(TWIN_MESSENGER_INSTANCE)); + twin_msgr->state = TWIN_MESSENGER_STATE_STOPPED; + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_NOT_SUBSCRIBED; + twin_msgr->amqp_msgr_state = AMQP_MESSENGER_STATE_STOPPED; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_005: [twin_messenger_create() shall save a copy of `messenger_config` info into `twin_msgr`] + if (mallocAndStrcpy_s(&twin_msgr->client_version, messenger_config->client_version) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_006: [If any `messenger_config` info fails to be copied, twin_messenger_create() shall fail and return NULL] + LogError("Failed copying client_version (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else if (mallocAndStrcpy_s(&twin_msgr->device_id, messenger_config->device_id) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_006: [If any `messenger_config` info fails to be copied, twin_messenger_create() shall fail and return NULL] + LogError("Failed copying device_id (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else if ((messenger_config->module_id != NULL) && (mallocAndStrcpy_s(&twin_msgr->module_id, messenger_config->module_id) != 0)) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_006: [If any `messenger_config` info fails to be copied, twin_messenger_create() shall fail and return NULL] + LogError("Failed copying module_id (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else if (mallocAndStrcpy_s(&twin_msgr->iothub_host_fqdn, messenger_config->iothub_host_fqdn) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_006: [If any `messenger_config` info fails to be copied, twin_messenger_create() shall fail and return NULL] + LogError("Failed copying iothub_host_fqdn (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_007: [`twin_msgr->pending_patches` shall be set using singlylinkedlist_create()] + else if ((twin_msgr->pending_patches = singlylinkedlist_create()) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_008: [If singlylinkedlist_create() fails, twin_messenger_create() shall fail and return NULL] + LogError("Failed creating list for queueing patches (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_009: [`twin_msgr->operations` shall be set using singlylinkedlist_create()] + else if ((twin_msgr->operations = singlylinkedlist_create()) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_010: [If singlylinkedlist_create() fails, twin_messenger_create() shall fail and return NULL] + LogError("Failed creating list for operations (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else if ((link_attach_properties = create_link_attach_properties(twin_msgr)) == NULL) + { + LogError("Failed creating link attach properties (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else + { + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_012: [`amqp_msgr_config->client_version` shall be set with `twin_msgr->client_version`] + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_013: [`amqp_msgr_config->device_id` shall be set with `twin_msgr->device_id`] + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_014: [`amqp_msgr_config->iothub_host_fqdn` shall be set with `twin_msgr->iothub_host_fqdn`] + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_015: [`amqp_msgr_config` shall have "twin/" as send link target suffix and receive link source suffix] + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_016: [`amqp_msgr_config` shall have send and receive link attach properties set as "com.microsoft:client-version" = `twin_msgr->client_version`, "com.microsoft:channel-correlation-id" = `twin:<UUID>`, "com.microsoft:api-version" = "2016-11-14"] + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_017: [`amqp_msgr_config` shall be set with `on_amqp_messenger_state_changed_callback` and `on_amqp_messenger_subscription_changed_callback` callbacks] + AMQP_MESSENGER_CONFIG amqp_msgr_config; + amqp_msgr_config.client_version = twin_msgr->client_version; + amqp_msgr_config.device_id = twin_msgr->device_id; + amqp_msgr_config.module_id = twin_msgr->module_id; + amqp_msgr_config.iothub_host_fqdn = twin_msgr->iothub_host_fqdn; + amqp_msgr_config.send_link.target_suffix = DEFAULT_TWIN_SEND_LINK_SOURCE_NAME; + amqp_msgr_config.send_link.attach_properties = link_attach_properties; + amqp_msgr_config.send_link.snd_settle_mode = sender_settle_mode_settled; + amqp_msgr_config.send_link.rcv_settle_mode = receiver_settle_mode_first; + amqp_msgr_config.receive_link.source_suffix = DEFAULT_TWIN_RECEIVE_LINK_TARGET_NAME; + amqp_msgr_config.receive_link.attach_properties = link_attach_properties; + amqp_msgr_config.receive_link.snd_settle_mode = sender_settle_mode_settled; + amqp_msgr_config.receive_link.rcv_settle_mode = receiver_settle_mode_first; + amqp_msgr_config.on_state_changed_callback = on_amqp_messenger_state_changed_callback; + amqp_msgr_config.on_state_changed_context = (void*)twin_msgr; + amqp_msgr_config.on_subscription_changed_callback = on_amqp_messenger_subscription_changed_callback; + amqp_msgr_config.on_subscription_changed_context = (void*)twin_msgr; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_011: [`twin_msgr->amqp_msgr` shall be set using amqp_messenger_create(), passing a AMQP_MESSENGER_CONFIG instance `amqp_msgr_config`] + if ((twin_msgr->amqp_msgr = amqp_messenger_create(&amqp_msgr_config)) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_018: [If amqp_messenger_create() fails, twin_messenger_create() shall fail and return NULL] + LogError("Failed creating the AMQP messenger (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_019: [`twin_msgr->amqp_msgr` shall subscribe for AMQP messages by calling amqp_messenger_subscribe_for_messages() passing `on_amqp_message_received`] + else if (amqp_messenger_subscribe_for_messages(twin_msgr->amqp_msgr, on_amqp_message_received_callback, (void*)twin_msgr)) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_020: [If amqp_messenger_subscribe_for_messages() fails, twin_messenger_create() shall fail and return NULL] + LogError("Failed subscribing for AMQP messages (%s)", messenger_config->device_id); + internal_twin_messenger_destroy(twin_msgr); + twin_msgr = NULL; + } + else + { + // Codes_SRS_IOTHUBTRANSPORT_TWIN_MESSENGER_09_013: [`messenger_config->on_state_changed_callback` shall be saved into `twin_msgr->on_state_changed_callback`] + twin_msgr->on_state_changed_callback = messenger_config->on_state_changed_callback; + + // Codes_SRS_IOTHUBTRANSPORT_TWIN_MESSENGER_09_014: [`messenger_config->on_state_changed_context` shall be saved into `twin_msgr->on_state_changed_context`] + twin_msgr->on_state_changed_context = messenger_config->on_state_changed_context; + } + + destroy_link_attach_properties(link_attach_properties); + } + } + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_021: [If no failures occurr, twin_messenger_create() shall return a handle to `twin_msgr`] + return (TWIN_MESSENGER_HANDLE)twin_msgr; +} + +int twin_messenger_report_state_async(TWIN_MESSENGER_HANDLE twin_msgr_handle, CONSTBUFFER_HANDLE data, TWIN_MESSENGER_REPORT_STATE_COMPLETE_CALLBACK on_report_state_complete_callback, const void* context) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_022: [If `twin_msgr_handle` or `data` are NULL, twin_messenger_report_state_async() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL || data == NULL) + { + LogError("Invalid argument (twin_msgr_handle=%p, data=%p)", twin_msgr_handle, data); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + TWIN_PATCH_OPERATION_CONTEXT* twin_patch_ctx; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_023: [twin_messenger_report_state_async() shall allocate memory for a TWIN_PATCH_OPERATION_CONTEXT structure (aka `twin_op_ctx`)] + if ((twin_patch_ctx = (TWIN_PATCH_OPERATION_CONTEXT*)malloc(sizeof(TWIN_PATCH_OPERATION_CONTEXT))) == NULL) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_024: [If malloc() fails, twin_messenger_report_state_async() shall fail and return a non-zero value] + LogError("Failed creating context for sending reported state (%s)", twin_msgr->device_id); + result = __FAILURE__; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_025: [`twin_op_ctx` shall have a copy of `data`] + else if ((twin_patch_ctx->data = CONSTBUFFER_Clone(data)) == NULL) + { + LogError("Failed cloning TWIN patch request data (%s)", twin_msgr->device_id); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_031: [If any failure occurs, twin_messenger_report_state_async() shall free any memory it has allocated] + free(twin_patch_ctx); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_026: [If `data` fails to be copied, twin_messenger_report_state_async() shall fail and return a non-zero value] + result = __FAILURE__; + } + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_027: [`twin_op_ctx->time_enqueued` shall be set using get_time] + else if ((twin_patch_ctx->time_enqueued = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("Failed setting reported state enqueue time (%s)", twin_msgr->device_id); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_031: [If any failure occurs, twin_messenger_report_state_async() shall free any memory it has allocated] + CONSTBUFFER_Destroy(twin_patch_ctx->data); + free(twin_patch_ctx); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_028: [If `twin_op_ctx->time_enqueued` fails to be set, twin_messenger_report_state_async() shall fail and return a non-zero value] + result = __FAILURE__; + } + else + { + twin_patch_ctx->on_report_state_complete_callback = on_report_state_complete_callback; + twin_patch_ctx->on_report_state_complete_context = context; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_029: [`twin_op_ctx` shall be added to `twin_msgr->pending_patches` using singlylinkedlist_add()] + if (singlylinkedlist_add(twin_msgr->pending_patches, twin_patch_ctx) == NULL) + { + LogError("Failed adding TWIN patch request to queue (%s)", twin_msgr->device_id); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_031: [If any failure occurs, twin_messenger_report_state_async() shall free any memory it has allocated] + CONSTBUFFER_Destroy(twin_patch_ctx->data); + free(twin_patch_ctx); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_030: [If singlylinkedlist_add() fails, twin_messenger_report_state_async() shall fail and return a non-zero value] + result = __FAILURE__; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_032: [If no failures occur, twin_messenger_report_state_async() shall return zero] + result = RESULT_OK; + } + } + } + + return result; +} + +int twin_messenger_subscribe(TWIN_MESSENGER_HANDLE twin_msgr_handle, TWIN_STATE_UPDATE_CALLBACK on_twin_state_update_callback, void* context) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_037: [If `twin_msgr_handle` or `on_twin_state_update_callback` are NULL, twin_messenger_subscribe() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL || on_twin_state_update_callback == NULL) + { + LogError("Invalid argument (twin_msgr_handle=%p, on_twin_state_update_callback=%p)", twin_msgr_handle, on_twin_state_update_callback); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_038: [If `twin_msgr` is already subscribed, twin_messenger_subscribe() shall return zero] + if (twin_msgr->subscription_state != TWIN_SUBSCRIPTION_STATE_NOT_SUBSCRIBED) + { + result = RESULT_OK; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_039: [`on_twin_state_update_callback` and `context` shall be saved on `twin_msgr`] + twin_msgr->on_message_received_callback = on_twin_state_update_callback; + twin_msgr->on_message_received_context = context; + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_040: [twin_messenger_subscribe() shall change `twin_msgr->subscription_state` to TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES] + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_GET_COMPLETE_PROPERTIES; + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_041: [If no failures occurr, twin_messenger_subscribe() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int twin_messenger_unsubscribe(TWIN_MESSENGER_HANDLE twin_msgr_handle) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_042: [If `twin_msgr_handle` is NULL, twin_messenger_unsubscribe() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL) + { + LogError("Invalid argument (twin_msgr_handle is NULL)"); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_043: [twin_messenger_subscribe() shall change `twin_msgr->subscription_state` to TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE] + twin_msgr->subscription_state = TWIN_SUBSCRIPTION_STATE_UNSUBSCRIBE; + twin_msgr->on_message_received_callback = NULL; + twin_msgr->on_message_received_context = NULL; + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_044: [If no failures occurr, twin_messenger_unsubscribe() shall return zero] + result = RESULT_OK; + } + + return result; +} + +int twin_messenger_get_send_status(TWIN_MESSENGER_HANDLE twin_msgr_handle, TWIN_MESSENGER_SEND_STATUS* send_status) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_033: [If `twin_msgr_handle` or `send_status` are NULL, twin_messenger_get_send_status() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL || send_status == NULL) + { + LogError("Invalid argument (twin_msgr_handle=%p, send_status=%p)", twin_msgr_handle, send_status); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + TWIN_OPERATION_TYPE twin_op_type = TWIN_OPERATION_TYPE_PATCH; + + if (singlylinkedlist_get_head_item(twin_msgr->pending_patches) != NULL || + singlylinkedlist_find(twin_msgr->operations, find_twin_operation_by_type, &twin_op_type)) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_034: [If `twin_msgr->pending_patches` or `twin_msgr->operations` have any TWIN patch requests, send_status shall be set to TWIN_MESSENGER_SEND_STATUS_BUSY] + *send_status = TWIN_MESSENGER_SEND_STATUS_BUSY; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_035: [Otherwise, send_status shall be set to TWIN_MESSENGER_SEND_STATUS_IDLE] + *send_status = TWIN_MESSENGER_SEND_STATUS_IDLE; + } + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_036: [If no failures occur, twin_messenger_get_send_status() shall return 0] + result = RESULT_OK; + } + + return result; +} + +int twin_messenger_start(TWIN_MESSENGER_HANDLE twin_msgr_handle, SESSION_HANDLE session_handle) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_045: [If `twin_msgr_handle` or `session_handle` are NULL, twin_messenger_start() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL || session_handle == NULL) + { + LogError("Invalid argument (twin_msgr_handle=%p, session_handle=%p)", twin_msgr_handle, session_handle); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_048: [If no failures occurr, `twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_STARTING, and `twin_msgr->on_state_changed_callback` invoked if provided] + update_state(twin_msgr, TWIN_MESSENGER_STATE_STARTING); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_046: [amqp_messenger_start() shall be invoked passing `twin_msgr->amqp_msgr` and `session_handle`] + if (amqp_messenger_start(twin_msgr->amqp_msgr, session_handle) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_047: [If amqp_messenger_start() fails, twin_messenger_start() fail and return a non-zero value] + LogError("Failed starting the AMQP messenger (%s)", twin_msgr->device_id); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_049: [If any failures occurr, `twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_ERROR, and `twin_msgr->on_state_changed_callback` invoked if provided] + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + result = __FAILURE__; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_050: [If no failures occurr, twin_messenger_start() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +int twin_messenger_stop(TWIN_MESSENGER_HANDLE twin_msgr_handle) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_051: [If `twin_msgr_handle` is NULL, twin_messenger_stop() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL) + { + LogError("Invalid argument (twin_msgr_handle is NULL)"); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_054: [`twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_STOPPING, and `twin_msgr->on_state_changed_callback` invoked if provided] + update_state(twin_msgr, TWIN_MESSENGER_STATE_STOPPING); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_052: [amqp_messenger_stop() shall be invoked passing `twin_msgr->amqp_msgr`] + if (amqp_messenger_stop(twin_msgr->amqp_msgr) != 0) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_053: [If amqp_messenger_stop() fails, twin_messenger_stop() fail and return a non-zero value] + LogError("Failed stopping the AMQP messenger (%s)", twin_msgr->device_id); + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_055: [If any failures occurr, `twin_msgr->state` shall be set to TWIN_MESSENGER_STATE_ERROR, and `twin_msgr->on_state_changed_callback` invoked if provided] + update_state(twin_msgr, TWIN_MESSENGER_STATE_ERROR); + result = __FAILURE__; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_056: [If no failures occurr, twin_messenger_stop() shall return 0] + result = RESULT_OK; + } + } + + return result; +} + +void twin_messenger_do_work(TWIN_MESSENGER_HANDLE twin_msgr_handle) +{ + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_057: [If `twin_msgr_handle` is NULL, twin_messenger_do_work() shall return immediately] + if (twin_msgr_handle != NULL) + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + if (twin_msgr->state == TWIN_MESSENGER_STATE_STARTED) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_058: [If `twin_msgr->state` is TWIN_MESSENGER_STATE_STARTED, twin_messenger_do_work() shall send the PATCHES in `twin_msgr->pending_patches`, removing them from the list] + (void)singlylinkedlist_remove_if(twin_msgr->pending_patches, send_pending_twin_patch, (const void*)twin_msgr); + + process_twin_subscription(twin_msgr); + } + + process_timeouts(twin_msgr); + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_083: [twin_messenger_do_work() shall invoke amqp_messenger_do_work() passing `twin_msgr->amqp_msgr`] + amqp_messenger_do_work(twin_msgr->amqp_msgr); + } +} + +void twin_messenger_destroy(TWIN_MESSENGER_HANDLE twin_msgr_handle) +{ + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_098: [If `twin_msgr_handle` is NULL, twin_messenger_destroy() shall return immediately] + if (twin_msgr_handle == NULL) + { + LogError("Invalid argument (twin_msgr_handle is NULL)"); + } + else + { + internal_twin_messenger_destroy((TWIN_MESSENGER_INSTANCE*)twin_msgr_handle); + } +} + +int twin_messenger_set_option(TWIN_MESSENGER_HANDLE twin_msgr_handle, const char* name, void* value) +{ + int result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_103: [If `twin_msgr_handle` or `name` or `value` are NULL, twin_messenger_set_option() shall fail and return a non-zero value] + if (twin_msgr_handle == NULL || name == NULL || value == NULL) + { + LogError("Invalid argument (twin_msgr_handle=%p, name=%p, value=%p)", twin_msgr_handle, name, value); + result = __FAILURE__; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_104: [amqp_messenger_set_option() shall be invoked passing `name` and `option`] + if (amqp_messenger_set_option(twin_msgr->amqp_msgr, name, value) != RESULT_OK) + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_105: [If amqp_messenger_set_option() fails, twin_messenger_set_option() shall fail and return a non-zero value] + LogError("Failed setting TWIN messenger option (%s, %s)", twin_msgr->device_id, name); + result = __FAILURE__; + } + else + { + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_106: [If no errors occur, twin_messenger_set_option shall return zero] + result = RESULT_OK; + } + } + + return result; +} + +OPTIONHANDLER_HANDLE twin_messenger_retrieve_options(TWIN_MESSENGER_HANDLE twin_msgr_handle) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_107: [If `twin_msgr_handle` is NULL, twin_messenger_retrieve_options shall fail and return NULL] + if (twin_msgr_handle == NULL) + { + LogError("Invalid argument (twin_msgr_handle is NULL)"); + result = NULL; + } + else + { + TWIN_MESSENGER_INSTANCE* twin_msgr = (TWIN_MESSENGER_INSTANCE*)twin_msgr_handle; + + // Codes_IOTHUBTRANSPORT_AMQP_TWIN_MESSENGER_09_108: [twin_messenger_retrieve_options() shall return the result of amqp_messenger_retrieve_options()] + if ((result = amqp_messenger_retrieve_options(twin_msgr->amqp_msgr)) == NULL) + { + LogError("Failed TWIN messenger options (%s)", twin_msgr->device_id); + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransport_mqtt_common.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,3353 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/agenttime.h" + +#include "iothub_client_core_ll.h" +#include "iothub_client_options.h" +#include "internal/iothub_client_private.h" +#include "azure_umqtt_c/mqtt_client.h" +#include "azure_c_shared_utility/sastoken.h" +#include "azure_c_shared_utility/tickcounter.h" + +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" + +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/shared_util_options.h" +#include "azure_c_shared_utility/urlencode.h" +#include "iothub_client_version.h" +#include "internal/iothub_client_retry_control.h" + +#include "internal/iothubtransport_mqtt_common.h" + +#include <stdarg.h> +#include <stdio.h> + +#include <limits.h> +#include <inttypes.h> + +#define SAS_TOKEN_DEFAULT_LIFETIME 3600 +#define SAS_REFRESH_MULTIPLIER .8 +#define EPOCH_TIME_T_VALUE 0 +#define DEFAULT_MQTT_KEEPALIVE 4*60 // 4 min +#define DEFAULT_CONNACK_TIMEOUT 30 // 30 seconds +#define BUILD_CONFIG_USERNAME 24 +#define SAS_TOKEN_DEFAULT_LEN 10 +#define RESEND_TIMEOUT_VALUE_MIN 1*60 +#define MAX_SEND_RECOUNT_LIMIT 2 +#define DEFAULT_CONNECTION_INTERVAL 30 +#define FAILED_CONN_BACKOFF_VALUE 5 +#define STATUS_CODE_FAILURE_VALUE 500 +#define STATUS_CODE_TIMEOUT_VALUE 408 + +#define DEFAULT_RETRY_POLICY IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER +#define DEFAULT_RETRY_TIMEOUT_IN_SECONDS 0 + +static const char TOPIC_DEVICE_TWIN_PREFIX[] = "$iothub/twin"; +static const char TOPIC_DEVICE_METHOD_PREFIX[] = "$iothub/methods"; + +static const char* TOPIC_GET_DESIRED_STATE = "$iothub/twin/res/#"; +static const char* TOPIC_NOTIFICATION_STATE = "$iothub/twin/PATCH/properties/desired/#"; + +static const char* TOPIC_DEVICE_MSG = "devices/%s/messages/devicebound/#"; +static const char* TOPIC_DEVICE_MODULE_MSG = "devices/%s/modules/%s/messages/devicebound/#"; +static const char* TOPIC_DEVICE_DEVICE = "devices/%s/messages/events/"; +static const char* TOPIC_DEVICE_DEVICE_MODULE = "devices/%s/modules/%s/messages/events/"; + +static const char* TOPIC_INPUT_QUEUE_NAME = "devices/%s/modules/%s/#"; + +static const char* TOPIC_DEVICE_METHOD_SUBSCRIBE = "$iothub/methods/POST/#"; + +static const char* IOTHUB_API_VERSION = "2017-11-08-preview"; + +static const char* PROPERTY_SEPARATOR = "&"; +static const char* REPORTED_PROPERTIES_TOPIC = "$iothub/twin/PATCH/properties/reported/?$rid=%"PRIu16; +static const char* GET_PROPERTIES_TOPIC = "$iothub/twin/GET/?$rid=%"PRIu16; +static const char* DEVICE_METHOD_RESPONSE_TOPIC = "$iothub/methods/res/%d/?$rid=%s"; + +static const char* REQUEST_ID_PROPERTY = "?$rid="; + +static const char* MESSAGE_ID_PROPERTY = "mid"; +static const char* CORRELATION_ID_PROPERTY = "cid"; +static const char* CONTENT_TYPE_PROPERTY = "ct"; +static const char* CONTENT_ENCODING_PROPERTY = "ce"; +static const char* DIAGNOSTIC_ID_PROPERTY = "diagid"; +static const char* DIAGNOSTIC_CONTEXT_PROPERTY = "diagctx"; +static const char* CONNECTION_DEVICE_ID = "cdid"; +static const char* CONNECTION_MODULE_ID_PROPERTY = "cmid"; + + +static const char* DIAGNOSTIC_CONTEXT_CREATION_TIME_UTC_PROPERTY = "creationtimeutc"; + +#define UNSUBSCRIBE_FROM_TOPIC 0x0000 +#define SUBSCRIBE_GET_REPORTED_STATE_TOPIC 0x0001 +#define SUBSCRIBE_NOTIFICATION_STATE_TOPIC 0x0002 +#define SUBSCRIBE_TELEMETRY_TOPIC 0x0004 +#define SUBSCRIBE_METHODS_TOPIC 0x0008 +#define SUBSCRIBE_DEVICE_METHOD_TOPIC 0x0010 +#define SUBSCRIBE_INPUT_QUEUE_TOPIC 0x0020 +#define SUBSCRIBE_TOPIC_COUNT 5 + +DEFINE_ENUM_STRINGS(MQTT_CLIENT_EVENT_ERROR, MQTT_CLIENT_EVENT_ERROR_VALUES) + +typedef struct SYSTEM_PROPERTY_INFO_TAG +{ + const char* propName; + size_t propLength; +} SYSTEM_PROPERTY_INFO; + +static SYSTEM_PROPERTY_INFO sysPropList[] = { + { "%24.exp", 7 }, +{ "%24.mid", 7 }, +{ "%24.uid", 7 }, +{ "%24.to", 6 }, +{ "%24.cid", 7 }, +{ "%24.ct", 6 }, +{ "%24.ce", 6 }, +{ "devices/", 8 }, +{ "iothub-operation", 16 }, +{ "iothub-ack", 10 }, +{ "%24.on", 6 }, +{ "%24.cdid", 8 }, +{ "%24.cmid", 8 } +}; + +static const int slashes_to_reach_input_name = 5; + +typedef enum DEVICE_TWIN_MSG_TYPE_TAG +{ + REPORTED_STATE, + RETRIEVE_PROPERTIES +} DEVICE_TWIN_MSG_TYPE; + +typedef enum MQTT_TRANSPORT_CREDENTIAL_TYPE_TAG +{ + CREDENTIAL_NOT_BUILD, + X509, + SAS_TOKEN_FROM_USER, + DEVICE_KEY, +} MQTT_TRANSPORT_CREDENTIAL_TYPE; + +typedef enum MQTT_CLIENT_STATUS_TAG +{ + MQTT_CLIENT_STATUS_NOT_CONNECTED, + MQTT_CLIENT_STATUS_CONNECTING, + MQTT_CLIENT_STATUS_CONNECTED, + MQTT_CLIENT_STATUS_PENDING_CLOSE +} MQTT_CLIENT_STATUS; + +typedef struct MQTTTRANSPORT_HANDLE_DATA_TAG +{ + // Topic control + STRING_HANDLE topic_MqttEvent; + STRING_HANDLE topic_MqttMessage; + STRING_HANDLE topic_GetState; + STRING_HANDLE topic_NotifyState; + STRING_HANDLE topic_InputQueue; + + STRING_HANDLE topic_DeviceMethods; + + uint32_t topics_ToSubscribe; + + // Connection related constants + STRING_HANDLE hostAddress; + STRING_HANDLE device_id; + STRING_HANDLE module_id; + STRING_HANDLE devicesAndModulesPath; + int portNum; + bool conn_attempted; + + MQTT_GET_IO_TRANSPORT get_io_transport; + + // The current mqtt iothub implementation requires that the hub name and the domain suffix be passed as the first of a series of segments + // passed through the username portion of the connection frame. + // The second segment will contain the device id. The two segments are delemited by a "/". + // The first segment can be a maximum 256 characters. + // The second segment can be a maximum 128 characters. + // With the / delimeter you have 384 chars (Plus a terminator of 0). + STRING_HANDLE configPassedThroughUsername; + + // Upper layer + IOTHUB_CLIENT_CORE_LL_HANDLE llClientHandle; + + // Protocol + MQTT_CLIENT_HANDLE mqttClient; + XIO_HANDLE xioTransport; + + // Session - connection + uint16_t packetId; + + // Connection state control + bool isRegistered; + MQTT_CLIENT_STATUS mqttClientStatus; + bool isDestroyCalled; + bool device_twin_get_sent; + bool isRecoverableError; + uint16_t keepAliveValue; + uint16_t connect_timeout_in_sec; + tickcounter_ms_t mqtt_connect_time; + size_t connectFailCount; + tickcounter_ms_t connectTick; + bool log_trace; + bool raw_trace; + TICK_COUNTER_HANDLE msgTickCounter; + OPTIONHANDLER_HANDLE saved_tls_options; // Here are the options from the xio layer if any is saved. + size_t option_sas_token_lifetime_secs; + + // Internal lists for message tracking + PDLIST_ENTRY waitingToSend; + DLIST_ENTRY ack_waiting_queue; + + // Message tracking + CONTROL_PACKET_TYPE currPacketState; + + // Telemetry specific + DLIST_ENTRY telemetry_waitingForAck; + bool auto_url_encode_decode; + + // Controls frequency of reconnection logic. + RETRY_CONTROL_HANDLE retry_control_handle; + + // Auth module used to generating handle authorization + // with either SAS Token, x509 Certs, and Device SAS Token + IOTHUB_AUTHORIZATION_HANDLE authorization_module; + + char* http_proxy_hostname; + int http_proxy_port; + char* http_proxy_username; + char* http_proxy_password; + bool isProductInfoSet; +} MQTTTRANSPORT_HANDLE_DATA, *PMQTTTRANSPORT_HANDLE_DATA; + +typedef struct MQTT_DEVICE_TWIN_ITEM_TAG +{ + tickcounter_ms_t msgPublishTime; + size_t retryCount; + IOTHUB_IDENTITY_TYPE iothub_type; + uint16_t packet_id; + uint32_t iothub_msg_id; + IOTHUB_DEVICE_TWIN* device_twin_data; + DEVICE_TWIN_MSG_TYPE device_twin_msg_type; + DLIST_ENTRY entry; +} MQTT_DEVICE_TWIN_ITEM; + +typedef struct MQTT_MESSAGE_DETAILS_LIST_TAG +{ + tickcounter_ms_t msgPublishTime; + size_t retryCount; + IOTHUB_MESSAGE_LIST* iotHubMessageEntry; + void* context; + uint16_t packet_id; + DLIST_ENTRY entry; +} MQTT_MESSAGE_DETAILS_LIST, *PMQTT_MESSAGE_DETAILS_LIST; + +typedef struct DEVICE_METHOD_INFO_TAG +{ + STRING_HANDLE request_id; +} DEVICE_METHOD_INFO; + +static void free_proxy_data(MQTTTRANSPORT_HANDLE_DATA* mqtt_transport_instance) +{ + if (mqtt_transport_instance->http_proxy_hostname != NULL) + { + free(mqtt_transport_instance->http_proxy_hostname); + mqtt_transport_instance->http_proxy_hostname = NULL; + } + + if (mqtt_transport_instance->http_proxy_username != NULL) + { + free(mqtt_transport_instance->http_proxy_username); + mqtt_transport_instance->http_proxy_username = NULL; + } + + if (mqtt_transport_instance->http_proxy_password != NULL) + { + free(mqtt_transport_instance->http_proxy_password); + mqtt_transport_instance->http_proxy_password = NULL; + } +} + +static void set_saved_tls_options(PMQTTTRANSPORT_HANDLE_DATA transport, OPTIONHANDLER_HANDLE new_options) +{ + if (transport->saved_tls_options != NULL) + { + OptionHandler_Destroy(transport->saved_tls_options); + } + transport->saved_tls_options = new_options; +} + +static void free_transport_handle_data(MQTTTRANSPORT_HANDLE_DATA* transport_data) +{ + if (transport_data->mqttClient != NULL) + { + mqtt_client_deinit(transport_data->mqttClient); + } + + if (transport_data->retry_control_handle != NULL) + { + retry_control_destroy(transport_data->retry_control_handle); + } + + set_saved_tls_options(transport_data, NULL); + + tickcounter_destroy(transport_data->msgTickCounter); + + free_proxy_data(transport_data); + + STRING_delete(transport_data->devicesAndModulesPath); + STRING_delete(transport_data->topic_MqttEvent); + STRING_delete(transport_data->topic_MqttMessage); + STRING_delete(transport_data->device_id); + STRING_delete(transport_data->module_id); + STRING_delete(transport_data->hostAddress); + STRING_delete(transport_data->configPassedThroughUsername); + STRING_delete(transport_data->topic_GetState); + STRING_delete(transport_data->topic_NotifyState); + STRING_delete(transport_data->topic_DeviceMethods); + STRING_delete(transport_data->topic_InputQueue); + + free(transport_data); +} + +int IoTHubTransport_MQTT_Common_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + int result; + + if (handle == NULL) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_041: [**If any handle is NULL then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return resultant line.] */ + LogError("Invalid handle parameter. NULL."); + result = __FAILURE__; + } + else + { + RETRY_CONTROL_HANDLE new_retry_control_handle; + + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_006: [ IoTHubTransport_MQTT_Common_SetRetryPolicy shall set the retry logic by calling retry_control_create() with retry policy and retryTimeout as parameters] + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_009: [ If retry_control_create() fails then IoTHubTransport_MQTT_Common_SetRetryPolicy shall revert to previous retry policy and return non-zero value ] + if ((new_retry_control_handle = retry_control_create(retryPolicy, (unsigned int)retryTimeoutLimitInSeconds)) == NULL) + { + LogError("Failed creating new retry control handle"); + result = __FAILURE__; + } + else + { + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + RETRY_CONTROL_HANDLE previous_retry_control_handle = transport_data->retry_control_handle; + + transport_data->retry_control_handle = new_retry_control_handle; + retry_control_destroy(previous_retry_control_handle); + + /*Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_045: [**If retry logic for specified parameters of retry policy and retryTimeoutLimitInSeconds is created successfully then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return 0]*/ + result = 0; + } + } + + return result; +} + +static uint16_t get_next_packet_id(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (transport_data->packetId + 1 >= USHRT_MAX) + { + transport_data->packetId = 1; + } + else + { + transport_data->packetId++; + } + return transport_data->packetId; +} + +static const char* retrieve_mqtt_return_codes(CONNECT_RETURN_CODE rtn_code) +{ + switch (rtn_code) + { + case CONNECTION_ACCEPTED: + return "Accepted"; + case CONN_REFUSED_UNACCEPTABLE_VERSION: + return "Unacceptable Version"; + case CONN_REFUSED_ID_REJECTED: + return "Id Rejected"; + case CONN_REFUSED_SERVER_UNAVAIL: + return "Server Unavailable"; + case CONN_REFUSED_BAD_USERNAME_PASSWORD: + return "Bad Username/Password"; + case CONN_REFUSED_NOT_AUTHORIZED: + return "Not Authorized"; + case CONN_REFUSED_UNKNOWN: + default: + return "Unknown"; + } +} + +static int retrieve_device_method_rid_info(const char* resp_topic, STRING_HANDLE method_name, STRING_HANDLE request_id) +{ + int result; + STRING_TOKENIZER_HANDLE token_handle = STRING_TOKENIZER_create_from_char(resp_topic); + if (token_handle == NULL) + { + LogError("Failed creating token from device twin topic."); + result = __FAILURE__; + } + else + { + STRING_HANDLE token_value; + if ((token_value = STRING_new()) == NULL) + { + LogError("Failed allocating new string ."); + result = __FAILURE__; + } + else + { + size_t token_index = 0; + size_t request_id_length = strlen(REQUEST_ID_PROPERTY); + result = __FAILURE__; + while (STRING_TOKENIZER_get_next_token(token_handle, token_value, "/") == 0) + { + if (token_index == 3) + { + if (STRING_concat_with_STRING(method_name, token_value) != 0) + { + LogError("Failed STRING_concat_with_STRING."); + result = __FAILURE__; + break; + } + } + else if (token_index == 4) + { + if (STRING_length(token_value) >= request_id_length) + { + const char* request_id_value = STRING_c_str(token_value); + if (memcmp(request_id_value, REQUEST_ID_PROPERTY, request_id_length) == 0) + { + if (STRING_concat(request_id, request_id_value + request_id_length) != 0) + { + LogError("Failed STRING_concat failed."); + result = __FAILURE__; + } + else + { + result = 0; + } + break; + } + } + } + token_index++; + } + STRING_delete(token_value); + } + STRING_TOKENIZER_destroy(token_handle); + } + return result; +} + +static int parse_device_twin_topic_info(const char* resp_topic, bool* patch_msg, size_t* request_id, int* status_code) +{ + int result; + STRING_TOKENIZER_HANDLE token_handle = STRING_TOKENIZER_create_from_char(resp_topic); + if (token_handle == NULL) + { + LogError("Failed creating token from device twin topic."); + result = __FAILURE__; + *status_code = 0; + *request_id = 0; + *patch_msg = false; + } + else + { + STRING_HANDLE token_value; + if ((token_value = STRING_new()) == NULL) + { + LogError("Failed allocating new string ."); + result = __FAILURE__; + *status_code = 0; + *request_id = 0; + *patch_msg = false; + } + else + { + result = __FAILURE__; + size_t token_count = 0; + while (STRING_TOKENIZER_get_next_token(token_handle, token_value, "/") == 0) + { + if (token_count == 2) + { + if (strcmp(STRING_c_str(token_value), "PATCH") == 0) + { + *patch_msg = true; + *status_code = 0; + *request_id = 0; + result = 0; + break; + } + else + { + *patch_msg = false; + } + } + else if (token_count == 3) + { + *status_code = (int)atol(STRING_c_str(token_value)); + if (STRING_TOKENIZER_get_next_token(token_handle, token_value, "/?$rid=") == 0) + { + *request_id = (size_t)atol(STRING_c_str(token_value)); + } + *patch_msg = false; + result = 0; + break; + } + token_count++; + } + STRING_delete(token_value); + } + STRING_TOKENIZER_destroy(token_handle); + } + return result; +} + +#define TOLOWER(c) (((c>='A') && (c<='Z'))?c-'A'+'a':c) +static int InternStrnicmp(const char* s1, const char* s2, size_t n) +{ + int result; + + if (s1 == NULL) result = -1; + else if (s2 == NULL) result = 1; + else + { + result = 0; + + while (n-- && result == 0) + { + if (*s1 == 0) result = -1; + else if (*s2 == 0) result = 1; + else + { + + result = TOLOWER(*s1) - TOLOWER(*s2); + ++s1; + ++s2; + } + } + } + + return result; +} + + +static IOTHUB_IDENTITY_TYPE retrieve_topic_type(const char* topic_resp, const char* input_queue) +{ + IOTHUB_IDENTITY_TYPE type; + if (InternStrnicmp(topic_resp, TOPIC_DEVICE_TWIN_PREFIX, sizeof(TOPIC_DEVICE_TWIN_PREFIX) - 1) == 0) + { + type = IOTHUB_TYPE_DEVICE_TWIN; + } + else if (InternStrnicmp(topic_resp, TOPIC_DEVICE_METHOD_PREFIX, sizeof(TOPIC_DEVICE_METHOD_PREFIX) - 1) == 0) + { + type = IOTHUB_TYPE_DEVICE_METHODS; + } + // input_queue contains additional "#" from subscribe, which we strip off on comparing incoming. + else if ((input_queue != NULL) && InternStrnicmp(topic_resp, input_queue, strlen(input_queue) - 1) == 0) + { + type = IOTHUB_TYPE_EVENT_QUEUE; + } + else + { + type = IOTHUB_TYPE_TELEMETRY; + } + return type; + +} + +static void sendMsgComplete(IOTHUB_MESSAGE_LIST* iothubMsgList, PMQTTTRANSPORT_HANDLE_DATA transport_data, IOTHUB_CLIENT_CONFIRMATION_RESULT confirmResult) +{ + DLIST_ENTRY messageCompleted; + DList_InitializeListHead(&messageCompleted); + DList_InsertTailList(&messageCompleted, &(iothubMsgList->entry)); + IoTHubClientCore_LL_SendComplete(transport_data->llClientHandle, &messageCompleted, confirmResult); +} + +static int addUserPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode) +{ + int result = 0; + const char* const* propertyKeys; + const char* const* propertyValues; + size_t propertyCount; + size_t index = *index_ptr; + MAP_HANDLE properties_map = IoTHubMessage_Properties(iothub_message_handle); + if (properties_map != NULL) + { + if (Map_GetInternals(properties_map, &propertyKeys, &propertyValues, &propertyCount) != MAP_OK) + { + LogError("Failed to get the internals of the property map."); + result = __FAILURE__; + } + else + { + if (propertyCount != 0) + { + for (index = 0; index < propertyCount && result == 0; index++) + { + if (urlencode) + { + STRING_HANDLE property_key = URL_EncodeString(propertyKeys[index]); + STRING_HANDLE property_value = URL_EncodeString(propertyValues[index]); + if ((property_key == NULL) || (property_value == NULL)) + { + LogError("Failed URL Encoding properties"); + result = __FAILURE__; + } + else if (STRING_sprintf(topic_string, "%s=%s%s", STRING_c_str(property_key), STRING_c_str(property_value), propertyCount - 1 == index ? "" : PROPERTY_SEPARATOR) != 0) + { + LogError("Failed constructing property string."); + result = __FAILURE__; + } + STRING_delete(property_key); + STRING_delete(property_value); + } + else + { + if (STRING_sprintf(topic_string, "%s=%s%s", propertyKeys[index], propertyValues[index], propertyCount - 1 == index ? "" : PROPERTY_SEPARATOR) != 0) + { + LogError("Failed constructing property string."); + result = __FAILURE__; + } + } + } + } + } + } + *index_ptr = index; + return result; +} + +static int addSystemPropertyToTopicString(STRING_HANDLE topic_string, size_t index, const char* property_key, const char* property_value, bool urlencode) +{ + int result = 0; + + if (urlencode) + { + STRING_HANDLE encoded_property_value = URL_EncodeString(property_value); + if (encoded_property_value == NULL) + { + LogError("Failed URL encoding %s.", property_key); + result = __FAILURE__; + } + else if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, property_key, STRING_c_str(encoded_property_value)) != 0) + { + LogError("Failed setting %s.", property_key); + result = __FAILURE__; + } + STRING_delete(encoded_property_value); + } + else + { + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, property_key, property_value) != 0) + { + LogError("Failed setting %s.", property_key); + result = __FAILURE__; + } + } + return result; +} + +static int addSystemPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode) +{ + (void)urlencode; + int result = 0; + size_t index = *index_ptr; + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_052: [ IoTHubTransport_MQTT_Common_DoWork shall check for the CorrelationId property and if found add the value as a system property in the format of $.cid=<id> ] */ + const char* correlation_id = IoTHubMessage_GetCorrelationId(iothub_message_handle); + if (correlation_id != NULL) + { + result = addSystemPropertyToTopicString(topic_string, index, CORRELATION_ID_PROPERTY, correlation_id, urlencode); + index++; + } + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_053: [ IoTHubTransport_MQTT_Common_DoWork shall check for the MessageId property and if found add the value as a system property in the format of $.mid=<id> ] */ + if (result == 0) + { + const char* msg_id = IoTHubMessage_GetMessageId(iothub_message_handle); + if (msg_id != NULL) + { + result = addSystemPropertyToTopicString(topic_string, index, MESSAGE_ID_PROPERTY, msg_id, urlencode); + index++; + } + } + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_010: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the ContentType property and if found add the `value` as a system property in the format of `$.ct=<value>` ] + if (result == 0) + { + const char* content_type = IoTHubMessage_GetContentTypeSystemProperty(iothub_message_handle); + if (content_type != NULL) + { + result = addSystemPropertyToTopicString(topic_string, index, CONTENT_TYPE_PROPERTY, content_type, urlencode); + index++; + } + } + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_011: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the ContentEncoding property and if found add the `value` as a system property in the format of `$.ce=<value>` ] + if (result == 0) + { + const char* content_encoding = IoTHubMessage_GetContentEncodingSystemProperty(iothub_message_handle); + if (content_encoding != NULL) + { + result = addSystemPropertyToTopicString(topic_string, index, CONTENT_ENCODING_PROPERTY, content_encoding, urlencode); + index++; + } + } + *index_ptr = index; + return result; +} + +static int addDiagnosticPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr) +{ + int result = 0; + size_t index = *index_ptr; + + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_014: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the diagnostic properties including diagid and diagCreationTimeUtc and if found both add them as system property in the format of `$.diagid` and `$.diagctx` respectively] + const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData = IoTHubMessage_GetDiagnosticPropertyData(iothub_message_handle); + if (diagnosticData != NULL) + { + const char* diag_id = diagnosticData->diagnosticId; + const char* creation_time_utc = diagnosticData->diagnosticCreationTimeUtc; + //diagid and creationtimeutc must be present/unpresent simultaneously + if (diag_id != NULL && creation_time_utc != NULL) + { + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_ID_PROPERTY, diag_id) != 0) + { + LogError("Failed setting diagnostic id"); + result = __FAILURE__; + } + index++; + + if (result == 0) + { + //construct diagnostic context, it should be urlencode(key1=value1,key2=value2) + STRING_HANDLE diagContextHandle = STRING_construct_sprintf("%s=%s", DIAGNOSTIC_CONTEXT_CREATION_TIME_UTC_PROPERTY, creation_time_utc); + if (diagContextHandle == NULL) + { + LogError("Failed constructing diagnostic context"); + result = __FAILURE__; + } + else + { + //Add other diagnostic context properties here if have more + STRING_HANDLE encodedContextValueHandle = URL_Encode(diagContextHandle); + const char* encodedContextValueString = NULL; + if (encodedContextValueHandle != NULL && + (encodedContextValueString = STRING_c_str(encodedContextValueHandle)) != NULL) + { + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_CONTEXT_PROPERTY, encodedContextValueString) != 0) + { + LogError("Failed setting diagnostic context"); + result = __FAILURE__; + } + STRING_delete(encodedContextValueHandle); + encodedContextValueHandle = NULL; + } + else + { + LogError("Failed encoding diagnostic context value"); + result = __FAILURE__; + } + STRING_delete(diagContextHandle); + diagContextHandle = NULL; + index++; + } + } + } + else if (diag_id != NULL || creation_time_utc != NULL) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_015: [ `IoTHubTransport_MQTT_Common_DoWork` shall check whether diagid and diagCreationTimeUtc be present simultaneously, treat as error if not] + LogError("diagid and diagcreationtimeutc must be present simultaneously."); + result = __FAILURE__; + } + } + return result; +} + + +static STRING_HANDLE addPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, const char* eventTopic, bool urlencode) +{ + size_t index = 0; + STRING_HANDLE result = STRING_construct(eventTopic); + if (result == NULL) + { + LogError("Failed to create event topic string handle"); + } + else if (addUserPropertiesTouMqttMessage(iothub_message_handle, result, &index, urlencode) != 0) + { + LogError("Failed adding Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } + else if (addSystemPropertiesTouMqttMessage(iothub_message_handle, result, &index, urlencode) != 0) + { + LogError("Failed adding System Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } + else if (addDiagnosticPropertiesTouMqttMessage(iothub_message_handle, result, &index) != 0) + { + LogError("Failed adding Diagnostic Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } + + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_060: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the OutputName property and if found add the alue as a system property in the format of $.on=<value> ] + if (result != NULL) + { + const char* output_name = IoTHubMessage_GetOutputName(iothub_message_handle); + if (output_name != NULL) + { + if (STRING_sprintf(result, "%s%%24.on=%s/", index == 0 ? "" : PROPERTY_SEPARATOR, output_name) != 0) + { + LogError("Failed setting output name."); + STRING_delete(result); + result = NULL; + } + index++; + } + } + + return result; +} + +static int publish_mqtt_telemetry_msg(PMQTTTRANSPORT_HANDLE_DATA transport_data, MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry, const unsigned char* payload, size_t len) +{ + int result; + STRING_HANDLE msgTopic = addPropertiesTouMqttMessage(mqttMsgEntry->iotHubMessageEntry->messageHandle, STRING_c_str(transport_data->topic_MqttEvent), transport_data->auto_url_encode_decode); + if (msgTopic == NULL) + { + LogError("Failed adding properties to mqtt message"); + result = __FAILURE__; + } + else + { + MQTT_MESSAGE_HANDLE mqttMsg = mqttmessage_create(mqttMsgEntry->packet_id, STRING_c_str(msgTopic), DELIVER_AT_LEAST_ONCE, payload, len); + if (mqttMsg == NULL) + { + LogError("Failed creating mqtt message"); + result = __FAILURE__; + } + else + { + if (tickcounter_get_current_ms(transport_data->msgTickCounter, &mqttMsgEntry->msgPublishTime) != 0) + { + LogError("Failed retrieving tickcounter info"); + result = __FAILURE__; + } + else + { + if (mqtt_client_publish(transport_data->mqttClient, mqttMsg) != 0) + { + LogError("Failed attempting to publish mqtt message"); + result = __FAILURE__; + } + else + { + mqttMsgEntry->retryCount++; + result = 0; + } + } + mqttmessage_destroy(mqttMsg); + } + STRING_delete(msgTopic); + } + return result; +} + +static int publish_device_method_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, int status_code, STRING_HANDLE request_id, const unsigned char* response, size_t response_size) +{ + int result; + uint16_t packet_id = get_next_packet_id(transport_data); + + STRING_HANDLE msg_topic = STRING_construct_sprintf(DEVICE_METHOD_RESPONSE_TOPIC, status_code, STRING_c_str(request_id)); + if (msg_topic == NULL) + { + LogError("Failed constructing message topic."); + result = __FAILURE__; + } + else + { + MQTT_MESSAGE_HANDLE mqtt_get_msg = mqttmessage_create(packet_id, STRING_c_str(msg_topic), DELIVER_AT_MOST_ONCE, response, response_size); + if (mqtt_get_msg == NULL) + { + LogError("Failed constructing mqtt message."); + result = __FAILURE__; + } + else + { + if (mqtt_client_publish(transport_data->mqttClient, mqtt_get_msg) != 0) + { + LogError("Failed publishing to mqtt client."); + result = __FAILURE__; + } + else + { + result = 0; + } + mqttmessage_destroy(mqtt_get_msg); + } + STRING_delete(msg_topic); + } + return result; +} + +static int publish_device_twin_get_message(MQTTTRANSPORT_HANDLE_DATA* transport_data) +{ + int result; + MQTT_DEVICE_TWIN_ITEM* mqtt_info = (MQTT_DEVICE_TWIN_ITEM*)malloc(sizeof(MQTT_DEVICE_TWIN_ITEM)); + if (mqtt_info == NULL) + { + LogError("Failed allocating device twin data."); + result = __FAILURE__; + } + else + { + mqtt_info->packet_id = get_next_packet_id(transport_data); + mqtt_info->iothub_msg_id = 0; + mqtt_info->device_twin_msg_type = RETRIEVE_PROPERTIES; + mqtt_info->retryCount = 0; + mqtt_info->msgPublishTime = 0; + mqtt_info->iothub_type = IOTHUB_TYPE_DEVICE_TWIN; + mqtt_info->device_twin_data = NULL; + STRING_HANDLE msg_topic = STRING_construct_sprintf(GET_PROPERTIES_TOPIC, mqtt_info->packet_id); + if (msg_topic == NULL) + { + LogError("Failed constructing get Prop topic."); + free(mqtt_info); + result = __FAILURE__; + } + else + { + MQTT_MESSAGE_HANDLE mqtt_get_msg = mqttmessage_create(mqtt_info->packet_id, STRING_c_str(msg_topic), DELIVER_AT_MOST_ONCE, NULL, 0); + if (mqtt_get_msg == NULL) + { + LogError("Failed constructing mqtt message."); + free(mqtt_info); + result = __FAILURE__; + } + else + { + if (mqtt_client_publish(transport_data->mqttClient, mqtt_get_msg) != 0) + { + LogError("Failed publishing to mqtt client."); + free(mqtt_info); + result = __FAILURE__; + } + else + { + DList_InsertTailList(&transport_data->ack_waiting_queue, &mqtt_info->entry); + result = 0; + } + mqttmessage_destroy(mqtt_get_msg); + } + STRING_delete(msg_topic); + } + } + return result; +} + +static int publish_device_twin_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, IOTHUB_DEVICE_TWIN* device_twin_info, MQTT_DEVICE_TWIN_ITEM* mqtt_info) +{ + int result; + mqtt_info->packet_id = get_next_packet_id(transport_data); + mqtt_info->device_twin_msg_type = REPORTED_STATE; + STRING_HANDLE msgTopic = STRING_construct_sprintf(REPORTED_PROPERTIES_TOPIC, mqtt_info->packet_id); + if (msgTopic == NULL) + { + LogError("Failed constructing reported prop topic."); + result = __FAILURE__; + } + else + { + const CONSTBUFFER* data_buff = CONSTBUFFER_GetContent(device_twin_info->report_data_handle); + MQTT_MESSAGE_HANDLE mqtt_rpt_msg = mqttmessage_create(mqtt_info->packet_id, STRING_c_str(msgTopic), DELIVER_AT_MOST_ONCE, data_buff->buffer, data_buff->size); + if (mqtt_rpt_msg == NULL) + { + LogError("Failed creating mqtt message"); + result = __FAILURE__; + } + else + { + if (tickcounter_get_current_ms(transport_data->msgTickCounter, &mqtt_info->msgPublishTime) != 0) + { + LogError("Failed retrieving tickcounter info"); + result = __FAILURE__; + } + else + { + if (mqtt_client_publish(transport_data->mqttClient, mqtt_rpt_msg) != 0) + { + LogError("Failed publishing mqtt message"); + result = __FAILURE__; + } + else + { + mqtt_info->retryCount++; + result = 0; + } + } + mqttmessage_destroy(mqtt_rpt_msg); + } + STRING_delete(msgTopic); + } + return result; +} + +static void changeStateToSubscribeIfAllowed(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (transport_data->currPacketState != CONNACK_TYPE && + transport_data->currPacketState != CONNECT_TYPE && + transport_data->currPacketState != DISCONNECT_TYPE && + transport_data->currPacketState != PACKET_TYPE_ERROR) + { + transport_data->currPacketState = SUBSCRIBE_TYPE; + } +} + +static int subscribeToNotifyStateIfNeeded(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + int result; + + if (transport_data->topic_NotifyState == NULL) + { + transport_data->topic_NotifyState = STRING_construct(TOPIC_NOTIFICATION_STATE); + if (transport_data->topic_NotifyState == NULL) + { + LogError("Failure: unable constructing notify state topic"); + result = __FAILURE__; + } + else + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_NOTIFICATION_STATE_TOPIC; + result = 0; + } + } + else + { + result = 0; + } + + if (result == 0) + { + changeStateToSubscribeIfAllowed(transport_data); + } + + return result; +} + + +static bool isSystemProperty(const char* tokenData) +{ + bool result = false; + size_t propCount = sizeof(sysPropList) / sizeof(sysPropList[0]); + size_t index = 0; + for (index = 0; index < propCount; index++) + { + if (memcmp(tokenData, sysPropList[index].propName, sysPropList[index].propLength) == 0) + { + result = true; + break; + } + } + return result; +} + +// Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_061: [ If the message is sent to an input queue, `IoTHubTransport_MQTT_Common_DoWork` shall parse out to the input queue name and store it in the message with IoTHubMessage_SetInputName ] +// Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_062: [ If IoTHubTransport_MQTT_Common_DoWork receives a malformatted inputQueue, it shall fail ] +static int addInputNamePropertyToMessage(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name) +{ + int result = __FAILURE__; + int number_tokens_read = 0; + + STRING_TOKENIZER_HANDLE token_handle = STRING_TOKENIZER_create_from_char(topic_name); + if (token_handle == NULL) + { + LogError("STRING_TOKENIZER_create_from_char failed\n"); + result = __FAILURE__; + } + else + { + STRING_HANDLE token_value; + if ((token_value = STRING_new()) == NULL) + { + LogError("Failed allocating token_value"); + } + else + { + while (STRING_TOKENIZER_get_next_token(token_handle, token_value, "/") == 0) + { + number_tokens_read++; + if (number_tokens_read == (slashes_to_reach_input_name + 1)) + { + if ((IOTHUB_MESSAGE_OK != IoTHubMessage_SetInputName(IoTHubMessage, STRING_c_str(token_value)))) + { + LogError("Failed adding input name to msg"); + result = __FAILURE__; + } + else + { + result = 0; + } + break; + } + } + } + STRING_delete(token_value); + + if (number_tokens_read != (slashes_to_reach_input_name + 1)) + { + LogError("Not enough '/' to contain input name. Got %d, need at least %d", number_tokens_read, (slashes_to_reach_input_name + 1)); + result = __FAILURE__; + } + STRING_TOKENIZER_destroy(token_handle); + } + + return result; +} + +static int setMqttMessagePropertyIfPossible(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* propName, const char* propValue, size_t nameLen) +{ + // Not finding a system property to map to isn't an error. + int result = 0; + + if (nameLen > 4) + { + if (strcmp((const char*)&propName[nameLen - 4], CONNECTION_DEVICE_ID) == 0) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_063: [ If type is IOTHUB_TYPE_TELEMETRY and the system property `$.cdid` is defined, its value shall be set on the IOTHUB_MESSAGE_HANDLE's ConnectionDeviceId property ] + if (IoTHubMessage_SetConnectionDeviceId(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'messageId' property."); + result = __FAILURE__; + } + return result; + } + if (strcmp((const char*)&propName[nameLen - 4], CONNECTION_MODULE_ID_PROPERTY) == 0) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_064: [ If type is IOTHUB_TYPE_TELEMETRY and the system property `$.cmid` is defined, its value shall be set on the IOTHUB_MESSAGE_HANDLE's ConnectionModuleId property ] + if (IoTHubMessage_SetConnectionModuleId(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'correlationId' property."); + result = __FAILURE__; + } + return result; + } + } + if (nameLen > 3) + { + if (strcmp((const char*)&propName[nameLen - 3], MESSAGE_ID_PROPERTY) == 0) + { + if (IoTHubMessage_SetMessageId(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'messageId' property."); + result = __FAILURE__; + } + return result; + } + else if (strcmp((const char*)&propName[nameLen - 3], CORRELATION_ID_PROPERTY) == 0) + { + if (IoTHubMessage_SetCorrelationId(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'correlationId' property."); + result = __FAILURE__; + } + return result; + } + } + + if (nameLen > 2) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_012: [ If type is IOTHUB_TYPE_TELEMETRY and the system property `$.ct` is defined, its value shall be set on the IOTHUB_MESSAGE_HANDLE's ContentType property ] + if (strcmp((const char*)&propName[nameLen - 2], CONTENT_TYPE_PROPERTY) == 0) + { + if (IoTHubMessage_SetContentTypeSystemProperty(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'customContentType' property."); + result = __FAILURE__; + } + return result; + } + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_013: [ If type is IOTHUB_TYPE_TELEMETRY and the system property `$.ce` is defined, its value shall be set on the IOTHUB_MESSAGE_HANDLE's ContentEncoding property ] + else if (strcmp((const char*)&propName[nameLen - 2], CONTENT_ENCODING_PROPERTY) == 0) + { + if (IoTHubMessage_SetContentEncodingSystemProperty(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK) + { + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'contentEncoding' property."); + result = __FAILURE__; + } + return result; + } + } + + return result; +} + +static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name, bool urldecode) +{ + int result; + STRING_HANDLE mqttTopic = STRING_construct(topic_name); + if (mqttTopic == NULL) + { + LogError("Failure constructing string topic name."); + result = __FAILURE__; + } + else + { + STRING_TOKENIZER_HANDLE token = STRING_TOKENIZER_create(mqttTopic); + if (token != NULL) + { + MAP_HANDLE propertyMap = IoTHubMessage_Properties(IoTHubMessage); + if (propertyMap == NULL) + { + LogError("Failure to retrieve IoTHubMessage_properties."); + result = __FAILURE__; + } + else + { + STRING_HANDLE output = STRING_new(); + if (output == NULL) + { + LogError("Failure to allocate STRING_new."); + result = __FAILURE__; + } + else + { + result = 0; + + while (STRING_TOKENIZER_get_next_token(token, output, PROPERTY_SEPARATOR) == 0 && result == 0) + { + const char* tokenData = STRING_c_str(output); + size_t tokenLen = strlen(tokenData); + if (tokenData == NULL || tokenLen == 0) + { + break; + } + else + { + if (isSystemProperty(tokenData)) + { + const char* iterator = tokenData; + while (iterator != NULL && *iterator != '\0' && result == 0) + { + if (*iterator == '=') + { + size_t nameLen = iterator - tokenData; + char* propName = malloc(nameLen + 1); + + size_t valLen = tokenLen - (nameLen + 1) + 1; + char* propValue = malloc(valLen + 1); + + if (propName == NULL || propValue == NULL) + { + LogError("Failed allocating property name (%p) and/or value (%p)", propName, propValue); + result = __FAILURE__; + } + else + { + strncpy(propName, tokenData, nameLen); + propName[nameLen] = '\0'; + + strncpy(propValue, iterator + 1, valLen); + propValue[valLen] = '\0'; + + if (urldecode) + { + STRING_HANDLE propValue_decoded; + if ((propValue_decoded = URL_DecodeString(propValue)) == NULL) + { + LogError("Failed to URL decode property value"); + result = __FAILURE__; + } + else if (setMqttMessagePropertyIfPossible(IoTHubMessage, propName, STRING_c_str(propValue_decoded), nameLen) != 0) + { + LogError("Unable to set message property"); + result = __FAILURE__; + } + STRING_delete(propValue_decoded); + } + else + { + if (setMqttMessagePropertyIfPossible(IoTHubMessage, propName, propValue, nameLen) != 0) + { + LogError("Unable to set message property"); + result = __FAILURE__; + } + } + } + free(propName); + free(propValue); + + break; + } + iterator++; + } + } + else //User Properties + { + const char* iterator = tokenData; + while (iterator != NULL && *iterator != '\0' && result == 0) + { + if (*iterator == '=') + { + size_t nameLen = iterator - tokenData; + char* propName = malloc(nameLen + 1); + + size_t valLen = tokenLen - (nameLen + 1) + 1; + char* propValue = malloc(valLen + 1); + + if (propName == NULL || propValue == NULL) + { + result = __FAILURE__; + } + else + { + strncpy(propName, tokenData, nameLen); + propName[nameLen] = '\0'; + + strncpy(propValue, iterator + 1, valLen); + propValue[valLen] = '\0'; + + if (urldecode) + { + STRING_HANDLE propName_decoded = URL_DecodeString(propName); + STRING_HANDLE propValue_decoded = URL_DecodeString(propValue); + if (propName_decoded == NULL || propValue_decoded == NULL) + { + LogError("Failed to URL decode property"); + result = __FAILURE__; + } + else if (Map_AddOrUpdate(propertyMap, STRING_c_str(propName_decoded), STRING_c_str(propValue_decoded)) != MAP_OK) + { + LogError("Map_AddOrUpdate failed."); + result = __FAILURE__; + } + STRING_delete(propName_decoded); + STRING_delete(propValue_decoded); + } + else + { + if (Map_AddOrUpdate(propertyMap, propName, propValue) != MAP_OK) + { + LogError("Map_AddOrUpdate failed."); + result = __FAILURE__; + } + } + } + free(propName); + free(propValue); + + break; + } + iterator++; + } + } + } + } + STRING_delete(output); + } + } + STRING_TOKENIZER_destroy(token); + } + else + { + LogError("Unable to create Tokenizer object."); + result = __FAILURE__; + } + STRING_delete(mqttTopic); + } + return result; +} + +static void mqtt_notification_callback(MQTT_MESSAGE_HANDLE msgHandle, void* callbackCtx) +{ + /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_051: [ If msgHandle or callbackCtx is NULL, mqtt_notification_callback shall do nothing. ] */ + if (msgHandle != NULL && callbackCtx != NULL) + { + /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_052: [ mqtt_notification_callback shall extract the topic Name from the MQTT_MESSAGE_HANDLE. ] */ + const char* topic_resp = mqttmessage_getTopicName(msgHandle); + if (topic_resp == NULL) + { + LogError("Failure: NULL topic name encountered"); + } + else + { + PMQTTTRANSPORT_HANDLE_DATA transportData = (PMQTTTRANSPORT_HANDLE_DATA)callbackCtx; + + IOTHUB_IDENTITY_TYPE type = retrieve_topic_type(topic_resp, STRING_c_str(transportData->topic_InputQueue)); + if (type == IOTHUB_TYPE_DEVICE_TWIN) + { + size_t request_id; + int status_code; + bool notification_msg; + if (parse_device_twin_topic_info(topic_resp, ¬ification_msg, &request_id, &status_code) != 0) + { + LogError("Failure: parsing device topic info"); + } + else + { + const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle); + if (notification_msg) + { + IoTHubClientCore_LL_RetrievePropertyComplete(transportData->llClientHandle, DEVICE_TWIN_UPDATE_PARTIAL, payload->message, payload->length); + } + else + { + PDLIST_ENTRY dev_twin_item = transportData->ack_waiting_queue.Flink; + while (dev_twin_item != &transportData->ack_waiting_queue) + { + DLIST_ENTRY saveListEntry; + saveListEntry.Flink = dev_twin_item->Flink; + MQTT_DEVICE_TWIN_ITEM* msg_entry = containingRecord(dev_twin_item, MQTT_DEVICE_TWIN_ITEM, entry); + if (request_id == msg_entry->packet_id) + { + (void)DList_RemoveEntryList(dev_twin_item); + if (msg_entry->device_twin_msg_type == RETRIEVE_PROPERTIES) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_054: [ If type is IOTHUB_TYPE_DEVICE_TWIN, then on success if msg_type is RETRIEVE_PROPERTIES then mqtt_notification_callback shall call IoTHubClientCore_LL_RetrievePropertyComplete... ] */ + IoTHubClientCore_LL_RetrievePropertyComplete(transportData->llClientHandle, DEVICE_TWIN_UPDATE_COMPLETE, payload->message, payload->length); + // Only after receiving device twin request should we start listening for patches. + (void)subscribeToNotifyStateIfNeeded(transportData); + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ if device_twin_msg_type is not RETRIEVE_PROPERTIES then mqtt_notification_callback shall call IoTHubClientCore_LL_ReportedStateComplete ] */ + IoTHubClientCore_LL_ReportedStateComplete(transportData->llClientHandle, msg_entry->iothub_msg_id, status_code); + } + free(msg_entry); + break; + } + dev_twin_item = saveListEntry.Flink; + } + } + } + } + else if (type == IOTHUB_TYPE_DEVICE_METHODS) + { + STRING_HANDLE method_name = STRING_new(); + if (method_name == NULL) + { + LogError("Failure: allocating method_name string value"); + } + else + { + DEVICE_METHOD_INFO* dev_method_info = malloc(sizeof(DEVICE_METHOD_INFO)); + if (dev_method_info == NULL) + { + LogError("Failure: allocating DEVICE_METHOD_INFO object"); + } + else + { + dev_method_info->request_id = STRING_new(); + if (dev_method_info->request_id == NULL) + { + LogError("Failure constructing request_id string"); + free(dev_method_info); + } + else if (retrieve_device_method_rid_info(topic_resp, method_name, dev_method_info->request_id) != 0) + { + LogError("Failure: retrieve device topic info"); + STRING_delete(dev_method_info->request_id); + free(dev_method_info); + } + else + { + /* CodesSRS_IOTHUB_MQTT_TRANSPORT_07_053: [ If type is IOTHUB_TYPE_DEVICE_METHODS, then on success mqtt_notification_callback shall call IoTHubClientCore_LL_DeviceMethodComplete. ] */ + const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle); + if (IoTHubClientCore_LL_DeviceMethodComplete(transportData->llClientHandle, STRING_c_str(method_name), payload->message, payload->length, (void*)dev_method_info) != 0) + { + LogError("Failure: IoTHubClientCore_LL_DeviceMethodComplete"); + STRING_delete(dev_method_info->request_id); + free(dev_method_info); + } + } + } + STRING_delete(method_name); + } + } + else + { + const APP_PAYLOAD* appPayload = mqttmessage_getApplicationMsg(msgHandle); + IOTHUB_MESSAGE_HANDLE IoTHubMessage = IoTHubMessage_CreateFromByteArray(appPayload->message, appPayload->length); + if (IoTHubMessage == NULL) + { + LogError("Failure: IotHub Message creation has failed."); + } + else + { + if ((type == IOTHUB_TYPE_EVENT_QUEUE) && (addInputNamePropertyToMessage(IoTHubMessage, topic_resp) != 0)) + { + LogError("failure adding input name to property."); + } + // Will need to update this when the service has messages that can be rejected + else if (extractMqttProperties(IoTHubMessage, topic_resp, transportData->auto_url_encode_decode) != 0) + { + LogError("failure extracting mqtt properties."); + } + else + { + MESSAGE_CALLBACK_INFO* messageData = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO)); + if (messageData == NULL) + { + LogError("malloc failed"); + } + else + { + messageData->messageHandle = IoTHubMessage; + messageData->transportContext = NULL; + + if (type == IOTHUB_TYPE_EVENT_QUEUE) + { + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_31_065: [ If type is IOTHUB_TYPE_TELEMETRY and sent to an input queue, then on success `mqtt_notification_callback` shall call `IoTHubClient_LL_MessageCallback`. ] + if (!IoTHubClientCore_LL_MessageCallbackFromInput(transportData->llClientHandle, messageData)) + { + LogError("IoTHubClientCore_LL_MessageCallbackreturned false"); + + IoTHubMessage_Destroy(IoTHubMessage); + free(messageData); + } + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_056: [ If type is IOTHUB_TYPE_TELEMETRY, then on success mqtt_notification_callback shall call IoTHubClientCore_LL_MessageCallback. ] */ + if (!IoTHubClientCore_LL_MessageCallback(transportData->llClientHandle, messageData)) + { + LogError("IoTHubClientCore_LL_MessageCallback returned false"); + IoTHubMessage_Destroy(IoTHubMessage); + free(messageData); + } + } + } + } + } + } + } + } +} + +static void mqtt_operation_complete_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_RESULT actionResult, const void* msgInfo, void* callbackCtx) +{ + (void)handle; + if (callbackCtx != NULL) + { + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)callbackCtx; + + switch (actionResult) + { + case MQTT_CLIENT_ON_PUBLISH_ACK: + case MQTT_CLIENT_ON_PUBLISH_COMP: + { + const PUBLISH_ACK* puback = (const PUBLISH_ACK*)msgInfo; + if (puback != NULL) + { + PDLIST_ENTRY currentListEntry = transport_data->telemetry_waitingForAck.Flink; + while (currentListEntry != &transport_data->telemetry_waitingForAck) + { + MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = containingRecord(currentListEntry, MQTT_MESSAGE_DETAILS_LIST, entry); + DLIST_ENTRY saveListEntry; + saveListEntry.Flink = currentListEntry->Flink; + + if (puback->packetId == mqttMsgEntry->packet_id) + { + (void)DList_RemoveEntryList(currentListEntry); //First remove the item from Waiting for Ack List. + sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_OK); + free(mqttMsgEntry); + } + currentListEntry = saveListEntry.Flink; + } + } + else + { + LogError("Failure: MQTT_CLIENT_ON_PUBLISH_ACK publish_ack structure NULL."); + } + break; + } + case MQTT_CLIENT_ON_CONNACK: + { + const CONNECT_ACK* connack = (const CONNECT_ACK*)msgInfo; + if (connack != NULL) + { + if (connack->returnCode == CONNECTION_ACCEPTED) + { + // The connect packet has been acked + transport_data->currPacketState = CONNACK_TYPE; + transport_data->isRecoverableError = true; + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_CONNECTED; + + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_008: [ Upon successful connection the retry control shall be reset using retry_control_reset() ] + retry_control_reset(transport_data->retry_control_handle); + + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_AUTHENTICATED, IOTHUB_CLIENT_CONNECTION_OK); + } + else + { + if (connack->returnCode == CONN_REFUSED_SERVER_UNAVAIL) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED); + } + else if (connack->returnCode == CONN_REFUSED_BAD_USERNAME_PASSWORD || connack->returnCode == CONN_REFUSED_ID_REJECTED) + { + transport_data->isRecoverableError = false; + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL); + } + else if (connack->returnCode == CONN_REFUSED_NOT_AUTHORIZED) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED); + } + else if (connack->returnCode == CONN_REFUSED_UNACCEPTABLE_VERSION) + { + transport_data->isRecoverableError = false; + } + LogError("Connection Not Accepted: 0x%x: %s", connack->returnCode, retrieve_mqtt_return_codes(connack->returnCode)); + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_PENDING_CLOSE; + transport_data->currPacketState = PACKET_TYPE_ERROR; + } + } + else + { + LogError("MQTT_CLIENT_ON_CONNACK CONNACK parameter is NULL."); + } + break; + } + case MQTT_CLIENT_ON_SUBSCRIBE_ACK: + { + const SUBSCRIBE_ACK* suback = (const SUBSCRIBE_ACK*)msgInfo; + if (suback != NULL) + { + size_t index = 0; + for (index = 0; index < suback->qosCount; index++) + { + if (suback->qosReturn[index] == DELIVER_FAILURE) + { + LogError("Subscribe delivery failure of subscribe %lu", index); + } + } + // The connect packet has been acked + transport_data->currPacketState = SUBACK_TYPE; + } + else + { + LogError("Failure: MQTT_CLIENT_ON_SUBSCRIBE_ACK SUBSCRIBE_ACK parameter is NULL."); + } + break; + } + case MQTT_CLIENT_ON_PUBLISH_RECV: + case MQTT_CLIENT_ON_PUBLISH_REL: + { + // Currently not used + break; + } + case MQTT_CLIENT_ON_DISCONNECT: + { + // Close the client so we can reconnect again + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + transport_data->currPacketState = DISCONNECT_TYPE; + break; + } + case MQTT_CLIENT_ON_UNSUBSCRIBE_ACK: + case MQTT_CLIENT_ON_PING_RESPONSE: + default: + { + break; + } + } + } +} + +// Prior to creating a new connection, if we have an existing xioTransport that has been connected before +// we need to clear it now or else cached settings (especially TLS when communicating with HTTP proxies) +// will break reconnection attempt. +static void ResetConnectionIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (transport_data->xioTransport != NULL && transport_data->conn_attempted) + { + OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); + set_saved_tls_options(transport_data, options); + + xio_destroy(transport_data->xioTransport); + transport_data->xioTransport = NULL; + } +} + +static void DisconnectFromClient(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (!transport_data->isDestroyCalled) + { + OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); + set_saved_tls_options(transport_data, options); + } + + (void)mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); + xio_destroy(transport_data->xioTransport); + transport_data->xioTransport = NULL; + + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + transport_data->currPacketState = DISCONNECT_TYPE; +} + +static void mqtt_error_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_ERROR error, void* callbackCtx) +{ + (void)handle; + if (callbackCtx != NULL) + { + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)callbackCtx; + switch (error) + { + case MQTT_CLIENT_CONNECTION_ERROR: + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_NO_NETWORK); + break; + } + case MQTT_CLIENT_COMMUNICATION_ERROR: + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR); + break; + } + case MQTT_CLIENT_NO_PING_RESPONSE: + { + LogError("Mqtt Ping Response was not encountered. Reconnecting device..."); + DisconnectFromClient(transport_data); + break; + } + case MQTT_CLIENT_PARSE_ERROR: + case MQTT_CLIENT_MEMORY_ERROR: + case MQTT_CLIENT_UNKNOWN_ERROR: + { + LogError("INTERNAL ERROR: unexpected error value received %s", ENUM_TO_STRING(MQTT_CLIENT_EVENT_ERROR, error)); + break; + } + } + if (transport_data->mqttClientStatus != MQTT_CLIENT_STATUS_PENDING_CLOSE) + { + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + } + transport_data->currPacketState = PACKET_TYPE_ERROR; + transport_data->device_twin_get_sent = false; + if (transport_data->topic_MqttMessage != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_TELEMETRY_TOPIC; + } + if (transport_data->topic_GetState != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_GET_REPORTED_STATE_TOPIC; + } + if (transport_data->topic_NotifyState != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_NOTIFICATION_STATE_TOPIC; + } + if (transport_data->topic_DeviceMethods != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_DEVICE_METHOD_TOPIC; + } + if (transport_data->topic_InputQueue != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_INPUT_QUEUE_TOPIC; + } + } + else + { + LogError("Failure: mqtt called back with null context."); + } +} + +static void SubscribeToMqttProtocol(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (transport_data->topics_ToSubscribe != UNSUBSCRIBE_FROM_TOPIC) + { + uint32_t topic_subscription = 0; + size_t subscribe_count = 0; + SUBSCRIBE_PAYLOAD subscribe[SUBSCRIBE_TOPIC_COUNT]; + if ((transport_data->topic_MqttMessage != NULL) && (SUBSCRIBE_TELEMETRY_TOPIC & transport_data->topics_ToSubscribe)) + { + subscribe[subscribe_count].subscribeTopic = STRING_c_str(transport_data->topic_MqttMessage); + subscribe[subscribe_count].qosReturn = DELIVER_AT_LEAST_ONCE; + topic_subscription |= SUBSCRIBE_TELEMETRY_TOPIC; + subscribe_count++; + } + if ((transport_data->topic_GetState != NULL) && (SUBSCRIBE_GET_REPORTED_STATE_TOPIC & transport_data->topics_ToSubscribe)) + { + subscribe[subscribe_count].subscribeTopic = STRING_c_str(transport_data->topic_GetState); + subscribe[subscribe_count].qosReturn = DELIVER_AT_MOST_ONCE; + topic_subscription |= SUBSCRIBE_GET_REPORTED_STATE_TOPIC; + subscribe_count++; + } + if ((transport_data->topic_NotifyState != NULL) && (SUBSCRIBE_NOTIFICATION_STATE_TOPIC & transport_data->topics_ToSubscribe)) + { + subscribe[subscribe_count].subscribeTopic = STRING_c_str(transport_data->topic_NotifyState); + subscribe[subscribe_count].qosReturn = DELIVER_AT_MOST_ONCE; + topic_subscription |= SUBSCRIBE_NOTIFICATION_STATE_TOPIC; + subscribe_count++; + } + if ((transport_data->topic_DeviceMethods != NULL) && (SUBSCRIBE_DEVICE_METHOD_TOPIC & transport_data->topics_ToSubscribe)) + { + subscribe[subscribe_count].subscribeTopic = STRING_c_str(transport_data->topic_DeviceMethods); + subscribe[subscribe_count].qosReturn = DELIVER_AT_MOST_ONCE; + topic_subscription |= SUBSCRIBE_DEVICE_METHOD_TOPIC; + subscribe_count++; + } + if ((transport_data->topic_InputQueue != NULL) && (SUBSCRIBE_INPUT_QUEUE_TOPIC & transport_data->topics_ToSubscribe)) + { + subscribe[subscribe_count].subscribeTopic = STRING_c_str(transport_data->topic_InputQueue); + subscribe[subscribe_count].qosReturn = DELIVER_AT_MOST_ONCE; + topic_subscription |= SUBSCRIBE_INPUT_QUEUE_TOPIC; + subscribe_count++; + } + + if (subscribe_count != 0) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_016: [IoTHubTransport_MQTT_Common_Subscribe shall call mqtt_client_subscribe to subscribe to the Message Topic.] */ + if (mqtt_client_subscribe(transport_data->mqttClient, get_next_packet_id(transport_data), subscribe, subscribe_count) != 0) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_017: [Upon failure IoTHubTransport_MQTT_Common_Subscribe shall return a non-zero value.] */ + LogError("Failure: mqtt_client_subscribe returned error."); + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_018: [On success IoTHubTransport_MQTT_Common_Subscribe shall return 0.] */ + transport_data->topics_ToSubscribe &= ~topic_subscription; + transport_data->currPacketState = SUBSCRIBE_TYPE; + } + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_017: [Upon failure IoTHubTransport_MQTT_Common_Subscribe shall return a non-zero value.] */ + LogError("Failure: subscribe_topic is empty."); + } + transport_data->currPacketState = SUBSCRIBE_TYPE; + } + else + { + transport_data->currPacketState = PUBLISH_TYPE; + } +} + +static const unsigned char* RetrieveMessagePayload(IOTHUB_MESSAGE_HANDLE messageHandle, size_t* length) +{ + const unsigned char* result; + + IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(messageHandle); + if (contentType == IOTHUBMESSAGE_BYTEARRAY) + { + if (IoTHubMessage_GetByteArray(messageHandle, &result, length) != IOTHUB_MESSAGE_OK) + { + LogError("Failure result from IoTHubMessage_GetByteArray"); + result = NULL; + *length = 0; + } + } + else if (contentType == IOTHUBMESSAGE_STRING) + { + result = (const unsigned char*)IoTHubMessage_GetString(messageHandle); + if (result == NULL) + { + LogError("Failure result from IoTHubMessage_GetString"); + result = NULL; + *length = 0; + } + else + { + *length = strlen((const char*)result); + } + } + else + { + result = NULL; + *length = 0; + } + return result; +} + +static int GetTransportProviderIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + int result; + + if (transport_data->xioTransport == NULL) + { + // construct address + const char* hostAddress = STRING_c_str(transport_data->hostAddress); + MQTT_TRANSPORT_PROXY_OPTIONS mqtt_proxy_options; + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_011: [ If no `proxy_data` option has been set, NULL shall be passed as the argument `mqtt_transport_proxy_options` when calling the function `get_io_transport` passed in `IoTHubTransport_MQTT_Common__Create`. ]*/ + mqtt_proxy_options.host_address = transport_data->http_proxy_hostname; + mqtt_proxy_options.port = transport_data->http_proxy_port; + mqtt_proxy_options.username = transport_data->http_proxy_username; + mqtt_proxy_options.password = transport_data->http_proxy_password; + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_010: [ If the `proxy_data` option has been set, the proxy options shall be filled in the argument `mqtt_transport_proxy_options` when calling the function `get_io_transport` passed in `IoTHubTransport_MQTT_Common__Create` to obtain the underlying IO handle. ]*/ + transport_data->xioTransport = transport_data->get_io_transport(hostAddress, (transport_data->http_proxy_hostname == NULL) ? NULL : &mqtt_proxy_options); + if (transport_data->xioTransport == NULL) + { + LogError("Unable to create the lower level TLS layer."); + result = __FAILURE__; + } + else + { + transport_data->conn_attempted = true; + if (transport_data->saved_tls_options != NULL) + { + if (OptionHandler_FeedOptions(transport_data->saved_tls_options, transport_data->xioTransport) != OPTIONHANDLER_OK) + { + LogError("Failed feeding existing options to new TLS instance."); + result = __FAILURE__; + } + else + { + // The tlsio has the options, so our copy can be deleted + set_saved_tls_options(transport_data, NULL); + result = 0; + } + } + else if (IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module) == IOTHUB_CREDENTIAL_TYPE_X509_ECC) + { + if (IoTHubClient_Auth_Set_xio_Certificate(transport_data->authorization_module, transport_data->xioTransport) != 0) + { + LogError("Unable to create the lower level TLS layer."); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + } + } + else + { + result = 0; + } + return result; +} + +//static int is_key_validate(const IOTHUBTRANSPORT_CONFIG* config) +//{ +// int result; +// IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(config->auth_module_handle); +// if (cred_type == IOTHUB_CREDENTIAL_TYPE_X509 || cred_type == IOTHUB_CREDENTIAL_TYPE_X509_ECC) +// { +// result = 0; +// } +// else +// { +// if (config->upperConfig->deviceKey == NULL && config->upperConfig->deviceSasToken == NULL) +// { +// if (IoTHubClient_Auth_Get_DeviceKey(config->auth_module_handle) == NULL) +// { +// result = __FAILURE__; +// } +// else +// { +// result = 0; +// } +// } +// else +// { +// result = 0; +// } +// } +// return result; +//} + +static STRING_HANDLE buildClientId(const char* device_id, const char* module_id) +{ + if (module_id == NULL) + { + return STRING_construct_sprintf("%s", device_id); + } + else + { + return STRING_construct_sprintf("%s/%s", device_id, module_id); + } +} + +static int SendMqttConnectMsg(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + int result; + + char* sasToken = NULL; + result = 0; + + IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module); + if (cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY || cred_type == IOTHUB_CREDENTIAL_TYPE_DEVICE_AUTH) + { + sasToken = IoTHubClient_Auth_Get_SasToken(transport_data->authorization_module, STRING_c_str(transport_data->devicesAndModulesPath), transport_data->option_sas_token_lifetime_secs, NULL); + if (sasToken == NULL) + { + LogError("failure getting sas token from IoTHubClient_Auth_Get_SasToken."); + result = __FAILURE__; + } + } + else if (cred_type == IOTHUB_CREDENTIAL_TYPE_SAS_TOKEN) + { + SAS_TOKEN_STATUS token_status = IoTHubClient_Auth_Is_SasToken_Valid(transport_data->authorization_module); + if (token_status == SAS_TOKEN_STATUS_INVALID) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN); + result = __FAILURE__; + } + else if (token_status == SAS_TOKEN_STATUS_FAILED) + { + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL); + result = __FAILURE__; + } + else + { + sasToken = IoTHubClient_Auth_Get_SasToken(transport_data->authorization_module, NULL, 0, NULL); + if (sasToken == NULL) + { + LogError("failure getting sas Token."); + result = __FAILURE__; + } + } + } + + if (result == 0) + { + if (!transport_data->isProductInfoSet) + { + // This requires the iothubClientHandle, which sadly the MQTT transport only gets on DoWork, so this code still needs to remain here. + // The correct place for this would be in the Create method, but we don't get the client handle there. + // Also, when device multiplexing is used, the customer creates the transport directly and explicitly, when the client is still not created. + // This will be a major hurdle when we add device multiplexing to MQTT transport. + + void* product_info; + STRING_HANDLE clone; + if ((IoTHubClientCore_LL_GetOption(transport_data->llClientHandle, OPTION_PRODUCT_INFO, &product_info) == IOTHUB_CLIENT_ERROR) || (product_info == NULL)) + { + clone = STRING_construct_sprintf("%s%%2F%s", CLIENT_DEVICE_TYPE_PREFIX, IOTHUB_SDK_VERSION); + } + else + { + clone = URL_Encode(product_info); + } + + if (clone == NULL) + { + LogError("Failed obtaining the product info"); + } + else + { + if (STRING_concat_with_STRING(transport_data->configPassedThroughUsername, clone) != 0) + { + LogError("Failed concatenating the product info"); + } + else + { + transport_data->isProductInfoSet = true; + } + + STRING_delete(clone); + } + } + + STRING_HANDLE clientId; + + clientId = buildClientId(STRING_c_str(transport_data->device_id), STRING_c_str(transport_data->module_id)); + if (NULL == clientId) + { + LogError("Unable to allocate clientId"); + result = __FAILURE__; + } + else + { + MQTT_CLIENT_OPTIONS options = { 0 }; + options.clientId = (char*)STRING_c_str(clientId); + options.willMessage = NULL; + options.username = (char*)STRING_c_str(transport_data->configPassedThroughUsername); + if (sasToken != NULL) + { + options.password = sasToken; + } + options.keepAliveInterval = transport_data->keepAliveValue; + options.useCleanSession = false; + options.qualityOfServiceValue = DELIVER_AT_LEAST_ONCE; + + if (GetTransportProviderIfNecessary(transport_data) == 0) + { + if (mqtt_client_connect(transport_data->mqttClient, transport_data->xioTransport, &options) != 0) + { + LogError("failure connecting to address %s.", STRING_c_str(transport_data->hostAddress)); + result = __FAILURE__; + } + else + { + (void)tickcounter_get_current_ms(transport_data->msgTickCounter, &transport_data->mqtt_connect_time); + result = 0; + } + } + else + { + result = __FAILURE__; + } + + if (sasToken != NULL) + { + free(sasToken); + } + STRING_delete(clientId); + } + + } + return result; +} + +static int InitializeConnection(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + int result = 0; + + // Make sure we're not destroying the object + if (!transport_data->isDestroyCalled) + { + RETRY_ACTION retry_action = RETRY_ACTION_RETRY_LATER; + + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_007: [ IoTHubTransport_MQTT_Common_DoWork shall try to reconnect according to the current retry policy set ] + if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_NOT_CONNECTED && transport_data->isRecoverableError && + (retry_control_should_retry(transport_data->retry_control_handle, &retry_action) != 0 || retry_action == RETRY_ACTION_RETRY_NOW)) + { + // Note: in case retry_control_should_retry fails, the reconnection shall be attempted anyway (defaulting to policy IOTHUB_CLIENT_RETRY_IMMEDIATE). + + if (tickcounter_get_current_ms(transport_data->msgTickCounter, &transport_data->connectTick) != 0) + { + transport_data->connectFailCount++; + result = __FAILURE__; + } + else + { + ResetConnectionIfNecessary(transport_data); + + if (SendMqttConnectMsg(transport_data) != 0) + { + transport_data->connectFailCount++; + result = __FAILURE__; + } + else + { + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_CONNECTING; + transport_data->connectFailCount = 0; + result = 0; + } + } + } + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_001: [ IoTHubTransport_MQTT_Common_DoWork shall trigger reconnection if the mqtt_client_connect does not complete within `keepalive` seconds] + else if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_CONNECTING) + { + tickcounter_ms_t current_time; + if (tickcounter_get_current_ms(transport_data->msgTickCounter, ¤t_time) != 0) + { + LogError("failed verifying MQTT_CLIENT_STATUS_CONNECTING timeout"); + result = __FAILURE__; + } + else if ((current_time - transport_data->mqtt_connect_time) / 1000 > transport_data->connect_timeout_in_sec) + { + LogError("mqtt_client timed out waiting for CONNACK"); + DisconnectFromClient(transport_data); + result = __FAILURE__; + } + } + else if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_CONNECTED) + { + // We are connected and not being closed, so does SAS need to reconnect? + tickcounter_ms_t current_time; + if (tickcounter_get_current_ms(transport_data->msgTickCounter, ¤t_time) != 0) + { + transport_data->connectFailCount++; + result = __FAILURE__; + } + else + { + if ((current_time - transport_data->mqtt_connect_time) / 1000 > (transport_data->option_sas_token_lifetime_secs*SAS_REFRESH_MULTIPLIER)) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_058: [ If the sas token has timed out IoTHubTransport_MQTT_Common_DoWork shall disconnect from the mqtt client and destroy the transport information and wait for reconnect. ] */ + OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); + set_saved_tls_options(transport_data, options); + (void)mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); + xio_destroy(transport_data->xioTransport); + transport_data->xioTransport = NULL; + + IoTHubClientCore_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN); + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + transport_data->currPacketState = UNKNOWN_TYPE; + transport_data->device_twin_get_sent = false; + if (transport_data->topic_MqttMessage != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_TELEMETRY_TOPIC; + } + if (transport_data->topic_GetState != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_GET_REPORTED_STATE_TOPIC; + } + if (transport_data->topic_NotifyState != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_NOTIFICATION_STATE_TOPIC; + } + if (transport_data->topic_DeviceMethods != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_DEVICE_METHOD_TOPIC; + } + if (transport_data->topic_InputQueue != NULL) + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_INPUT_QUEUE_TOPIC; + } + } + } + } + } + return result; +} + +static STRING_HANDLE buildConfigForUsername(const IOTHUB_CLIENT_CONFIG* upperConfig, const char* moduleId) +{ + if (moduleId == NULL) + { + return STRING_construct_sprintf("%s.%s/%s/?api-version=%s&DeviceClientType=", upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId, IOTHUB_API_VERSION); + } + else + { + return STRING_construct_sprintf("%s.%s/%s/%s/?api-version=%s&DeviceClientType=", upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId, moduleId, IOTHUB_API_VERSION); + } +} + +static STRING_HANDLE buildMqttEventString(const char* device_id, const char* module_id) +{ + if (module_id == NULL) + { + return STRING_construct_sprintf(TOPIC_DEVICE_DEVICE, device_id); + } + else + { + return STRING_construct_sprintf(TOPIC_DEVICE_DEVICE_MODULE, device_id, module_id); + } +} + +static STRING_HANDLE buildDevicesAndModulesPath(const IOTHUB_CLIENT_CONFIG* upperConfig, const char* moduleId) +{ + if (moduleId == NULL) + { + return STRING_construct_sprintf("%s.%s/devices/%s", upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId); + } + else + { + return STRING_construct_sprintf("%s.%s/devices/%s/modules/%s", upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId, moduleId); + } +} + +static PMQTTTRANSPORT_HANDLE_DATA InitializeTransportHandleData(const IOTHUB_CLIENT_CONFIG* upperConfig, PDLIST_ENTRY waitingToSend, IOTHUB_AUTHORIZATION_HANDLE auth_module, const char* moduleId) +{ + PMQTTTRANSPORT_HANDLE_DATA state = (PMQTTTRANSPORT_HANDLE_DATA)malloc(sizeof(MQTTTRANSPORT_HANDLE_DATA)); + if (state == NULL) + { + LogError("Could not create MQTT transport state. Memory allocation failed."); + } + else + { + memset(state, 0, sizeof(MQTTTRANSPORT_HANDLE_DATA)); + if ((state->msgTickCounter = tickcounter_create()) == NULL) + { + LogError("Invalid Argument: iotHubName is empty"); + free_transport_handle_data(state); + state = NULL; + } + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_005: [ MQTT transport shall use EXPONENTIAL_WITH_BACK_OFF as default retry policy ] + else if ((state->retry_control_handle = retry_control_create(DEFAULT_RETRY_POLICY, DEFAULT_RETRY_TIMEOUT_IN_SECONDS)) == NULL) + { + LogError("Failed creating default retry control"); + free_transport_handle_data(state); + state = NULL; + } + else if ((state->device_id = STRING_construct(upperConfig->deviceId)) == NULL) + { + LogError("failure constructing device_id."); + free_transport_handle_data(state); + state = NULL; + } + else if ((moduleId != NULL) && ((state->module_id = STRING_construct(moduleId)) == NULL)) + { + LogError("failure constructing module_id."); + free_transport_handle_data(state); + state = NULL; + } + else if ((state->devicesAndModulesPath = buildDevicesAndModulesPath(upperConfig, moduleId)) == NULL) + { + LogError("failure constructing devicesPath."); + free_transport_handle_data(state); + state = NULL; + } + else + { + if ((state->topic_MqttEvent = buildMqttEventString(upperConfig->deviceId, moduleId)) == NULL) + { + LogError("Could not create topic_MqttEvent for MQTT"); + free_transport_handle_data(state); + state = NULL; + } + else + { + state->mqttClient = mqtt_client_init(mqtt_notification_callback, mqtt_operation_complete_callback, state, mqtt_error_callback, state); + if (state->mqttClient == NULL) + { + LogError("failure initializing mqtt client."); + free_transport_handle_data(state); + state = NULL; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_008: [If the upperConfig contains a valid protocolGatewayHostName value the this shall be used for the hostname, otherwise the hostname shall be constructed using the iothubname and iothubSuffix.] */ + if (upperConfig->protocolGatewayHostName == NULL) + { + state->hostAddress = STRING_construct_sprintf("%s.%s", upperConfig->iotHubName, upperConfig->iotHubSuffix); + } + else + { + state->hostAddress = STRING_construct(upperConfig->protocolGatewayHostName); + } + + if (state->hostAddress == NULL) + { + LogError("failure constructing host address."); + free_transport_handle_data(state); + state = NULL; + } + else if ((state->configPassedThroughUsername = buildConfigForUsername(upperConfig, moduleId)) == NULL) + { + free_transport_handle_data(state); + state = NULL; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_010: [IoTHubTransport_MQTT_Common_Create shall allocate memory to save its internal state where all topics, hostname, device_id, device_key, sasTokenSr and client handle shall be saved.] */ + DList_InitializeListHead(&(state->telemetry_waitingForAck)); + DList_InitializeListHead(&(state->ack_waiting_queue)); + state->isDestroyCalled = false; + state->isRegistered = false; + state->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + state->device_twin_get_sent = false; + state->isRecoverableError = true; + state->packetId = 1; + state->llClientHandle = NULL; + state->xioTransport = NULL; + state->portNum = 0; + state->waitingToSend = waitingToSend; + state->currPacketState = CONNECT_TYPE; + state->keepAliveValue = DEFAULT_MQTT_KEEPALIVE; + state->connect_timeout_in_sec = DEFAULT_CONNACK_TIMEOUT; + state->connectFailCount = 0; + state->connectTick = 0; + state->topic_MqttMessage = NULL; + state->topic_GetState = NULL; + state->topic_NotifyState = NULL; + state->topics_ToSubscribe = UNSUBSCRIBE_FROM_TOPIC; + state->topic_DeviceMethods = NULL; + state->topic_InputQueue = NULL; + state->log_trace = state->raw_trace = false; + srand((unsigned int)get_time(NULL)); + state->authorization_module = auth_module; + state->isProductInfoSet = false; + state->option_sas_token_lifetime_secs = SAS_TOKEN_DEFAULT_LIFETIME; + state->auto_url_encode_decode = false; + state->conn_attempted = false; + } + } + } + } + } + return state; +} + +TRANSPORT_LL_HANDLE IoTHubTransport_MQTT_Common_Create(const IOTHUBTRANSPORT_CONFIG* config, MQTT_GET_IO_TRANSPORT get_io_transport) +{ + PMQTTTRANSPORT_HANDLE_DATA result; + size_t deviceIdSize; + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_001: [If parameter config is NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_041: [ if get_io_transport is NULL then IoTHubTransport_MQTT_Common_Create shall return NULL. ] */ + if (config == NULL || get_io_transport == NULL) + { + LogError("Invalid Argument: Config Parameter is NULL."); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [If the parameter config's variables upperConfig, auth_module_handle or waitingToSend are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if (config->auth_module_handle == NULL) + { + LogError("Invalid Argument: auth_module_handle is NULL)"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [If the parameter config's variables upperConfig or waitingToSend are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, both deviceKey and deviceSasToken, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_003: [If both deviceKey & deviceSasToken fields are NOT NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if (config->upperConfig == NULL || + config->upperConfig->protocol == NULL || + config->upperConfig->deviceId == NULL || + ((config->upperConfig->deviceKey != NULL) && (config->upperConfig->deviceSasToken != NULL)) || + //is_key_validate(config) != 0 || + config->upperConfig->iotHubName == NULL || + config->upperConfig->iotHubSuffix == NULL) + { + LogError("Invalid Argument: upperConfig structure contains an invalid parameter"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [If the parameter config's variables upperConfig, auth_module_handle or waitingToSend are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if (config->waitingToSend == NULL) + { + LogError("Invalid Argument: waitingToSend is NULL)"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_006: [If the upperConfig's variables deviceId is an empty strings or length is greater then 128 then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if (((deviceIdSize = strlen(config->upperConfig->deviceId)) > 128U) || (deviceIdSize == 0)) + { + LogError("Invalid Argument: DeviceId is of an invalid size"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, both deviceKey and deviceSasToken, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if ((config->upperConfig->deviceKey != NULL) && (strlen(config->upperConfig->deviceKey) == 0)) + { + LogError("Invalid Argument: deviceKey is empty"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, both deviceKey and deviceSasToken, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if ((config->upperConfig->deviceSasToken != NULL) && (strlen(config->upperConfig->deviceSasToken) == 0)) + { + LogError("Invalid Argument: deviceSasToken is empty"); + result = NULL; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, deviceKey, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + else if (strlen(config->upperConfig->iotHubName) == 0) + { + LogError("Invalid Argument: iotHubName is empty"); + result = NULL; + } + else + { + result = InitializeTransportHandleData(config->upperConfig, config->waitingToSend, config->auth_module_handle, config->moduleId); + if (result != NULL) + { + result->get_io_transport = get_io_transport; + result->http_proxy_hostname = NULL; + result->http_proxy_username = NULL; + result->http_proxy_password = NULL; + } + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_009: [If any error is encountered then IoTHubTransport_MQTT_Common_Create shall return NULL.] */ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_011: [On Success IoTHubTransport_MQTT_Common_Create shall return a non-NULL value.] */ + return result; +} + +void IoTHubTransport_MQTT_Common_Destroy(TRANSPORT_LL_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_012: [IoTHubTransport_MQTT_Common_Destroy shall do nothing if parameter handle is NULL.] */ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if (transport_data != NULL) + { + transport_data->isDestroyCalled = true; + + DisconnectFromClient(transport_data); + + //Empty the Waiting for Ack Messages. + while (!DList_IsListEmpty(&transport_data->telemetry_waitingForAck)) + { + PDLIST_ENTRY currentEntry = DList_RemoveHeadList(&transport_data->telemetry_waitingForAck); + MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = containingRecord(currentEntry, MQTT_MESSAGE_DETAILS_LIST, entry); + sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY); + free(mqttMsgEntry); + } + while (!DList_IsListEmpty(&transport_data->ack_waiting_queue)) + { + PDLIST_ENTRY currentEntry = DList_RemoveHeadList(&transport_data->ack_waiting_queue); + MQTT_DEVICE_TWIN_ITEM* mqtt_device_twin = containingRecord(currentEntry, MQTT_DEVICE_TWIN_ITEM, entry); + IoTHubClientCore_LL_ReportedStateComplete(transport_data->llClientHandle, mqtt_device_twin->iothub_msg_id, STATUS_CODE_TIMEOUT_VALUE); + free(mqtt_device_twin); + } + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_014: [IoTHubTransport_MQTT_Common_Destroy shall free all the resources currently in use.] */ + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_012: [ `IoTHubTransport_MQTT_Common_Destroy` shall free the stored proxy options. ]*/ + free_transport_handle_data(transport_data); + } +} + +int IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if (transport_data == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_042: [If the parameter handle is NULL than IoTHubTransport_MQTT_Common_Subscribe shall return a non-zero value.] */ + LogError("Invalid handle parameter. NULL."); + result = __FAILURE__; + } + else + { + if (transport_data->topic_GetState == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_044: [IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin shall construct the get state topic string and the notify state topic string.] */ + transport_data->topic_GetState = STRING_construct(TOPIC_GET_DESIRED_STATE); + if (transport_data->topic_GetState == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_046: [Upon failure IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin shall return a non-zero value.] */ + LogError("Failure: unable constructing reported state topic"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_047: [On success IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin shall return 0.] */ + transport_data->topics_ToSubscribe |= SUBSCRIBE_GET_REPORTED_STATE_TOPIC; + result = 0; + } + } + else + { + result = 0; + } + if (result == 0) + { + changeStateToSubscribeIfAllowed(transport_data); + } + } + return result; +} + +void IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_048: [If the parameter handle is NULL than IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin shall do nothing.] */ + if (transport_data != NULL) + { + if (transport_data->topic_GetState != NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_049: [If subscribe_state is set to IOTHUB_DEVICE_TWIN_DESIRED_STATE then IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin shall unsubscribe from the topic_GetState to the mqtt client.] */ + transport_data->topics_ToSubscribe &= ~SUBSCRIBE_GET_REPORTED_STATE_TOPIC; + STRING_delete(transport_data->topic_GetState); + transport_data->topic_GetState = NULL; + } + if (transport_data->topic_NotifyState != NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_050: [If subscribe_state is set to IOTHUB_DEVICE_TWIN_NOTIFICATION_STATE then IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin shall unsubscribe from the topic_NotifyState to the mqtt client.] */ + transport_data->topics_ToSubscribe &= ~SUBSCRIBE_NOTIFICATION_STATE_TOPIC; + STRING_delete(transport_data->topic_NotifyState); + transport_data->topic_NotifyState = NULL; + } + } + else + { + LogError("Invalid argument to unsubscribe (handle is NULL)."); + } +} + +int IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + + if (transport_data == NULL) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_001 : [If the parameter handle is NULL than IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall return a non - zero value.]*/ + LogError("Invalid handle parameter. NULL."); + result = __FAILURE__; + } + else + { + if (transport_data->topic_DeviceMethods == NULL) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_004 : [IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall construct the DEVICE_METHOD topic string for subscribe.]*/ + transport_data->topic_DeviceMethods = STRING_construct(TOPIC_DEVICE_METHOD_SUBSCRIBE); + if (transport_data->topic_DeviceMethods == NULL) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_006 : [Upon failure IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall return a non - zero value.]*/ + LogError("Failure: unable constructing device method subscribe topic"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_003 : [IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall set the signaling flag for DEVICE_METHOD topic for the receiver's topic list. ]*/ + transport_data->topics_ToSubscribe |= SUBSCRIBE_DEVICE_METHOD_TOPIC; + result = 0; + } + } + else + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_002 : [If the MQTT transport has been previously subscribed to DEVICE_METHOD topic IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall do nothing and return 0.]*/ + result = 0; + } + + if (result == 0) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_005 : [IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall schedule the send of the subscription.]*/ + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_007 : [On success IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod shall return 0.]*/ + changeStateToSubscribeIfAllowed(transport_data); + } + } + return result; +} + +void IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_008 : [If the parameter handle is NULL than IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall do nothing and return.]*/ + if (transport_data != NULL) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_009 : [If the MQTT transport has not been subscribed to DEVICE_METHOD topic IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall do nothing and return.]*/ + if (transport_data->topic_DeviceMethods != NULL) + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_010 : [IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall construct the DEVICE_METHOD topic string for unsubscribe.]*/ + const char* unsubscribe[1]; + unsubscribe[0] = STRING_c_str(transport_data->topic_DeviceMethods); + + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_011 : [IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall send the unsubscribe.]*/ + if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0) + { + LogError("Failure calling mqtt_client_unsubscribe"); + } + + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_012 : [IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall removes the signaling flag for DEVICE_METHOD topic from the receiver's topic list. ]*/ + STRING_delete(transport_data->topic_DeviceMethods); + transport_data->topic_DeviceMethods = NULL; + transport_data->topics_ToSubscribe &= ~SUBSCRIBE_DEVICE_METHOD_TOPIC; + } + } + else + { + LogError("Invalid argument to unsubscribe (NULL)."); + } +} + +int IoTHubTransport_MQTT_Common_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t respSize, int status) +{ + int result; + MQTTTRANSPORT_HANDLE_DATA* transport_data = (MQTTTRANSPORT_HANDLE_DATA*)handle; + if (transport_data != NULL) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_042: [ IoTHubTransport_MQTT_Common_DeviceMethod_Response shall publish an mqtt message for the device method response. ] */ + DEVICE_METHOD_INFO* dev_method_info = (DEVICE_METHOD_INFO*)methodId; + if (dev_method_info == NULL) + { + LogError("Failure: DEVICE_METHOD_INFO was NULL"); + result = __FAILURE__; + } + else + { + if (publish_device_method_message(transport_data, status, dev_method_info->request_id, response, respSize) != 0) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_051: [ If any error is encountered, IoTHubTransport_MQTT_Common_DeviceMethod_Response shall return a non-zero value. ] */ + LogError("Failure: publishing device method response"); + result = __FAILURE__; + } + else + { + result = 0; + } + STRING_delete(dev_method_info->request_id); + free(dev_method_info); + } + } + else + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_041: [ If the parameter handle is NULL than IoTHubTransport_MQTT_Common_DeviceMethod_Response shall return a non-zero value. ] */ + result = __FAILURE__; + LogError("Failure: invalid IOTHUB_DEVICE_HANDLE parameter specified"); + } + return result; +} + +static STRING_HANDLE buildTopicMqttMessage(const char* device_id, const char* module_id) +{ + if (module_id == NULL) + { + return STRING_construct_sprintf(TOPIC_DEVICE_MSG, device_id); + } + else + { + return STRING_construct_sprintf(TOPIC_DEVICE_MODULE_MSG, device_id, module_id); + } +} + +int IoTHubTransport_MQTT_Common_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if (transport_data == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_015: [If parameter handle is NULL than IoTHubTransport_MQTT_Common_Subscribe shall return a non-zero value.] */ + LogError("Invalid handle parameter. NULL."); + result = __FAILURE__; + } + else + { + /* Code_SRS_IOTHUB_MQTT_TRANSPORT_07_016: [IoTHubTransport_MQTT_Common_Subscribe shall set a flag to enable mqtt_client_subscribe to be called to subscribe to the Message Topic.] */ + transport_data->topic_MqttMessage = buildTopicMqttMessage(STRING_c_str(transport_data->device_id), STRING_c_str(transport_data->module_id)); + if (transport_data->topic_MqttMessage == NULL) + { + LogError("Failure constructing Message Topic"); + result = __FAILURE__; + } + else + { + transport_data->topics_ToSubscribe |= SUBSCRIBE_TELEMETRY_TOPIC; + /* Code_SRS_IOTHUB_MQTT_TRANSPORT_07_035: [If current packet state is not CONNACT, DISCONNECT_TYPE, or PACKET_TYPE_ERROR then IoTHubTransport_MQTT_Common_Subscribe shall set the packet state to SUBSCRIBE_TYPE.]*/ + changeStateToSubscribeIfAllowed(transport_data); + result = 0; + } + } + return result; +} + +void IoTHubTransport_MQTT_Common_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_019: [If parameter handle is NULL then IoTHubTransport_MQTT_Common_Unsubscribe shall do nothing.] */ + if (transport_data != NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_020: [IoTHubTransport_MQTT_Common_Unsubscribe shall call mqtt_client_unsubscribe to unsubscribe the mqtt message topic.] */ + const char* unsubscribe[1]; + unsubscribe[0] = STRING_c_str(transport_data->topic_MqttMessage); + if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0) + { + LogError("Failure calling mqtt_client_unsubscribe"); + } + STRING_delete(transport_data->topic_MqttMessage); + transport_data->topic_MqttMessage = NULL; + transport_data->topics_ToSubscribe &= ~SUBSCRIBE_TELEMETRY_TOPIC; + } + else + { + LogError("Invalid argument to unsubscribe (NULL)."); + } +} + +IOTHUB_PROCESS_ITEM_RESULT IoTHubTransport_MQTT_Common_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + IOTHUB_PROCESS_ITEM_RESULT result; + /* Codes_SRS_IOTHUBCLIENT_LL_07_001: [ If handle or iothub_item are NULL then IoTHubTransport_MQTT_Common_ProcessItem shall return IOTHUB_PROCESS_ERROR. ]*/ + if (handle == NULL || iothub_item == NULL) + { + LogError("Invalid handle parameter iothub_item=%p", iothub_item); + result = IOTHUB_PROCESS_ERROR; + } + else + { + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + + if (transport_data->currPacketState == PUBLISH_TYPE) + { + if (item_type == IOTHUB_TYPE_DEVICE_TWIN) + { + MQTT_DEVICE_TWIN_ITEM* mqtt_info = (MQTT_DEVICE_TWIN_ITEM*)malloc(sizeof(MQTT_DEVICE_TWIN_ITEM)); + if (mqtt_info == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_004: [ If any errors are encountered IoTHubTransport_MQTT_Common_ProcessItem shall return IOTHUB_PROCESS_ERROR. ]*/ + result = IOTHUB_PROCESS_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_07_003: [ IoTHubTransport_MQTT_Common_ProcessItem shall publish a message to the mqtt protocol with the message topic for the message type.]*/ + mqtt_info->iothub_type = item_type; + mqtt_info->iothub_msg_id = iothub_item->device_twin->item_id; + mqtt_info->retryCount = 0; + + /* Codes_SRS_IOTHUBCLIENT_LL_07_005: [ If successful IoTHubTransport_MQTT_Common_ProcessItem shall add mqtt info structure acknowledgement queue. ] */ + DList_InsertTailList(&transport_data->ack_waiting_queue, &mqtt_info->entry); + + if (publish_device_twin_message(transport_data, iothub_item->device_twin, mqtt_info) != 0) + { + DList_RemoveEntryList(&mqtt_info->entry); + + free(mqtt_info); + /* Codes_SRS_IOTHUBCLIENT_LL_07_004: [ If any errors are encountered IoTHubTransport_MQTT_Common_ProcessItem shall return IOTHUB_PROCESS_ERROR. ]*/ + result = IOTHUB_PROCESS_ERROR; + } + else + { + result = IOTHUB_PROCESS_OK; + } + } + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_006: [ If the item_type is not a supported type IoTHubTransport_MQTT_Common_ProcessItem shall return IOTHUB_PROCESS_CONTINUE. ]*/ + result = IOTHUB_PROCESS_CONTINUE; + } + } + else + { + /* Codes_SRS_IOTHUBCLIENT_LL_07_002: [ If the mqtt is not ready to publish messages IoTHubTransport_MQTT_Common_ProcessItem shall return IOTHUB_PROCESS_NOT_CONNECTED. ] */ + result = IOTHUB_PROCESS_NOT_CONNECTED; + } + } + return result; +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_054: [ IoTHubTransport_MQTT_Common_DoWork shall subscribe to the Notification and get_state Topics if they are defined. ] */ +void IoTHubTransport_MQTT_Common_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_026: [IoTHubTransport_MQTT_Common_DoWork shall do nothing if parameter handle and/or iotHubClientHandle is NULL.] */ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if (transport_data != NULL && iotHubClientHandle != NULL) + { + transport_data->llClientHandle = iotHubClientHandle; + + if (InitializeConnection(transport_data) != 0) + { + // Don't want to flood the logs with failures here + } + else + { + if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_PENDING_CLOSE) + { + mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + } + else if (transport_data->currPacketState == CONNACK_TYPE || transport_data->currPacketState == SUBSCRIBE_TYPE) + { + SubscribeToMqttProtocol(transport_data); + } + else if (transport_data->currPacketState == SUBACK_TYPE) + { + if ((transport_data->topic_NotifyState != NULL || transport_data->topic_GetState != NULL) && + !transport_data->device_twin_get_sent) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ IoTHubTransport_MQTT_Common_DoWork shall send a device twin get property message upon successfully retrieving a SUBACK on device twin topics. ] */ + if (publish_device_twin_get_message(transport_data) == 0) + { + transport_data->device_twin_get_sent = true; + } + else + { + LogError("Failure: sending device twin get property command."); + } + } + // Publish can be called now + transport_data->currPacketState = PUBLISH_TYPE; + } + else if (transport_data->currPacketState == PUBLISH_TYPE) + { + PDLIST_ENTRY currentListEntry = transport_data->telemetry_waitingForAck.Flink; + while (currentListEntry != &transport_data->telemetry_waitingForAck) + { + tickcounter_ms_t current_ms; + MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = containingRecord(currentListEntry, MQTT_MESSAGE_DETAILS_LIST, entry); + DLIST_ENTRY nextListEntry; + nextListEntry.Flink = currentListEntry->Flink; + + (void)tickcounter_get_current_ms(transport_data->msgTickCounter, ¤t_ms); + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_033: [IoTHubTransport_MQTT_Common_DoWork shall iterate through the Waiting Acknowledge messages looking for any message that has been waiting longer than 2 min.]*/ + if (((current_ms - mqttMsgEntry->msgPublishTime) / 1000) > RESEND_TIMEOUT_VALUE_MIN) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_034: [If IoTHubTransport_MQTT_Common_DoWork has resent the message two times then it shall fail the message and reconnect to IoTHub ... ] */ + if (mqttMsgEntry->retryCount >= MAX_SEND_RECOUNT_LIMIT) + { + PDLIST_ENTRY current_entry; + (void)DList_RemoveEntryList(currentListEntry); + sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT); + free(mqttMsgEntry); + + transport_data->currPacketState = PACKET_TYPE_ERROR; + transport_data->device_twin_get_sent = false; + DisconnectFromClient(transport_data); + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_057: [ ... then go through all the rest of the waiting messages and reset the retryCount on the message. ]*/ + current_entry = transport_data->telemetry_waitingForAck.Flink; + while (current_entry != &transport_data->telemetry_waitingForAck) + { + MQTT_MESSAGE_DETAILS_LIST* msg_reset_entry; + msg_reset_entry = containingRecord(current_entry, MQTT_MESSAGE_DETAILS_LIST, entry); + msg_reset_entry->retryCount = 0; + current_entry = current_entry->Flink; + } + } + else + { + size_t messageLength; + const unsigned char* messagePayload = RetrieveMessagePayload(mqttMsgEntry->iotHubMessageEntry->messageHandle, &messageLength); + if (messageLength == 0 || messagePayload == NULL) + { + LogError("Failure from creating Message IoTHubMessage_GetData"); + } + else + { + if (publish_mqtt_telemetry_msg(transport_data, mqttMsgEntry, messagePayload, messageLength) != 0) + { + (void)DList_RemoveEntryList(currentListEntry); + sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR); + free(mqttMsgEntry); + } + } + } + } + currentListEntry = nextListEntry.Flink; + } + + currentListEntry = transport_data->waitingToSend->Flink; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */ + while (currentListEntry != transport_data->waitingToSend) + { + IOTHUB_MESSAGE_LIST* iothubMsgList = containingRecord(currentListEntry, IOTHUB_MESSAGE_LIST, entry); + DLIST_ENTRY savedFromCurrentListEntry; + savedFromCurrentListEntry.Flink = currentListEntry->Flink; + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */ + size_t messageLength; + const unsigned char* messagePayload = RetrieveMessagePayload(iothubMsgList->messageHandle, &messageLength); + if (messageLength == 0 || messagePayload == NULL) + { + LogError("Failure result from IoTHubMessage_GetData"); + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_029: [IoTHubTransport_MQTT_Common_DoWork shall create a MQTT_MESSAGE_HANDLE and pass this to a call to mqtt_client_publish.] */ + MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = (MQTT_MESSAGE_DETAILS_LIST*)malloc(sizeof(MQTT_MESSAGE_DETAILS_LIST)); + if (mqttMsgEntry == NULL) + { + LogError("Allocation Error: Failure allocating MQTT Message Detail List."); + } + else + { + mqttMsgEntry->retryCount = 0; + mqttMsgEntry->iotHubMessageEntry = iothubMsgList; + mqttMsgEntry->packet_id = get_next_packet_id(transport_data); + if (publish_mqtt_telemetry_msg(transport_data, mqttMsgEntry, messagePayload, messageLength) != 0) + { + (void)(DList_RemoveEntryList(currentListEntry)); + sendMsgComplete(iothubMsgList, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR); + free(mqttMsgEntry); + } + else + { + (void)(DList_RemoveEntryList(currentListEntry)); + DList_InsertTailList(&(transport_data->telemetry_waitingForAck), &(mqttMsgEntry->entry)); + } + } + } + currentListEntry = savedFromCurrentListEntry.Flink; + } + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_030: [IoTHubTransport_MQTT_Common_DoWork shall call mqtt_client_dowork everytime it is called if it is connected.] */ + mqtt_client_dowork(transport_data->mqttClient); + } + } +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_MQTT_Common_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + + if (handle == NULL || iotHubClientStatus == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_023: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter.] */ + LogError("invalid arument."); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + MQTTTRANSPORT_HANDLE_DATA* handleData = (MQTTTRANSPORT_HANDLE_DATA*)handle; + if (!DList_IsListEmpty(handleData->waitingToSend) || !DList_IsListEmpty(&(handleData->telemetry_waitingForAck))) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_025: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent.] */ + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_024: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent.] */ + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE; + } + result = IOTHUB_CLIENT_OK; + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_MQTT_Common_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_021: [If any parameter is NULL then IoTHubTransport_MQTT_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + IOTHUB_CLIENT_RESULT result; + if ( + (handle == NULL) || + (option == NULL) || + (value == NULL) + ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid parameter (NULL) passed to IoTHubTransport_MQTT_Common_SetOption."); + } + else + { + MQTTTRANSPORT_HANDLE_DATA* transport_data = (MQTTTRANSPORT_HANDLE_DATA*)handle; + + IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module); + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_031: [If the option parameter is set to "logtrace" then the value shall be a bool_ptr and the value will determine if the mqtt client log is on or off.] */ + if (strcmp(OPTION_LOG_TRACE, option) == 0) + { + transport_data->log_trace = *((bool*)value); + mqtt_client_set_trace(transport_data->mqttClient, transport_data->log_trace, transport_data->raw_trace); + result = IOTHUB_CLIENT_OK; + } + else if (strcmp("rawlogtrace", option) == 0) + { + transport_data->raw_trace = *((bool*)value); + mqtt_client_set_trace(transport_data->mqttClient, transport_data->log_trace, transport_data->raw_trace); + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(OPTION_AUTO_URL_ENCODE_DECODE, option) == 0) + { + transport_data->auto_url_encode_decode = *((bool*)value); + result = IOTHUB_CLIENT_OK; + } + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_052: [ If the option parameter is set to "sas_token_lifetime" then the value shall be a size_t_ptr and the value will determine the mqtt sas token lifetime.] */ + else if (strcmp(OPTION_SAS_TOKEN_LIFETIME, option) == 0) + { + size_t* sas_lifetime = (size_t*)value; + transport_data->option_sas_token_lifetime_secs = *sas_lifetime; + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(OPTION_CONNECTION_TIMEOUT, option) == 0) + { + int* connection_time = (int*)value; + if (*connection_time != transport_data->connect_timeout_in_sec) + { + transport_data->connect_timeout_in_sec = (uint16_t)(*connection_time); + } + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(OPTION_KEEP_ALIVE, option) == 0) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_036: [If the option parameter is set to "keepalive" then the value shall be a int_ptr and the value will determine the mqtt keepalive time that is set for pings.] */ + int* keepAliveOption = (int*)value; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_037 : [If the option parameter is set to supplied int_ptr keepalive is the same value as the existing keepalive then IoTHubTransport_MQTT_Common_SetOption shall do nothing.] */ + if (*keepAliveOption != transport_data->keepAliveValue) + { + transport_data->keepAliveValue = (uint16_t)(*keepAliveOption); + if (transport_data->mqttClientStatus != MQTT_CLIENT_STATUS_NOT_CONNECTED) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_038: [If the client is connected when the keepalive is set then IoTHubTransport_MQTT_Common_SetOption shall disconnect and reconnect with the specified keepalive value.] */ + DisconnectFromClient(transport_data); + } + } + result = IOTHUB_CLIENT_OK; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_039: [If the option parameter is set to "x509certificate" then the value shall be a const char of the certificate to be used for x509.] */ + else if ((strcmp(OPTION_X509_CERT, option) == 0) && (cred_type != IOTHUB_CREDENTIAL_TYPE_X509 && cred_type != IOTHUB_CREDENTIAL_TYPE_UNKNOWN)) + { + LogError("x509certificate specified, but authentication method is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_040: [If the option parameter is set to "x509privatekey" then the value shall be a const char of the RSA Private Key to be used for x509.] */ + else if ((strcmp(OPTION_X509_PRIVATE_KEY, option) == 0) && (cred_type != IOTHUB_CREDENTIAL_TYPE_X509 && cred_type != IOTHUB_CREDENTIAL_TYPE_UNKNOWN)) + { + LogError("x509privatekey specified, but authentication method is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else if (strcmp(OPTION_HTTP_PROXY, option) == 0) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_001: [ If `option` is `proxy_data`, `value` shall be used as an `HTTP_PROXY_OPTIONS*`. ]*/ + HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS*)value; + + if (transport_data->xioTransport != NULL) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_007: [ If the underlying IO has already been created, then `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + LogError("Cannot set proxy option once the underlying IO is created"); + result = IOTHUB_CLIENT_ERROR; + } + else if (proxy_options->host_address == NULL) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_003: [ If `host_address` is NULL, `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + LogError("NULL host_address in proxy options"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_006: [ If only one of `username` and `password` is NULL, `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + else if (((proxy_options->username == NULL) || (proxy_options->password == NULL)) && + (proxy_options->username != proxy_options->password)) + { + LogError("Only one of username and password for proxy settings was NULL"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + char* copied_proxy_hostname = NULL; + char* copied_proxy_username = NULL; + char* copied_proxy_password = NULL; + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_002: [ The fields `host_address`, `port`, `username` and `password` shall be saved for later used (needed when creating the underlying IO to be used by the transport). ]*/ + transport_data->http_proxy_port = proxy_options->port; + if (mallocAndStrcpy_s(&copied_proxy_hostname, proxy_options->host_address) != 0) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_004: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + LogError("Cannot copy HTTP proxy hostname"); + result = IOTHUB_CLIENT_ERROR; + } + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_005: [ `username` and `password` shall be allowed to be NULL. ]*/ + else if ((proxy_options->username != NULL) && (mallocAndStrcpy_s(&copied_proxy_username, proxy_options->username) != 0)) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_004: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + free(copied_proxy_hostname); + LogError("Cannot copy HTTP proxy username"); + result = IOTHUB_CLIENT_ERROR; + } + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_005: [ `username` and `password` shall be allowed to be NULL. ]*/ + else if ((proxy_options->password != NULL) && (mallocAndStrcpy_s(&copied_proxy_password, proxy_options->password) != 0)) + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_004: [ If copying `host_address`, `username` or `password` fails, `IoTHubTransport_MQTT_Common_SetOption` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ + if (copied_proxy_username != NULL) + { + free(copied_proxy_username); + } + free(copied_proxy_hostname); + LogError("Cannot copy HTTP proxy password"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_009: [ When setting the proxy options succeeds any previously saved proxy options shall be freed. ]*/ + free_proxy_data(transport_data); + + transport_data->http_proxy_hostname = copied_proxy_hostname; + transport_data->http_proxy_username = copied_proxy_username; + transport_data->http_proxy_password = copied_proxy_password; + + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_008: [ If setting the `proxy_data` option suceeds, `IoTHubTransport_MQTT_Common_SetOption` shall return `IOTHUB_CLIENT_OK` ]*/ + result = IOTHUB_CLIENT_OK; + } + } + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_039: [If the option parameter is set to "x509certificate" then the value shall be a const char of the certificate to be used for x509.] */ + if (((strcmp(OPTION_X509_CERT, option) == 0) || (strcmp(OPTION_X509_PRIVATE_KEY, option) == 0)) && (cred_type != IOTHUB_CREDENTIAL_TYPE_X509)) + { + IoTHubClient_Auth_Set_x509_Type(transport_data->authorization_module, true); + } + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_032: [IoTHubTransport_MQTT_Common_SetOption shall pass down the option to xio_setoption if the option parameter is not a known option string for the MQTT transport.] */ + if (GetTransportProviderIfNecessary(transport_data) == 0) + { + if (xio_setoption(transport_data->xioTransport, option, value) == 0) + { + result = IOTHUB_CLIENT_OK; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_132: [IoTHubTransport_MQTT_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG xio_setoption fails] */ + result = IOTHUB_CLIENT_INVALID_ARG; + } + } + else + { + result = IOTHUB_CLIENT_ERROR; + } + } + } + return result; +} + +static bool check_module_ids_equal(const char* transportModuleId, const char* deviceModuleId) +{ + if ((transportModuleId != NULL) && (deviceModuleId == NULL)) + { + return false; + } + else if ((transportModuleId == NULL) && (deviceModuleId != NULL)) + { + return false; + } + else if ((transportModuleId == NULL) && (deviceModuleId == NULL)) + { + return true; + } + else + { + return (0 == strcmp(transportModuleId, deviceModuleId)); + } +} + +IOTHUB_DEVICE_HANDLE IoTHubTransport_MQTT_Common_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + IOTHUB_DEVICE_HANDLE result = NULL; + (void)iotHubClientHandle; + + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_001: [ IoTHubTransport_MQTT_Common_Register shall return NULL if the TRANSPORT_LL_HANDLE is NULL.] + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_002: [ IoTHubTransport_MQTT_Common_Register shall return NULL if device or waitingToSend are NULL.] + if ((handle == NULL) || (device == NULL) || (waitingToSend == NULL)) + { + LogError("IoTHubTransport_MQTT_Common_Register: handle, device or waitingToSend is NULL."); + result = NULL; + } + else + { + MQTTTRANSPORT_HANDLE_DATA* transport_data = (MQTTTRANSPORT_HANDLE_DATA*)handle; + + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_001: [ IoTHubTransport_MQTT_Common_Register shall return NULL if deviceId, or both deviceKey and deviceSasToken are NULL.] + if (device->deviceId == NULL) + { + LogError("IoTHubTransport_MQTT_Common_Register: deviceId is NULL."); + result = NULL; + } + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_002: [ IoTHubTransport_MQTT_Common_Register shall return NULL if both deviceKey and deviceSasToken are provided.] + else if ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) + { + LogError("IoTHubTransport_MQTT_Common_Register: Both deviceKey and deviceSasToken are defined. Only one can be used."); + result = NULL; + } + else + { + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_003: [ IoTHubTransport_MQTT_Common_Register shall return NULL if deviceId or deviceKey do not match the deviceId and deviceKey passed in during IoTHubTransport_MQTT_Common_Create.] + if (strcmp(STRING_c_str(transport_data->device_id), device->deviceId) != 0) + { + LogError("IoTHubTransport_MQTT_Common_Register: deviceId does not match."); + result = NULL; + } + else if (!check_module_ids_equal(STRING_c_str(transport_data->module_id), device->moduleId)) + { + LogError("IoTHubTransport_MQTT_Common_Register: moduleId does not match."); + result = NULL; + } + else if (IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module) == IOTHUB_CREDENTIAL_TYPE_DEVICE_KEY && + (strcmp(IoTHubClient_Auth_Get_DeviceKey(transport_data->authorization_module), device->deviceKey) != 0)) + { + LogError("IoTHubTransport_MQTT_Common_Register: deviceKey does not match."); + result = NULL; + } + else + { + if (transport_data->isRegistered == true) + { + LogError("Transport already has device registered by id: [%s]", device->deviceId); + result = NULL; + } + else + { + transport_data->isRegistered = true; + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_004: [ IoTHubTransport_MQTT_Common_Register shall return the TRANSPORT_LL_HANDLE as the IOTHUB_DEVICE_HANDLE. ] + result = (IOTHUB_DEVICE_HANDLE)handle; + } + } + } + } + + return result; +} + +void IoTHubTransport_MQTT_Common_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_005: [ If deviceHandle is NULL `IoTHubTransport_MQTT_Common_Unregister` shall do nothing. ] + if (deviceHandle != NULL) + { + MQTTTRANSPORT_HANDLE_DATA* transport_data = (MQTTTRANSPORT_HANDLE_DATA*)deviceHandle; + + transport_data->isRegistered = false; + } +} + +STRING_HANDLE IoTHubTransport_MQTT_Common_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + STRING_HANDLE result; + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_02_001: [ If handle is NULL then IoTHubTransport_MQTT_Common_GetHostname shall fail and return NULL. ]*/ + if (handle == NULL) + { + result = NULL; + } + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_02_002: [ Otherwise IoTHubTransport_MQTT_Common_GetHostname shall return a non-NULL STRING_HANDLE containg the hostname. ]*/ + else if ((result = STRING_clone(((MQTTTRANSPORT_HANDLE_DATA*)(handle))->hostAddress)) == NULL) + { + LogError("Cannot provide the target host name (STRING_clone failed)."); + } + + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubTransport_MQTT_Common_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + (void)disposition; + + IOTHUB_CLIENT_RESULT result; + if (message_data) + { + if (message_data->messageHandle) + { + IoTHubMessage_Destroy(message_data->messageHandle); + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_10_002: [If any of the messageData fields are NULL, IoTHubTransport_MQTT_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("message handle is NULL"); + result = IOTHUB_CLIENT_ERROR; + } + free(message_data); + } + else + { + /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_10_001: [If messageData is NULL, IoTHubTransport_MQTT_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("message_data is NULL"); + result = IOTHUB_CLIENT_ERROR; + } + return result; +} + + +int IoTHubTransport_MQTT_Common_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if (transport_data == NULL) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_066: [ If parameter handle is NULL than IoTHubTransport_MQTT_Common_Subscribe_InputQueue shall return a non-zero value.] + LogError("Invalid handle parameter. NULL."); + result = __FAILURE__; + } + else if (transport_data->module_id == NULL) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_073: [ If module ID is not set on the transpont, IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue shall fail.] + LogError("ModuleID must be specified for input queues. NULL."); + result = __FAILURE__; + } + else if ((transport_data->topic_InputQueue == NULL) && + (transport_data->topic_InputQueue = STRING_construct_sprintf(TOPIC_INPUT_QUEUE_NAME, STRING_c_str(transport_data->device_id), STRING_c_str(transport_data->module_id))) == NULL) + { + LogError("Failure constructing Message Topic"); + result = __FAILURE__; + } + else + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_067: [ IoTHubTransport_MQTT_Common_Subscribe_InputQueue shall set a flag to enable mqtt_client_subscribe to be called to subscribe to the input queue Message Topic.] + transport_data->topics_ToSubscribe |= SUBSCRIBE_INPUT_QUEUE_TOPIC; + changeStateToSubscribeIfAllowed(transport_data); + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_070: [ On success IoTHubTransport_MQTT_Common_Subscribe_InputQueue shall return 0.] + result = 0; + } + return result; +} + +void IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle; + if ((transport_data != NULL) && (transport_data->topic_InputQueue != NULL)) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_072: [ IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue shall call mqtt_client_unsubscribe to unsubscribe the mqtt input queue message topic.] + const char* unsubscribe[1]; + unsubscribe[0] = STRING_c_str(transport_data->topic_InputQueue); + if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0) + { + LogError("Failure calling mqtt_client_unsubscribe"); + } + STRING_delete(transport_data->topic_InputQueue); + transport_data->topic_InputQueue = NULL; + transport_data->topics_ToSubscribe &= ~SUBSCRIBE_INPUT_QUEUE_TOPIC; + } + else + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_071: [ If parameter handle is NULL then IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue shall do nothing.] + LogError("Invalid argument to unsubscribe input queue (NULL)."); + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransportamqp.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "iothubtransportamqp.h" +#include "internal/iothubtransport_amqp_common.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/xlogging.h" + +#define RESULT_OK 0 +#define DEFAULT_IOTHUB_AMQP_PORT 5671 + +static XIO_HANDLE getTLSIOTransport(const char* fqdn, const AMQP_TRANSPORT_PROXY_OPTIONS* amqp_transport_proxy_options) +{ + XIO_HANDLE result; + TLSIO_CONFIG tls_io_config; + + (void)amqp_transport_proxy_options; + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_009: [ `getIoTransportProvider` shall obtain the TLS IO interface handle by calling `platform_get_default_tlsio`. ]*/ + const IO_INTERFACE_DESCRIPTION* io_interface_description = platform_get_default_tlsio(); + if (io_interface_description == NULL) + { + LogError("Failed obtaining default TLS IO interface"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_010: [ The TLS IO parameters shall be a `TLSIO_CONFIG` structure filled as below: ]*/ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_011: [ - `hostname` shall be set to `fqdn`. ]*/ + tls_io_config.hostname = fqdn; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_012: [ - `port` shall be set to 443. ]*/ + tls_io_config.port = DEFAULT_IOTHUB_AMQP_PORT; + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_013: [ `underlying_io_interface` shall be set to NULL. ]*/ + tls_io_config.underlying_io_interface = NULL; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_01_014: [ `underlying_io_parameters` shall be set to NULL. ]*/ + tls_io_config.underlying_io_parameters = NULL; + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_09_003: [If `platform_get_default_tlsio` returns NULL `getTLSIOTransport` shall return NULL.] */ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_09_004: [`getTLSIOTransport` shall return the `XIO_HANDLE` created using `xio_create`.] */ + if ((result = xio_create(io_interface_description, &tls_io_config)) == NULL) + { + LogError("Failed to get the TLS/IO transport (xio_create failed)"); + } + } + + return result; +} + +// API functions +static TRANSPORT_LL_HANDLE IoTHubTransportAMQP_Create(const IOTHUBTRANSPORT_CONFIG* config) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_001: [IoTHubTransportAMQP_Create shall create a TRANSPORT_LL_HANDLE by calling into the IoTHubTransport_AMQP_Common_Create function, passing `config` and getTLSIOTransport.] + return IoTHubTransport_AMQP_Common_Create(config, getTLSIOTransport); +} + +static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportAMQP_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_014: [IoTHubTransportAMQP_ProcessItem shall invoke IoTHubTransport_AMQP_Common_ProcessItem() and return its result.] + return IoTHubTransport_AMQP_Common_ProcessItem(handle, item_type, iothub_item); +} + +static void IoTHubTransportAMQP_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_015: [IoTHubTransportAMQP_DoWork shall call into the IoTHubTransport_AMQP_Common_DoWork()] + IoTHubTransport_AMQP_Common_DoWork(handle, iotHubClientHandle); +} + +static int IoTHubTransportAMQP_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_012: [IoTHubTransportAMQP_Subscribe shall subscribe for D2C messages by calling into the IoTHubTransport_AMQP_Common_Subscribe().] + return IoTHubTransport_AMQP_Common_Subscribe(handle); +} + +static void IoTHubTransportAMQP_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_013: [IoTHubTransportAMQP_Unsubscribe shall subscribe for D2C messages by calling into the IoTHubTransport_AMQP_Common_Unsubscribe().] + IoTHubTransport_AMQP_Common_Unsubscribe(handle); +} + +static int IoTHubTransportAMQP_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_008: [IoTHubTransportAMQP_Subscribe_DeviceTwin shall invoke IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin() and return its result.] + return IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin(handle); +} + +static void IoTHubTransportAMQP_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_009: [IoTHubTransportAMQP_Unsubscribe_DeviceTwin shall invoke IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin()] + IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin(handle); +} + +static int IoTHubTransportAMQP_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_010: [IoTHubTransportAMQP_Subscribe_DeviceMethod shall invoke IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod() and return its result.] + return IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod(handle); +} + +static void IoTHubTransportAMQP_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_011: [IoTHubTransportAMQP_Unsubscribe_DeviceMethod shall invoke IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod()] + IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod(handle); +} + +static int IoTHubTransportAMQP_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + return IoTHubTransport_AMQP_Common_DeviceMethod_Response(handle, methodId, response, response_size, status_response); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_016: [IoTHubTransportAMQP_GetSendStatus shall get the send status by calling into the IoTHubTransport_AMQP_Common_GetSendStatus()] + return IoTHubTransport_AMQP_Common_GetSendStatus(handle, iotHubClientStatus); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_017: [IoTHubTransportAMQP_SetOption shall set the options by calling into the IoTHubTransport_AMQP_Common_SetOption()] + return IoTHubTransport_AMQP_Common_SetOption(handle, option, value); +} + +static IOTHUB_DEVICE_HANDLE IoTHubTransportAMQP_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_006: [IoTHubTransportAMQP_Register shall register the device by calling into the IoTHubTransport_AMQP_Common_Register().] + return IoTHubTransport_AMQP_Common_Register(handle, device, iotHubClientHandle, waitingToSend); +} + +static void IoTHubTransportAMQP_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_007: [IoTHubTransportAMQP_Unregister shall unregister the device by calling into the IoTHubTransport_AMQP_Common_Unregister().] + IoTHubTransport_AMQP_Common_Unregister(deviceHandle); +} + +static void IoTHubTransportAMQP_Destroy(TRANSPORT_LL_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_005: [IoTHubTransportAMQP_Destroy shall destroy the TRANSPORT_LL_HANDLE by calling into the IoTHubTransport_AMQP_Common_Destroy().] + IoTHubTransport_AMQP_Common_Destroy(handle); +} + +static int IoTHubTransportAMQP_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_020: [IoTHubTransportAMQP_SetRetryPolicy shall call into the IoTHubTransport_AMQP_Common_SetRetryPolicy().] + return IoTHubTransport_AMQP_Common_SetRetryPolicy(handle, retryPolicy, retryTimeoutLimitInSeconds); +} + +static STRING_HANDLE IoTHubTransportAMQP_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_09_018: [IoTHubTransportAMQP_GetHostname shall get the hostname by calling into the IoTHubTransport_AMQP_Common_GetHostname()] + return IoTHubTransport_AMQP_Common_GetHostname(handle); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_10_001: [IoTHubTransportAMQP_SendMessageDisposition shall send the message disposition by calling into the IoTHubTransport_AMQP_Common_SendMessageDispostion().] + return IoTHubTransport_AMQP_Common_SendMessageDisposition(message_data, disposition); +} + +static int IotHubTransportAMQP_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_31_021: [IoTHubTransportAMQP_Subscribe_InputQueue shall return a failure as input queues are not implemented for AMQP] + (void)handle; + LogError("AMQP does not support input queues"); + return __FAILURE__; +} + +static void IotHubTransportAMQP_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IOTHUBTRANSPORTAMQP_31_022: [IotHubTransportAMQP_Unsubscribe_InputQueue shall do nothing as input queues are not implemented for AMQP] + (void)handle; + LogError("AMQP does not support input queues"); +} + +static TRANSPORT_PROVIDER thisTransportProvider = +{ + IoTHubTransportAMQP_SendMessageDisposition, /*pfIotHubTransport_Send_Message_Disposition IoTHubTransport_Send_Message_Disposition;*/ + IoTHubTransportAMQP_Subscribe_DeviceMethod, /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/ + IoTHubTransportAMQP_Unsubscribe_DeviceMethod, /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/ + IoTHubTransportAMQP_DeviceMethod_Response, + IoTHubTransportAMQP_Subscribe_DeviceTwin, /*pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin;*/ + IoTHubTransportAMQP_Unsubscribe_DeviceTwin, /*pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin;*/ + IoTHubTransportAMQP_ProcessItem, /*pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem;*/ + IoTHubTransportAMQP_GetHostname, /*pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname;*/ + IoTHubTransportAMQP_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption;*/ + IoTHubTransportAMQP_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create;*/ + IoTHubTransportAMQP_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy;*/ + IoTHubTransportAMQP_Register, /*pfIotHubTransport_Register IoTHubTransport_Register;*/ + IoTHubTransportAMQP_Unregister, /*pfIotHubTransport_Unregister IoTHubTransport_Unegister;*/ + IoTHubTransportAMQP_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe;*/ + IoTHubTransportAMQP_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe;*/ + IoTHubTransportAMQP_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork;*/ + IoTHubTransportAMQP_SetRetryPolicy, /*pfIoTHubTransport_DoWork IoTHubTransport_SetRetryPolicy;*/ + IoTHubTransportAMQP_GetSendStatus, /*pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus;*/ + IotHubTransportAMQP_Subscribe_InputQueue, /*pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; */ + IotHubTransportAMQP_Unsubscribe_InputQueue, /*pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue; */ +}; + +/* Codes_SRS_IOTHUBTRANSPORTAMQP_09_019: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for it's fields: +IoTHubTransport_SendMessageDisposition = IoTHubTransportAMQP_SendMessageDisposition +IoTHubTransport_Subscribe_DeviceMethod = IoTHubTransportAMQP_Subscribe_DeviceMethod +IoTHubTransport_Unsubscribe_DeviceMethod = IoTHubTransportAMQP_Unsubscribe_DeviceMethod +IoTHubTransport_Subscribe_DeviceTwin = IoTHubTransportAMQP_Subscribe_DeviceTwin +IoTHubTransport_Unsubscribe_DeviceTwin = IoTHubTransportAMQP_Unsubscribe_DeviceTwin +IoTHubTransport_ProcessItem - IoTHubTransportAMQP_ProcessItem +IoTHubTransport_GetHostname = IoTHubTransportAMQP_GetHostname +IoTHubTransport_Create = IoTHubTransportAMQP_Create +IoTHubTransport_Destroy = IoTHubTransportAMQP_Destroy +IoTHubTransport_Subscribe = IoTHubTransportAMQP_Subscribe +IoTHubTransport_Unsubscribe = IoTHubTransportAMQP_Unsubscribe +IoTHubTransport_DoWork = IoTHubTransportAMQP_DoWork +IoTHubTransport_SetRetryPolicy = IoTHubTransportAMQP_SetRetryPolicy +IoTHubTransport_SetOption = IoTHubTransportAMQP_SetOption]*/ +extern const TRANSPORT_PROVIDER* AMQP_Protocol(void) +{ + return &thisTransportProvider; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransportamqp_methods.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1094 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/amqp_definitions_application_properties.h" + +#include "internal/iothubtransportamqp_methods.h" + +typedef enum SUBSCRIBE_STATE_TAG +{ + SUBSCRIBE_STATE_NOT_SUBSCRIBED, + SUBSCRIBE_STATE_SUBSCRIBED +} SUBSCRIBE_STATE; + +typedef struct IOTHUBTRANSPORT_AMQP_METHODS_TAG +{ + char* device_id; + char* module_id; + char* hostname; + LINK_HANDLE receiver_link; + LINK_HANDLE sender_link; + MESSAGE_RECEIVER_HANDLE message_receiver; + MESSAGE_SENDER_HANDLE message_sender; + ON_METHOD_REQUEST_RECEIVED on_method_request_received; + void* on_method_request_received_context; + ON_METHODS_ERROR on_methods_error; + void* on_methods_error_context; + ON_METHODS_UNSUBSCRIBED on_methods_unsubscribed; + void* on_methods_unsubscribed_context; + SUBSCRIBE_STATE subscribe_state; + IOTHUBTRANSPORT_AMQP_METHOD_HANDLE* method_request_handles; + size_t method_request_handle_count; + bool receiver_link_disconnected; + bool sender_link_disconnected; +} IOTHUBTRANSPORT_AMQP_METHODS; + +typedef enum MESSAGE_OUTCOME_TAG +{ + MESSAGE_OUTCOME_ACCEPTED, + MESSAGE_OUTCOME_REJECTED, + MESSAGE_OUTCOME_RELEASED +} MESSAGE_OUTCOME; + +typedef struct IOTHUBTRANSPORT_AMQP_METHOD_TAG +{ + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle; + uuid correlation_id; +} IOTHUBTRANSPORT_AMQP_METHOD; + +static void remove_tracked_handle(IOTHUBTRANSPORT_AMQP_METHODS* amqp_methods_handle, IOTHUBTRANSPORT_AMQP_METHOD_HANDLE method_request_handle) +{ + size_t i; + + for (i = 0; i < amqp_methods_handle->method_request_handle_count; i++) + { + if (amqp_methods_handle->method_request_handles[i] == method_request_handle) + { + if (amqp_methods_handle->method_request_handle_count - i > 1) + { + (void)memmove(&amqp_methods_handle->method_request_handles[i], &amqp_methods_handle->method_request_handles[i + 1], + (amqp_methods_handle->method_request_handle_count - i - 1) * sizeof(IOTHUBTRANSPORT_AMQP_METHOD_HANDLE)); + } + + amqp_methods_handle->method_request_handle_count--; + i--; + } + } + + if (amqp_methods_handle->method_request_handle_count == 0) + { + free(amqp_methods_handle->method_request_handles); + amqp_methods_handle->method_request_handles = NULL; + } + else + { + IOTHUBTRANSPORT_AMQP_METHOD_HANDLE* new_handles = (IOTHUBTRANSPORT_AMQP_METHOD_HANDLE*)realloc(amqp_methods_handle->method_request_handles, amqp_methods_handle->method_request_handle_count * sizeof(IOTHUBTRANSPORT_AMQP_METHOD_HANDLE)); + if (new_handles != NULL) + { + amqp_methods_handle->method_request_handles = new_handles; + } + } +} + +IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransportamqp_methods_create(const char* hostname, const char* device_id, const char* module_id) +{ + IOTHUBTRANSPORT_AMQP_METHODS* result; + + if ((hostname == NULL) || + (device_id == NULL)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_002: [ If any argument is NULL, `iothubtransportamqp_methods_create` shall return NULL. ]*/ + result = NULL; + LogError("Bad arguments: hostname=%p, device_id=%p", hostname, device_id); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_001: [ `iothubtransportamqp_methods_create` shall instantiate a new handler for C2D methods over AMQP for device `device_id` and on success return a non-NULL handle to it. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_003: [ `iothubtransportamqp_methods_create` shall allocate memory for the new instance. ]*/ + result = malloc(sizeof(IOTHUBTRANSPORT_AMQP_METHODS)); + if (result == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_004: [ If allocating memory fails, `iothubtransportamqp_methods_create` shall return NULL. ]*/ + LogError("Cannot allocate memory for AMQP C2D methods handle"); + } + else + { + memset(result, 0, sizeof(IOTHUBTRANSPORT_AMQP_METHODS)); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_115: [ `iothubtransportamqp_methods_create` shall save the device id for later use by using `mallocAndStrcpy_s`. ]*/ + if (mallocAndStrcpy_s(&result->device_id, device_id) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_116: [ If `mallocAndStrcpy_s` fails, `iothubtransportamqp_methods_create` shall return NULL. ]*/ + LogError("Cannot copy device_id"); + free(result); + result = NULL; + } + else if ((module_id != NULL) && (mallocAndStrcpy_s(&result->module_id, module_id) != 0)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_116: [ If `mallocAndStrcpy_s` fails, `iothubtransportamqp_methods_create` shall return NULL. ]*/ + LogError("Cannot copy device_id"); + free(result->device_id); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_139: [ `iothubtransportamqp_methods_create` shall save the `hostname` for later use by using `mallocAndStrcpy_s`. ]*/ + if (mallocAndStrcpy_s(&result->hostname, hostname) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_116: [ If `mallocAndStrcpy_s` fails, `iothubtransportamqp_methods_create` shall return NULL. ]*/ + LogError("Cannot copy hostname"); + free(result->device_id); + free(result->module_id); + free(result); + result = NULL; + } + else + { + result->subscribe_state = SUBSCRIBE_STATE_NOT_SUBSCRIBED; + result->method_request_handles = NULL; + result->method_request_handle_count = 0; + result->receiver_link_disconnected = false; + result->sender_link_disconnected = false; + } + } + } + } + + return result; +} + +void iothubtransportamqp_methods_destroy(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_006: [ If `iothubtransport_amqp_methods_handle` is NULL, `iothubtransportamqp_methods_destroy` shall do nothing. ]*/ + if (iothubtransport_amqp_methods_handle == NULL) + { + LogError("NULL handle"); + } + else + { + size_t i; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_007: [ If the instance pointed to by `iothubtransport_amqp_methods_handle` is subscribed to receive C2D methods, `iothubtransportamqp_methods_destroy` shall free all resources allocated by the subscribe. ]*/ + if (iothubtransport_amqp_methods_handle->subscribe_state == SUBSCRIBE_STATE_SUBSCRIBED) + { + iothubtransportamqp_methods_unsubscribe(iothubtransport_amqp_methods_handle); + } + + for (i = 0; i < iothubtransport_amqp_methods_handle->method_request_handle_count; i++) + { + free(iothubtransport_amqp_methods_handle->method_request_handles[i]); + } + + if (iothubtransport_amqp_methods_handle->method_request_handles != NULL) + { + free(iothubtransport_amqp_methods_handle->method_request_handles); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_005: [ `iothubtransportamqp_methods_destroy` shall free all resources allocated by `iothubtransportamqp_methods_create` for the handle `iothubtransport_amqp_methods_handle`. ]*/ + free(iothubtransport_amqp_methods_handle->hostname); + free(iothubtransport_amqp_methods_handle->device_id); + free(iothubtransport_amqp_methods_handle->module_id); + free(iothubtransport_amqp_methods_handle); + } +} + +static void call_methods_unsubscribed_if_needed(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE amqp_methods_handle) +{ + if (amqp_methods_handle->receiver_link_disconnected && amqp_methods_handle->sender_link_disconnected) + { + amqp_methods_handle->receiver_link_disconnected = false; + amqp_methods_handle->sender_link_disconnected = false; + amqp_methods_handle->on_methods_unsubscribed(amqp_methods_handle->on_methods_unsubscribed_context); + } +} + +static void on_message_receiver_state_changed(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state) +{ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_119: [ When `on_message_receiver_state_changed` is called with the `new_state` being `MESSAGE_RECEIVER_STATE_ERROR`, an error shall be indicated by calling the `on_methods_error` callback passed to `iothubtransportamqp_methods_subscribe`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_126: [ For the other state changes, on_message_receiver_state_changed shall do nothing. ]*/ + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE amqp_methods_handle = (IOTHUBTRANSPORT_AMQP_METHODS_HANDLE)context; + if ((new_state != previous_state) && + (new_state == MESSAGE_RECEIVER_STATE_ERROR)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_120: [ When an error is indicated by calling the `on_methods_error`, it shall be called with the context being the `on_methods_error_context` argument passed to `iothubtransportamqp_methods_subscribe`. ]*/ + amqp_methods_handle->on_methods_error(amqp_methods_handle->on_methods_error_context); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_12_001: [ When `on_message_receiver_state_changed` is called with the `new_state` being `MESSAGE_RECEIVER_STATE_IDLE` and `previous_state` being `MESSAGE_RECEIVER_STATE_OPEN`and the sender link is already diconnected `on_message_receiver_state_changed` calls to `on_methods_unsubscribed`. ]*/ + if ((new_state == MESSAGE_RECEIVER_STATE_IDLE) && (previous_state == MESSAGE_RECEIVER_STATE_OPEN)) + { + amqp_methods_handle->receiver_link_disconnected = true; + } + call_methods_unsubscribed_if_needed(amqp_methods_handle); + } +} + +static void on_message_sender_state_changed(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state) +{ + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE amqp_methods_handle = (IOTHUBTRANSPORT_AMQP_METHODS_HANDLE)context; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_059: [ When `on_message_sender_state_changed` is called with the `new_state` being `MESSAGE_SENDER_STATE_ERROR`, an error shall be indicated by calling the `on_methods_error` callback passed to `iothubtransportamqp_methods_subscribe`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_127: [ For the other state changes, on_message_sender_state_changed shall do nothing. ]*/ + if ((new_state != previous_state) && + (new_state == MESSAGE_SENDER_STATE_ERROR)) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_120: [ When an error is indicated by calling the `on_methods_error`, it shall be called with the context being the `on_methods_error_context` argument passed to `iothubtransportamqp_methods_subscribe`. ]*/ + amqp_methods_handle->on_methods_error(amqp_methods_handle->on_methods_error_context); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_12_002: [ When `on_message_sender_state_changed` is called with the `new_state` being `MESSAGE_SENDER_STATE_IDLE` and `previous_state` being `MESSAGE_SENDER_STATE_OPEN`and the receiver link is already diconnected `on_message_sender_state_changed` calls to `on_methods_unsubscribed`. ]*/ + if ((new_state == MESSAGE_SENDER_STATE_IDLE) && (previous_state == MESSAGE_SENDER_STATE_OPEN)) + { + amqp_methods_handle->sender_link_disconnected = true; + } + call_methods_unsubscribed_if_needed(amqp_methods_handle); + } +} + +static void on_message_send_complete(void* context, MESSAGE_SEND_RESULT send_result, AMQP_VALUE delivery_state) +{ + (void)delivery_state; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_084: [ Otherwise no action shall be taken. ]*/ + if (send_result == MESSAGE_SEND_ERROR) + { + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE amqp_methods_handle = (IOTHUBTRANSPORT_AMQP_METHODS_HANDLE)context; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_083: [ If `send_result` is `MESSAGE_SEND_ERROR` then an error shall be indicated by calling the `on_methods_error` callback passed to `iothubtransportamqp_methods_subscribe`. ]*/ + amqp_methods_handle->on_methods_error(amqp_methods_handle->on_methods_error_context); + } +} + +static AMQP_VALUE on_message_received(const void* context, MESSAGE_HANDLE message) +{ + PROPERTIES_HANDLE properties; + /* VS believes this is not initialized, so have to set it to the worse case here */ + AMQP_VALUE result = NULL; + IOTHUBTRANSPORT_AMQP_METHODS_HANDLE amqp_methods_handle = (IOTHUBTRANSPORT_AMQP_METHODS_HANDLE)context; + MESSAGE_OUTCOME message_outcome; + + if (message == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_041: [ If `message` is NULL, the RELEASED outcome shall be returned and an error shall be indicated. ]*/ + LogError("NULL message"); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_043: [ When `on_message_received` is called (to indicate a new message being received over the receiver link), the message shall be processed as below: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_044: [ - The message properties shall be obtained by calling `message_get_properties`. ]*/ + if (message_get_properties(message, &properties) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_045: [ If `message_get_properties` fails, the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot retrieve message properties"); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot retrieve message properties"); + } + else + { + AMQP_VALUE correlation_id; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_046: [ - The correlation id shall be obtained by calling `properties_get_correlation_id` on the message properties. ]*/ + if (properties_get_correlation_id(properties, &correlation_id) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_047: [ If `properties_get_correlation_id` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot retrieve correlation id"); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot retrieve correlation id"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_112: [ Memory shall be allocated for the `IOTHUBTRANSPORT_AMQP_METHOD_HANDLE` to hold the correlation-id, so that it can be used in the `iothubtransportamqp_methods_respond` function. ]*/ + IOTHUBTRANSPORT_AMQP_METHOD* method_handle = (IOTHUBTRANSPORT_AMQP_METHOD*)malloc(sizeof(IOTHUBTRANSPORT_AMQP_METHOD)); + if (method_handle == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_130: [ If allocating memory for the `IOTHUBTRANSPORT_AMQP_METHOD_HANDLE` handle fails, the RELEASED outcome shall be returned and an error shall be indicated. ]*/ + LogError("Cannot allocate method handle"); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + IOTHUBTRANSPORT_AMQP_METHOD_HANDLE* new_handles; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_113: [ All `IOTHUBTRANSPORT_AMQP_METHOD_HANDLE` handles shall be tracked in an array of handles that shall be resized accordingly when a methopd handle is added to it. ]*/ + new_handles = (IOTHUBTRANSPORT_AMQP_METHOD_HANDLE*)realloc(amqp_methods_handle->method_request_handles, (amqp_methods_handle->method_request_handle_count + 1) * sizeof(IOTHUBTRANSPORT_AMQP_METHOD_HANDLE)); + if (new_handles == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_138: [ If resizing the tracked method handles array fails, the RELEASED outcome shall be returned and an error shall be indicated. ]*/ + free(method_handle); + LogError("Cannot grow method handles array"); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + amqp_methods_handle->method_request_handles = new_handles; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_121: [ The uuid value for the correlation ID shall be obtained by calling `amqpvalue_get_uuid`. ]*/ + if (amqpvalue_get_uuid(correlation_id, &method_handle->correlation_id) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_122: [ If `amqpvalue_get_uuid` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + free(method_handle); + LogError("Cannot get uuid value for correlation-id"); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot get uuid value for correlation-id"); + } + else + { + BINARY_DATA binary_data; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_048: [ - The message payload shall be obtained by calling `message_get_body_amqp_data_in_place` with the index argument being 0. ]*/ + if (message_get_body_amqp_data_in_place(message, 0, &binary_data) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_049: [ If `message_get_body_amqp_data_in_place` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + free(method_handle); + LogError("Cannot get method request message payload"); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot get method request message payload"); + } + else + { + AMQP_VALUE application_properties; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_099: [ The application properties for the received message shall be obtained by calling `message_get_application_properties`. ]*/ + if (message_get_application_properties(message, &application_properties) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_133: [ If `message_get_application_properties` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot get application properties"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot get application properties"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_123: [ The AMQP map shall be retrieve from the application properties by calling `amqpvalue_get_inplace_described_value`. ]*/ + AMQP_VALUE amqp_properties_map = amqpvalue_get_inplace_described_value(application_properties); + if (amqp_properties_map == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_134: [ If `amqpvalue_get_inplace_described_value` fails the RELEASED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot get application properties map"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_100: [ A property key `IoThub-methodname` shall be created by calling `amqpvalue_create_string`. ]*/ + AMQP_VALUE property_key = amqpvalue_create_string("IoThub-methodname"); + if (property_key == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_132: [ If `amqpvalue_create_string` fails the RELEASED outcome shall be returned. ]*/ + LogError("Cannot create the property key for method name"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_101: [ The method name property value shall be found in the map by calling `amqpvalue_get_map_value`. ]*/ + AMQP_VALUE property_value = amqpvalue_get_map_value(amqp_properties_map, property_key); + if (property_value == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_103: [ If `amqpvalue_get_map_value` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot find the IoThub-methodname property in the properties map"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot find the IoThub-methodname property in the properties map"); + } + else + { + const char* method_name; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_102: [ The string contained by the property value shall be obtained by calling `amqpvalue_get_string`. ]*/ + if (amqpvalue_get_string(property_value, &method_name) != 0) + { + /*Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_131: [ If `amqpvalue_get_string` fails the REJECTED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot read the method name from the property value"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:decode-error", "Cannot read the method name from the property value"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_056: [ On success the `on_message_received` callback shall return a newly constructed delivery state obtained by calling `messaging_delivery_accepted`. ]*/ + result = messaging_delivery_accepted(); + if (result == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_057: [ If `messaging_delivery_accepted` fails the RELEASED outcome with `amqp:decode-error` shall be returned. ]*/ + LogError("Cannot allocate memory for delivery state"); + free(method_handle); + message_outcome = MESSAGE_OUTCOME_RELEASED; + } + else + { + method_handle->iothubtransport_amqp_methods_handle = amqp_methods_handle; + + /* set the method request handle in the handle array */ + amqp_methods_handle->method_request_handles[amqp_methods_handle->method_request_handle_count] = method_handle; + amqp_methods_handle->method_request_handle_count++; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_050: [ The binary message payload shall be indicated by calling the `on_method_request_received` callback passed to `iothubtransportamqp_methods_subscribe` with the arguments: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_051: [ - `context` shall be set to the `on_method_request_received_context` argument passed to `iothubtransportamqp_methods_subscribe`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_098: [ - `method_name` shall be set to the application property value for `IoThub-methodname`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_052: [ - `request` shall be set to the payload bytes obtained by calling `message_get_body_amqp_data_in_place`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_053: [ - `request_size` shall be set to the payload size obtained by calling `message_get_body_amqp_data_in_place`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_054: [ - `method_handle` shall be set to a newly created `IOTHUBTRANSPORT_AMQP_METHOD_HANDLE` that can be passed later as an argument to `iothubtransportamqp_methods_respond`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_109: [ `iothubtransportamqp_methods_respond` shall be allowed to be called from the callback `on_method_request_received`. ]*/ + if (amqp_methods_handle->on_method_request_received(amqp_methods_handle->on_method_request_received_context, method_name, binary_data.bytes, binary_data.length, method_handle) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_147: [ If `on_method_request_received` fails, the REJECTED outcome shall be returned with `amqp:internal-error`. ]*/ + LogError("Cannot execute the callback with the given data"); + amqpvalue_destroy(result); + free(method_handle); + amqp_methods_handle->method_request_handle_count--; + message_outcome = MESSAGE_OUTCOME_REJECTED; + result = messaging_delivery_rejected("amqp:internal-error", "Cannot execute the callback with the given data"); + } + else + { + message_outcome = MESSAGE_OUTCOME_ACCEPTED; + } + } + } + + amqpvalue_destroy(property_value); + } + + amqpvalue_destroy(property_key); + } + } + + application_properties_destroy(application_properties); + } + } + } + } + } + } + + properties_destroy(properties); + } + } + + switch (message_outcome) + { + default: + break; + + case MESSAGE_OUTCOME_RELEASED: + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_129: [ The released outcome shall be created by calling `messaging_delivery_released`. ]*/ + result = messaging_delivery_released(); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_128: [ When the RELEASED outcome is returned, an error shall be indicated by calling the `on_methods_error` callback passed to `iothubtransportamqp_methods_subscribe`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_042: [ When an error is indicated by calling the `on_methods_error`, it shall be called with the context being the `on_methods_error_context` argument passed to `iothubtransportamqp_methods_subscribe`. ]*/ + amqp_methods_handle->on_methods_error(amqp_methods_handle->on_methods_error_context); + break; + + case MESSAGE_OUTCOME_REJECTED: + case MESSAGE_OUTCOME_ACCEPTED: + /* all is well */ + break; + } + + return result; +} + +STRING_HANDLE create_correlation_id(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + if (iothubtransport_amqp_methods_handle->module_id != NULL) + { + return STRING_construct_sprintf("%s/%s", iothubtransport_amqp_methods_handle->device_id, iothubtransport_amqp_methods_handle->module_id); + } + else + { + return STRING_construct(iothubtransport_amqp_methods_handle->device_id); + } +} + +STRING_HANDLE create_peer_endpoint_name(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + if (iothubtransport_amqp_methods_handle->module_id != NULL) + { + return STRING_construct_sprintf("amqps://%s/devices/%s/modules/%s/methods/devicebound", iothubtransport_amqp_methods_handle->hostname, iothubtransport_amqp_methods_handle->device_id, iothubtransport_amqp_methods_handle->module_id); + } + else + { + return STRING_construct_sprintf("amqps://%s/devices/%s/methods/devicebound", iothubtransport_amqp_methods_handle->hostname, iothubtransport_amqp_methods_handle->device_id); + } +} + +STRING_HANDLE create_requests_link_name(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + if (iothubtransport_amqp_methods_handle->module_id != NULL) + { + return STRING_construct_sprintf("methods_requests_link-%s/%s", iothubtransport_amqp_methods_handle->device_id, iothubtransport_amqp_methods_handle->module_id); + } + else + { + return STRING_construct_sprintf("methods_requests_link-%s", iothubtransport_amqp_methods_handle->device_id); + } +} + +STRING_HANDLE create_responses_link_name(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + if (iothubtransport_amqp_methods_handle->module_id != NULL) + { + return STRING_construct_sprintf("methods_responses_link-%s/%s", iothubtransport_amqp_methods_handle->device_id, iothubtransport_amqp_methods_handle->module_id); + } + else + { + return STRING_construct_sprintf("methods_responses_link-%s", iothubtransport_amqp_methods_handle->device_id); + } +} + + + +static int set_link_attach_properties(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + int result = 0; + fields link_attach_properties; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_140: [ A link attach properties map shall be created by calling `amqpvalue_create_map`. ]*/ + link_attach_properties = amqpvalue_create_map(); + if (link_attach_properties == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the map for link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_141: [ A property key which shall be a symbol named `com.microsoft:channel-correlation-id` shall be created by calling `amqp_create_symbol`. ]*/ + AMQP_VALUE channel_correlation_id_property_key = amqpvalue_create_symbol("com.microsoft:channel-correlation-id"); + if (channel_correlation_id_property_key == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the channel correlation id property key for the link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_142: [ A property value of type string that shall contain the device id (and "/" + module id if module is present) shall be created by calling `amqpvalue_create_string`. ]*/ + STRING_HANDLE correlation_id = NULL; + AMQP_VALUE channel_correlation_id_property_value = NULL; + + if ((correlation_id = create_correlation_id(iothubtransport_amqp_methods_handle)) == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the channel correlation id string for the link attach properties"); + result = __FAILURE__; + } + else if ((channel_correlation_id_property_value = amqpvalue_create_string(STRING_c_str(correlation_id))) == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the channel correlation id property key for the link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_143: [ The `com.microsoft:channel-correlation-id` shall be added to the link attach properties by calling `amqpvalue_set_map_value`. ]*/ + if (amqpvalue_set_map_value(link_attach_properties, channel_correlation_id_property_key, channel_correlation_id_property_value) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the property for channel correlation on the link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_150: [ A property key which shall be a symbol named `com.microsoft:api-version` shall be created by calling `amqp_create_symbol`. ]*/ + AMQP_VALUE api_version_property_key = amqpvalue_create_symbol("com.microsoft:api-version"); + if (api_version_property_key == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the API version property key for the link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_151: [ A property value of type string that shall contain the `2016-11-14` shall be created by calling `amqpvalue_create_string`. ]*/ + AMQP_VALUE api_version_property_value = amqpvalue_create_string("2016-11-14"); + if (api_version_property_value == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the API version property value for the link attach properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_152: [ The `com.microsoft:api-version` shall be added to the link attach properties by calling `amqpvalue_set_map_value`. ]*/ + if (amqpvalue_set_map_value(link_attach_properties, api_version_property_key, api_version_property_value) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the property for API version on the link attach properties"); + result = __FAILURE__; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_144: [ The link attach properties shall be set on the receiver and sender link by calling `link_set_attach_properties`. ]*/ + else if (link_set_attach_properties(iothubtransport_amqp_methods_handle->sender_link, link_attach_properties) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the link attach properties on the sender link"); + result = __FAILURE__; + } + else if (link_set_attach_properties(iothubtransport_amqp_methods_handle->receiver_link, link_attach_properties) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_145: [ If any call for creating or setting the link attach properties fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the link attach properties on the receiver link"); + result = __FAILURE__; + } + else + { + result = 0; + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_146: [ The link attach properties and all associated values shall be freed by calling `amqpvalue_destroy` after setting the link attach properties. ]*/ + amqpvalue_destroy(api_version_property_value); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_146: [ The link attach properties and all associated values shall be freed by calling `amqpvalue_destroy` after setting the link attach properties. ]*/ + amqpvalue_destroy(api_version_property_key); + } + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_146: [ The link attach properties and all associated values shall be freed by calling `amqpvalue_destroy` after setting the link attach properties. ]*/ + amqpvalue_destroy(channel_correlation_id_property_value); + } + STRING_delete(correlation_id); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_146: [ The link attach properties and all associated values shall be freed by calling `amqpvalue_destroy` after setting the link attach properties. ]*/ + amqpvalue_destroy(channel_correlation_id_property_key); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_146: [ The link attach properties and all associated values shall be freed by calling `amqpvalue_destroy` after setting the link attach properties. ]*/ + amqpvalue_destroy(link_attach_properties); + } + + return result; +} + +int iothubtransportamqp_methods_subscribe(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle, + SESSION_HANDLE session_handle, ON_METHODS_ERROR on_methods_error, void* on_methods_error_context, + ON_METHOD_REQUEST_RECEIVED on_method_request_received, void* on_method_request_received_context, + ON_METHODS_UNSUBSCRIBED on_methods_unsubscribed, void* on_methods_unsubscribed_context) +{ + int result; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_009: [ If any of the argument `iothubtransport_amqp_methods_handle`, `session_handle`, `on_methods_error`, `on_method_request_received`, `on_methods_unsubscribed` is NULL, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + if ((iothubtransport_amqp_methods_handle == NULL) || + (session_handle == NULL) || + (on_methods_error == NULL) || + (on_method_request_received == NULL) || + (on_methods_unsubscribed == NULL)) + { + LogError("Invalid arguments: iothubtransport_amqp_methods_handle=%p, session_handle=%p, on_methods_error=%p, on_method_request_received=%p, on_methods_unsubscribed=%p", + iothubtransport_amqp_methods_handle, session_handle, on_methods_error, on_method_request_received, on_methods_unsubscribed); + result = __FAILURE__; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_137: [ `iothubtransportamqp_methods_subscribe` after another succesfull `iothubtransportamqp_methods_subscribe` without any unsubscribe shall return a non-zero value without performing any subscribe action. ]*/ + else if (iothubtransport_amqp_methods_handle->subscribe_state != SUBSCRIBE_STATE_NOT_SUBSCRIBED) + { + LogError("Already subscribed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_015: [ The address string used to create the source shall be of the form `/devices/{device id}` + (`/modules/{module id}` if modules are present) + `/methods/devicebound`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_016: [ The string shall be created by using `STRING_construct_sprintf`. ]*/ + STRING_HANDLE peer_endpoint_string = create_peer_endpoint_name(iothubtransport_amqp_methods_handle); + if (peer_endpoint_string == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_018: [ If `STRING_construct_sprintf` fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + result = __FAILURE__; + } + else + { + iothubtransport_amqp_methods_handle->on_method_request_received = on_method_request_received; + iothubtransport_amqp_methods_handle->on_method_request_received_context = on_method_request_received_context; + iothubtransport_amqp_methods_handle->on_methods_error = on_methods_error; + iothubtransport_amqp_methods_handle->on_methods_error_context = on_methods_error_context; + iothubtransport_amqp_methods_handle->on_methods_unsubscribed = on_methods_unsubscribed; + iothubtransport_amqp_methods_handle->on_methods_unsubscribed_context = on_methods_unsubscribed_context; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_014: [ - `source` shall be the a source value created by calling `messaging_create_source`. ]*/ + AMQP_VALUE receiver_source = messaging_create_source(STRING_c_str(peer_endpoint_string)); + if (receiver_source == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_019: [ If creating the target or source values fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create receiver source"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_017: [ - `target` shall be the a target value created by calling `messaging_create_target`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_117: [ The address string used to create the target shall be `requests`. ]*/ + AMQP_VALUE receiver_target = messaging_create_target("requests"); + if (receiver_target == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_019: [ If creating the target or source values fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create receiver target"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_012: [ - `name` shall be in the format `methods_requests_link-{device_id}` (+ `/{module-id}` if module id is present). ]*/ + STRING_HANDLE requests_link_name = create_requests_link_name(iothubtransport_amqp_methods_handle); + if (requests_link_name == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_153: [ If constructing the requests link name fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create methods requests link name."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_010: [ `iothubtransportamqp_methods_subscribe` shall create a receiver link by calling `link_create` with the following arguments: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_011: [ - `session_handle` shall be the session_handle argument passed to iothubtransportamqp_methods_subscribe ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_013: [ - `role` shall be role_receiver. ]*/ + iothubtransport_amqp_methods_handle->receiver_link = link_create(session_handle, STRING_c_str(requests_link_name), role_receiver, receiver_source, receiver_target); + if (iothubtransport_amqp_methods_handle->receiver_link == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_020: [ If creating the receiver link fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create receiver link"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_025: [ - `source` shall be the a source value created by calling `messaging_create_source`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_026: [ The address string used to create the target shall be `responses`. ]*/ + AMQP_VALUE sender_source = messaging_create_source("responses"); + if (sender_source == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_031: [ If creating the target or source values fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create sender source"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_027: [ - `target` shall be the a target value created by calling `messaging_create_target`. ]*/ + AMQP_VALUE sender_target = messaging_create_target(STRING_c_str(peer_endpoint_string)); + if (sender_target == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_031: [ If creating the target or source values fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create sender target"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_023: [ - `name` shall be format `methods_responses_link-{device_id}` (+ `/{module-id}` if module id is present). ]*/ + STRING_HANDLE responses_link_name = create_responses_link_name(iothubtransport_amqp_methods_handle); + if (responses_link_name == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_154: [ If constructing the responses link name fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create methods responses link name."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_021: [ `iothubtransportamqp_methods_subscribe` shall create a sender link by calling `link_create` with the following arguments: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_022: [ - `session_handle` shall be the session_handle argument passed to iothubtransportamqp_methods_subscribe ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_024: [ - `role` shall be role_sender. ]*/ + iothubtransport_amqp_methods_handle->sender_link = link_create(session_handle, STRING_c_str(responses_link_name), role_sender, sender_source, sender_target); + if (iothubtransport_amqp_methods_handle->sender_link == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_032: [ If creating the receiver link fails `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create sender link"); + result = __FAILURE__; + } + else + { + if (set_link_attach_properties(iothubtransport_amqp_methods_handle) != 0) + { + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_033: [ `iothubtransportamqp_methods_subscribe` shall create a message receiver associated with the receiver link by calling `messagereceiver_create` and passing the receiver link handle to it. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_118: [ An `on_message_receiver_state_changed` callback together with its context shall be passed to `messagereceiver_create`. ]*/ + iothubtransport_amqp_methods_handle->message_receiver = messagereceiver_create(iothubtransport_amqp_methods_handle->receiver_link, on_message_receiver_state_changed, iothubtransport_amqp_methods_handle); + if (iothubtransport_amqp_methods_handle->message_receiver == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_034: [ If `messagereceiver_create` fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create message receiver"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_035: [ `iothubtransportamqp_methods_subscribe` shall create a message sender associated with the sender link by calling `messagesender_create` and passing the sender link handle to it. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_106: [ An `on_message_sender_state_changed` callback together with its context shall be passed to `messagesender_create`. ]*/ + iothubtransport_amqp_methods_handle->message_sender = messagesender_create(iothubtransport_amqp_methods_handle->sender_link, on_message_sender_state_changed, iothubtransport_amqp_methods_handle); + if (iothubtransport_amqp_methods_handle->message_sender == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_036: [ If `messagesender_create` fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot create message sender"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_037: [ `iothubtransportamqp_methods_subscribe` shall open the message sender by calling `messagesender_open`. ]*/ + if (messagesender_open(iothubtransport_amqp_methods_handle->message_sender) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_038: [ If `messagesender_open` fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot open the message sender"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_039: [ `iothubtransportamqp_methods_subscribe` shall open the message sender by calling `messagereceiver_open`. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_104: [ An `on_message_received` callback together with its context shall be passed to `messagereceiver_open`. ]*/ + if (messagereceiver_open(iothubtransport_amqp_methods_handle->message_receiver, on_message_received, iothubtransport_amqp_methods_handle) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_040: [ If `messagereceiver_open` fails, `iothubtransportamqp_methods_subscribe` shall fail and return a non-zero value. ]*/ + LogError("Cannot open the message receiver"); + result = __FAILURE__; + } + else + { + iothubtransport_amqp_methods_handle->subscribe_state = SUBSCRIBE_STATE_SUBSCRIBED; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_008: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + } + } + } + + STRING_delete(responses_link_name); + } + + amqpvalue_destroy(sender_target); + } + + amqpvalue_destroy(sender_source); + } + } + + STRING_delete(requests_link_name); + } + + amqpvalue_destroy(receiver_target); + } + + amqpvalue_destroy(receiver_source); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_105: [ The string created in order to hold the source and target addresses shall be freed by calling `STRING_delete`. ]*/ + STRING_delete(peer_endpoint_string); + } + } + + return result; +} + +void iothubtransportamqp_methods_unsubscribe(IOTHUBTRANSPORT_AMQP_METHODS_HANDLE iothubtransport_amqp_methods_handle) +{ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_072: [ If the argument `iothubtransport_amqp_methods_handle` is NULL, `iothubtransportamqp_methods_unsubscribe` shall do nothing. ]*/ + if (iothubtransport_amqp_methods_handle == NULL) + { + LogError("NULL handle"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_135: [ If subscribe was not called yet, `iothubtransportamqp_methods_unsubscribe` shall do nothing. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_136: [ `iothubtransportamqp_methods_unsubscribe` after `iothubtransportamqp_methods_unsubscribe` shall do nothing. ]*/ + if (iothubtransport_amqp_methods_handle->subscribe_state != SUBSCRIBE_STATE_SUBSCRIBED) + { + LogError("unsubscribe called while not subscribed"); + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_073: [ Otherwise `iothubtransportamqp_methods_unsubscribe` shall free all resources allocated in `iothubtransportamqp_methods_subscribe`: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_075: [ - It shall free the message receiver by calling `messagereceiver_destroy'. ]*/ + messagereceiver_destroy(iothubtransport_amqp_methods_handle->message_receiver); + iothubtransport_amqp_methods_handle->message_receiver = NULL; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_074: [ - It shall free the message sender by calling `messagesender_destroy'. ]*/ + messagesender_destroy(iothubtransport_amqp_methods_handle->message_sender); + iothubtransport_amqp_methods_handle->message_sender = NULL; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_076: [ - It shall free the sender link by calling `link_destroy'. ]*/ + link_destroy(iothubtransport_amqp_methods_handle->sender_link); + iothubtransport_amqp_methods_handle->sender_link = NULL; + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_077: [ - It shall free the receiver link by calling `link_destroy'. ]*/ + link_destroy(iothubtransport_amqp_methods_handle->receiver_link); + iothubtransport_amqp_methods_handle->receiver_link = NULL; + + iothubtransport_amqp_methods_handle->subscribe_state = SUBSCRIBE_STATE_NOT_SUBSCRIBED; + } + } +} + +int iothubtransportamqp_methods_respond(IOTHUBTRANSPORT_AMQP_METHOD_HANDLE method_handle, const unsigned char* response, size_t response_size, int status_code) +{ + int result; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_107: [ If the argument `method_handle` is NULL, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + if (method_handle == NULL) + { + LogError("NULL method handle"); + result = __FAILURE__; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_108: [ If `response_size` is greater than zero and `response` is NULL, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + else if ((response == NULL) && (response_size > 0)) + { + LogError("NULL response buffer with > 0 response payload size"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_061: [ - A new uAMQP message shall be created by calling `message_create`. ]*/ + MESSAGE_HANDLE message = message_create(); + if (message == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_062: [ If the `message_create` call fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create message"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_063: [ - A new properties handle shall be created by calling `properties_create`. ]*/ + PROPERTIES_HANDLE properties = properties_create(); + if (properties == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_064: [ If the `properties_create call` fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_124: [ - An AMQP value holding the correlation id associated with the `method_handle` handle shall be created by calling `amqpvalue_create_uuid`. ]*/ + AMQP_VALUE correlation_id = amqpvalue_create_uuid(method_handle->correlation_id); + if (correlation_id == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_125: [ If `amqpvalue_create_uuid` fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create correlation_id value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_065: [ - The correlation id on the message properties shall be set by calling `properties_set_correlation_id` and passing as argument the already create correlation ID AMQP value. ]*/ + if (properties_set_correlation_id(properties, correlation_id) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_066: [ If the `properties_set_correlation_id` call fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot set correlation_id on the properties"); + result = __FAILURE__; + } + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_148: [ The properties shall be set on the message by calling `message_set_properties`. ]*/ + else if (message_set_properties(message, properties) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_149: [ If `message_set_properties` fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot set properties on the response message"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_090: [ An AMQP map shall be created to hold the application properties for the response by calling `amqpvalue_create_map`. ]*/ + AMQP_VALUE application_properties_map = amqpvalue_create_map(); + if (application_properties_map == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_096: [ If any of the calls `amqpvalue_create_string`, `amqpvalue_create_int`, `amqpvalue_create_map`, `amqpvalue_set_map_value` or `message_set_application_properties` fails `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create map for application properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_091: [ A property key `IoThub-status` shall be created by calling `amqpvalue_create_string`. ]*/ + AMQP_VALUE property_key_status = amqpvalue_create_string("IoThub-status"); + if (property_key_status == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_096: [ If any of the calls `amqpvalue_create_string`, `amqpvalue_create_int`, `amqpvalue_create_map`, `amqpvalue_set_map_value` or `message_set_application_properties` fails `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the property key for the status property"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_097: [ A property value of type int shall be created from the `status_code` argument by calling `amqpvalue_create_int`. ] ]*/ + AMQP_VALUE property_value_status = amqpvalue_create_int(status_code); + if (property_value_status == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_096: [ If any of the calls `amqpvalue_create_string`, `amqpvalue_create_int`, `amqpvalue_create_map`, `amqpvalue_set_map_value` or `message_set_application_properties` fails `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot create the status code property value for the application properties map"); + result = __FAILURE__; + } + else + { + /* Cdoes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_093: [ A new entry shall be added in the application properties map by calling `amqpvalue_set_map_value` and passing the key and value that were previously created. ]*/ + if (amqpvalue_set_map_value(application_properties_map, property_key_status, property_value_status) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_096: [ If any of the calls `amqpvalue_create_string`, `amqpvalue_create_int`, `amqpvalue_create_map`, `amqpvalue_set_map_value` or `message_set_application_properties` fails `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot add the status property to the application properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_094: [ The application properties map shall be set on the response message by calling `message_set_application_properties`. ]*/ + if (message_set_application_properties(message, application_properties_map) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_096: [ If any of the calls `amqpvalue_create_string`, `amqpvalue_create_int`, `amqpvalue_create_map`, `amqpvalue_set_map_value` or `message_set_application_properties` fails `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the application properties on the message"); + result = __FAILURE__; + } + else + { + BINARY_DATA binary_data; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_079: [ The field `bytes` of the `binary_data` argument shall be set to the `response` argument value. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_080: [ The field `length` of the `binary_data` argument shall be set to the `response_size` argument value. ]*/ + binary_data.bytes = response; + binary_data.length = response_size; + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_078: [ The binary payload for the response shall be set by calling `message_add_body_amqp_data` for the newly created message handle. ]*/ + if (message_add_body_amqp_data(message, binary_data) != 0) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_081: [ If the `message_add_body_amqp_data` call fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot set the response payload on the reponse message"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_067: [ The message shall be handed over to the message_sender by calling `messagesender_send` and passing as arguments: ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_068: [ - The response message handle. ]*/ + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_069: [ - A send callback and its context for the `on_message_send_complete` callback. ]*/ + if (messagesender_send_async(method_handle->iothubtransport_amqp_methods_handle->message_sender, message, on_message_send_complete, method_handle->iothubtransport_amqp_methods_handle, 0) == NULL) + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_071: [ If the `messagesender_send` call fails, `iothubtransportamqp_methods_respond` shall fail and return a non-zero value. ]*/ + LogError("Cannot send response message"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_114: [ The handle `method_handle` shall be removed from the array used to track the method handles. ]*/ + remove_tracked_handle(method_handle->iothubtransport_amqp_methods_handle, method_handle); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_111: [ The handle `method_handle` shall be freed (have no meaning) after `iothubtransportamqp_methods_respond` has been executed. ]*/ + free(method_handle); + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_060: [ `iothubtransportamqp_methods_respond` shall construct a response message and on success it shall return 0. ]*/ + result = 0; + } + } + } + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + amqpvalue_destroy(property_value_status); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + amqpvalue_destroy(property_key_status); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + amqpvalue_destroy(application_properties_map); + } + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + amqpvalue_destroy(correlation_id); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + properties_destroy(properties); + } + + /* Codes_SRS_IOTHUBTRANSPORT_AMQP_METHODS_01_095: [ The application property map and all intermediate values shall be freed after being passed to `message_set_application_properties`. ]*/ + message_destroy(message); + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransportamqp_websockets.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,271 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "iothubtransportamqp_websockets.h" +#include "azure_c_shared_utility/wsio.h" +#include "internal/iothubtransport_amqp_common.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/http_proxy_io.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/shared_util_options.h" + +#define DEFAULT_WS_PROTOCOL_NAME "AMQPWSB10" +#define DEFAULT_WS_RELATIVE_PATH "/$iothub/websocket" +#define DEFAULT_WS_PORT 443 + +static XIO_HANDLE getWebSocketsIOTransport(const char* fqdn, const AMQP_TRANSPORT_PROXY_OPTIONS* amqp_transport_proxy_options) +{ + WSIO_CONFIG ws_io_config; + TLSIO_CONFIG tls_io_config; + HTTP_PROXY_IO_CONFIG http_proxy_io_config; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_001: [ `getIoTransportProvider` shall obtain the WebSocket IO interface handle by calling `wsio_get_interface_description`. ]*/ + const IO_INTERFACE_DESCRIPTION* io_interface_description = wsio_get_interface_description(); + XIO_HANDLE result; + + if (io_interface_description == NULL) + { + LogError("Failure constructing the provider interface"); + /* Codes_SRS_IoTHubTransportAMQP_WS_09_003: [If `io_interface_description` is NULL getWebSocketsIOTransport shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_003: [ - `hostname` shall be set to `fqdn`. ]*/ + ws_io_config.hostname = fqdn; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_004: [ - `port` shall be set to 443. ]*/ + ws_io_config.port = DEFAULT_WS_PORT; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_005: [ - `protocol` shall be set to `AMQPWSB10`. ]*/ + ws_io_config.protocol = DEFAULT_WS_PROTOCOL_NAME; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_006: [ - `resource_name` shall be set to `/$iothub/websocket`. ]*/ + ws_io_config.resource_name = DEFAULT_WS_RELATIVE_PATH; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_007: [ - `underlying_io_interface` shall be set to the TLS IO interface description. ]*/ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_009: [ `getIoTransportProvider` shall obtain the TLS IO interface handle by calling `platform_get_default_tlsio`. ]*/ + ws_io_config.underlying_io_interface = platform_get_default_tlsio(); + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_029: [ If `platform_get_default_tlsio` returns NULL, NULL shall be set in the WebSocket IO parameters structure for the interface description and parameters. ]*/ + if (ws_io_config.underlying_io_interface == NULL) + { + ws_io_config.underlying_io_parameters = NULL; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_008: [ - `underlying_io_parameters` shall be set to the TLS IO arguments. ]*/ + ws_io_config.underlying_io_parameters = &tls_io_config; + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_010: [ The TLS IO parameters shall be a `TLSIO_CONFIG` structure filled as below: ]*/ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_011: [ - `hostname` shall be set to `fqdn`. ]*/ + tls_io_config.hostname = fqdn; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_012: [ - `port` shall be set to 443. ]*/ + tls_io_config.port = DEFAULT_WS_PORT; + + if (amqp_transport_proxy_options != NULL) + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_015: [ - If `amqp_transport_proxy_options` is not NULL, `underlying_io_interface` shall be set to the HTTP proxy IO interface description. ]*/ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_022: [ `getIoTransportProvider` shall obtain the HTTP proxy IO interface handle by calling `http_proxy_io_get_interface_description`. ]*/ + tls_io_config.underlying_io_interface = http_proxy_io_get_interface_description(); + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_028: [ If `http_proxy_io_get_interface_description` returns NULL, NULL shall be set in the TLS IO parameters structure for the interface description and parameters. ]*/ + if (tls_io_config.underlying_io_interface == NULL) + { + tls_io_config.underlying_io_parameters = NULL; + } + else + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_016: [ - If `amqp_transport_proxy_options` is not NULL `underlying_io_parameters` shall be set to the HTTP proxy IO arguments. ]*/ + tls_io_config.underlying_io_parameters = &http_proxy_io_config; + + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_023: [ The HTTP proxy IO arguments shall be an `HTTP_PROXY_IO_CONFIG` structure, filled as below: ]*/ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_026: [ - `proxy_hostname`, `proxy_port`, `username` and `password` shall be copied from the `mqtt_transport_proxy_options` argument. ]*/ + http_proxy_io_config.proxy_hostname = amqp_transport_proxy_options->host_address; + http_proxy_io_config.proxy_port = amqp_transport_proxy_options->port; + http_proxy_io_config.username = amqp_transport_proxy_options->username; + http_proxy_io_config.password = amqp_transport_proxy_options->password; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_024: [ - `hostname` shall be set to `fully_qualified_name`. ]*/ + http_proxy_io_config.hostname = fqdn; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_025: [ - `port` shall be set to 443. ]*/ + http_proxy_io_config.port = DEFAULT_WS_PORT; + } + } + else + { + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_013: [ - If `amqp_transport_proxy_options` is NULL, `underlying_io_interface` shall be set to NULL. ]*/ + tls_io_config.underlying_io_interface = NULL; + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_014: [ - If `amqp_transport_proxy_options` is NULL `underlying_io_parameters` shall be set to NULL. ]*/ + tls_io_config.underlying_io_parameters = NULL; + } + } + + /* Codes_SRS_IoTHubTransportAMQP_WS_09_004: [getWebSocketsIOTransport shall return the XIO_HANDLE created using xio_create().] */ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_WS_01_002: [ `getIoTransportProvider` shall call `xio_create` while passing the WebSocket IO interface description to it and the WebSocket configuration as a WSIO_CONFIG structure, filled as below: ]*/ + result = xio_create(io_interface_description, &ws_io_config); + } + + return result; +} + +// API functions +static TRANSPORT_LL_HANDLE IoTHubTransportAMQP_WS_Create(const IOTHUBTRANSPORT_CONFIG* config) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_001: [IoTHubTransportAMQP_WS_Create shall create a TRANSPORT_LL_HANDLE by calling into the IoTHubTransport_AMQP_Common_Create function, passing `config` and getWebSocketsIOTransport.] + return IoTHubTransport_AMQP_Common_Create(config, getWebSocketsIOTransport); +} + +static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportAMQP_WS_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_014: [IoTHubTransportAMQP_WS_ProcessItem shall invoke IoTHubTransport_AMQP_Common_ProcessItem() and return its result.] + return IoTHubTransport_AMQP_Common_ProcessItem(handle, item_type, iothub_item); +} + +static void IoTHubTransportAMQP_WS_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_015: [IoTHubTransportAMQP_WS_DoWork shall call into the IoTHubTransport_AMQP_Common_DoWork()] + IoTHubTransport_AMQP_Common_DoWork(handle, iotHubClientHandle); +} + +static int IoTHubTransportAMQP_WS_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_012: [IoTHubTransportAMQP_WS_Subscribe shall subscribe for D2C messages by calling into the IoTHubTransport_AMQP_Common_Subscribe().] + return IoTHubTransport_AMQP_Common_Subscribe(handle); +} + +static void IoTHubTransportAMQP_WS_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_013: [IoTHubTransportAMQP_WS_Unsubscribe shall subscribe for D2C messages by calling into the IoTHubTransport_AMQP_Common_Unsubscribe().] + IoTHubTransport_AMQP_Common_Unsubscribe(handle); +} + +static int IoTHubTransportAMQP_WS_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_008: [IoTHubTransportAMQP_WS_Subscribe_DeviceTwin shall invoke IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin() and return its result.] + return IoTHubTransport_AMQP_Common_Subscribe_DeviceTwin(handle); +} + +static void IoTHubTransportAMQP_WS_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_009: [IoTHubTransportAMQP_WS_Unsubscribe_DeviceTwin shall invoke IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin()] + IoTHubTransport_AMQP_Common_Unsubscribe_DeviceTwin(handle); +} + +static int IoTHubTransportAMQP_WS_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_010: [IoTHubTransportAMQP_WS_Subscribe_DeviceMethod shall invoke IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod() and return its result.] + return IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod(handle); +} + +static void IoTHubTransportAMQP_WS_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_011: [IoTHubTransportAMQP_WS_Unsubscribe_DeviceMethod shall invoke IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod()] + IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod(handle); +} + +static int IoTHubTransportAMQP_WS_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + return IoTHubTransport_AMQP_Common_DeviceMethod_Response(handle, methodId, response, response_size, status_response); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_WS_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_016: [IoTHubTransportAMQP_WS_GetSendStatus shall get the send status by calling into the IoTHubTransport_AMQP_Common_GetSendStatus()] + return IoTHubTransport_AMQP_Common_GetSendStatus(handle, iotHubClientStatus); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_WS_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_017: [IoTHubTransportAMQP_WS_SetOption shall set the options by calling into the IoTHubTransport_AMQP_Common_SetOption()] + return IoTHubTransport_AMQP_Common_SetOption(handle, option, value); +} + +static IOTHUB_DEVICE_HANDLE IoTHubTransportAMQP_WS_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_006: [IoTHubTransportAMQP_WS_Register shall register the device by calling into the IoTHubTransport_AMQP_Common_Register().] + return IoTHubTransport_AMQP_Common_Register(handle, device, iotHubClientHandle, waitingToSend); +} + +static void IoTHubTransportAMQP_WS_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_007: [IoTHubTransportAMQP_WS_Unregister shall unregister the device by calling into the IoTHubTransport_AMQP_Common_Unregister().] + IoTHubTransport_AMQP_Common_Unregister(deviceHandle); +} + +static void IoTHubTransportAMQP_WS_Destroy(TRANSPORT_LL_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_005: [IoTHubTransportAMQP_WS_Destroy shall destroy the TRANSPORT_LL_HANDLE by calling into the IoTHubTransport_AMQP_Common_Destroy().] + IoTHubTransport_AMQP_Common_Destroy(handle); +} + +static int IoTHubTransportAMQP_WS_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + return IoTHubTransport_AMQP_Common_SetRetryPolicy(handle, retryPolicy, retryTimeoutLimitInSeconds); +} + +static STRING_HANDLE IoTHubTransportAMQP_WS_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_09_018: [IoTHubTransportAMQP_WS_GetHostname shall get the hostname by calling into the IoTHubTransport_AMQP_Common_GetHostname()] + return IoTHubTransport_AMQP_Common_GetHostname(handle); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_WS_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + // Codes_SRS_IoTHubTransportAMQP_WS_10_001[**IoTHubTransportAMQP_WS_SendMessageDisposition shall sned the message disposition by calling into the IoTHubTransport_AMQP_Common_SendMessageDisposition()] + return IoTHubTransport_AMQP_Common_SendMessageDisposition(message_data, disposition); +} + +static int IotHubTransportAMQP_WS_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("AMQP WS does not support input queues"); + return (int)-1; +} + +static void IotHubTransportAMQP_WS_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("AMQP WS does not support input queues"); +} + +static TRANSPORT_PROVIDER thisTransportProvider_WebSocketsOverTls = +{ + IoTHubTransportAMQP_WS_SendMessageDisposition, /*pfIotHubTransport_Send_Message_Disposition IoTHubTransport_Send_Message_Disposition;*/ + IoTHubTransportAMQP_WS_Subscribe_DeviceMethod, /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/ + IoTHubTransportAMQP_WS_Unsubscribe_DeviceMethod, /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/ + IoTHubTransportAMQP_WS_DeviceMethod_Response, + IoTHubTransportAMQP_WS_Subscribe_DeviceTwin, /*pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin;*/ + IoTHubTransportAMQP_WS_Unsubscribe_DeviceTwin, /*pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin;*/ + IoTHubTransportAMQP_WS_ProcessItem, /*pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem;*/ + IoTHubTransportAMQP_WS_GetHostname, /*pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname;*/ + IoTHubTransportAMQP_WS_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption;*/ + IoTHubTransportAMQP_WS_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create;*/ + IoTHubTransportAMQP_WS_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy;*/ + IoTHubTransportAMQP_WS_Register, /*pfIotHubTransport_Register IoTHubTransport_Register;*/ + IoTHubTransportAMQP_WS_Unregister, /*pfIotHubTransport_Unregister IoTHubTransport_Unegister;*/ + IoTHubTransportAMQP_WS_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe;*/ + IoTHubTransportAMQP_WS_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe;*/ + IoTHubTransportAMQP_WS_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork;*/ + IoTHubTransportAMQP_WS_SetRetryPolicy, /*pfIoTHubTransport_SetRetryLogic IoTHubTransport_SetRetryPolicy;*/ + IoTHubTransportAMQP_WS_GetSendStatus, /*pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus;*/ + IotHubTransportAMQP_WS_Subscribe_InputQueue, /*pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; */ + IotHubTransportAMQP_WS_Unsubscribe_InputQueue /*pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue; */ +}; + +/* Codes_SRS_IoTHubTransportAMQP_WS_09_019: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for it's fields: +IoTHubTransport_Send_Message_Disposition = IoTHubTransportAMQP_Send_Message_Disposition +IoTHubTransport_Subscribe_DeviceMethod = IoTHubTransportAMQP_WS_Subscribe_DeviceMethod +IoTHubTransport_Unsubscribe_DeviceMethod = IoTHubTransportAMQP_WS_Unsubscribe_DeviceMethod +IoTHubTransport_Subscribe_DeviceTwin = IoTHubTransportAMQP_WS_Subscribe_DeviceTwin +IoTHubTransport_Unsubscribe_DeviceTwin = IoTHubTransportAMQP_WS_Unsubscribe_DeviceTwin +IoTHubTransport_ProcessItem - IoTHubTransportAMQP_WS_ProcessItem +IoTHubTransport_GetHostname = IoTHubTransportAMQP_WS_GetHostname +IoTHubTransport_Create = IoTHubTransportAMQP_WS_Create +IoTHubTransport_Destroy = IoTHubTransportAMQP_WS_Destroy +IoTHubTransport_Register = IoTHubTransportAMQP_WS_Register +IoTHubTransport_Unregister = IoTHubTransportAMQP_WS_Unregister +IoTHubTransport_Subscribe = IoTHubTransportAMQP_WS_Subscribe +IoTHubTransport_Unsubscribe = IoTHubTransportAMQP_WS_Unsubscribe +IoTHubTransport_DoWork = IoTHubTransportAMQP_WS_DoWork +IoTHubTransport_SetRetryLogic = IoTHubTransportAMQP_WS_SetRetryLogic +IoTHubTransport_SetOption = IoTHubTransportAMQP_WS_SetOption +IoTHubTransport_GetSendStatus = IoTHubTransportAMQP_WS_GetSendStatus] */ +extern const TRANSPORT_PROVIDER* AMQP_Protocol_over_WebSocketsTls(void) +{ + return &thisTransportProvider_WebSocketsOverTls; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransporthttp.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2516 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include <time.h> +#include "iothub_client_options.h" +#include "iothub_client_version.h" +#include "internal/iothub_client_private.h" +#include "iothub_transport_ll.h" +#include "iothubtransporthttp.h" +#include "internal/iothubtransport.h" + +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/urlencode.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/doublylinkedlist.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/httpheaders.h" +#include "azure_c_shared_utility/agenttime.h" + +#define IOTHUB_APP_PREFIX "iothub-app-" +static const char* IOTHUB_MESSAGE_ID = "iothub-messageid"; +static const char* IOTHUB_CORRELATION_ID = "iothub-correlationid"; +static const char* IOTHUB_CONTENT_TYPE_D2C = "iothub-contenttype"; +static const char* IOTHUB_CONTENT_ENCODING_D2C = "iothub-contentencoding"; +static const char* IOTHUB_CONTENT_TYPE_C2D = "ContentType"; +static const char* IOTHUB_CONTENT_ENCODING_C2D = "ContentEncoding"; + +#define CONTENT_TYPE "Content-Type" +#define APPLICATION_OCTET_STREAM "application/octet-stream" +#define APPLICATION_VND_MICROSOFT_IOTHUB_JSON "application/vnd.microsoft.iothub.json" + +/*DEFAULT_GETMINIMUMPOLLINGTIME is the minimum time in seconds allowed between 2 consecutive GET issues to the service (GET=fetch messages)*/ +/*the default is 25 minutes*/ +#define DEFAULT_GETMINIMUMPOLLINGTIME ((unsigned int)25*60) + +#define MAXIMUM_MESSAGE_SIZE (255*1024-1) +#define MAXIMUM_PAYLOAD_OVERHEAD 384 +#define MAXIMUM_PROPERTY_OVERHEAD 16 + +/*forward declaration*/ +static int appendMapToJSON(STRING_HANDLE existing, const char* const* keys, const char* const* values, size_t count); + +typedef struct HTTPTRANSPORT_HANDLE_DATA_TAG +{ + STRING_HANDLE hostName; + HTTPAPIEX_HANDLE httpApiExHandle; + bool doBatchedTransfers; + unsigned int getMinimumPollingTime; + VECTOR_HANDLE perDeviceList; +}HTTPTRANSPORT_HANDLE_DATA; + +typedef struct HTTPTRANSPORT_PERDEVICE_DATA_TAG +{ + HTTPTRANSPORT_HANDLE_DATA* transportHandle; + + STRING_HANDLE deviceId; + STRING_HANDLE deviceKey; + STRING_HANDLE deviceSasToken; + STRING_HANDLE eventHTTPrelativePath; + STRING_HANDLE messageHTTPrelativePath; + HTTP_HEADERS_HANDLE eventHTTPrequestHeaders; + HTTP_HEADERS_HANDLE messageHTTPrequestHeaders; + STRING_HANDLE abandonHTTPrelativePathBegin; + HTTPAPIEX_SAS_HANDLE sasObject; + bool DoWork_PullMessage; + time_t lastPollTime; + bool isFirstPoll; + + IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle; + PDLIST_ENTRY waitingToSend; + DLIST_ENTRY eventConfirmations; /*holds items for event confirmations*/ +} HTTPTRANSPORT_PERDEVICE_DATA; + +typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG +{ + HTTPTRANSPORT_HANDLE_DATA* handleData; + HTTPTRANSPORT_PERDEVICE_DATA* deviceData; + char* etagValue; +} MESSAGE_DISPOSITION_CONTEXT; + +DEFINE_ENUM_STRINGS(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES); + +static void destroy_eventHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->eventHTTPrelativePath); + handleData->eventHTTPrelativePath = NULL; +} + +static bool create_eventHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_017: [ IoTHubTransportHttp_Register shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(deviceId) + "/messages/events" + APIVERSION. ]*/ + bool result; + STRING_HANDLE urlEncodedDeviceId; + handleData->eventHTTPrelativePath = STRING_construct("/devices/"); + if (handleData->eventHTTPrelativePath == NULL) + { + LogError("STRING_construct failed."); + result = false; + } + else + { + if (!( + ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) && + (STRING_concat_with_STRING(handleData->eventHTTPrelativePath, urlEncodedDeviceId) == 0) && + (STRING_concat(handleData->eventHTTPrelativePath, EVENT_ENDPOINT API_VERSION) == 0) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_018: [ If creating the string fail for any reason then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + destroy_eventHTTPrelativePath(handleData); + LogError("Creating HTTP event relative path failed."); + result = false; + } + else + { + result = true; + } + STRING_delete(urlEncodedDeviceId); + } + return result; +} + +static void destroy_messageHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->messageHTTPrelativePath); + handleData->messageHTTPrelativePath = NULL; +} + +static bool create_messageHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId) +{ + bool result; + handleData->messageHTTPrelativePath = STRING_construct("/devices/"); + if (handleData->messageHTTPrelativePath == NULL) + { + LogError("STRING_construct failed."); + result = false; + } + else + { + STRING_HANDLE urlEncodedDeviceId; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_019: [ IoTHubTransportHttp_Register shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(deviceId) + "/messages/devicebound" + APIVERSION. ]*/ + if (!( + ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) && + (STRING_concat_with_STRING(handleData->messageHTTPrelativePath, urlEncodedDeviceId) == 0) && + (STRING_concat(handleData->messageHTTPrelativePath, MESSAGE_ENDPOINT_HTTP API_VERSION) == 0) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_020: [ If creating the message HTTP relative path fails, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + LogError("Creating HTTP message relative path failed."); + result = false; + destroy_messageHTTPrelativePath(handleData); + } + else + { + result = true; + } + STRING_delete(urlEncodedDeviceId); + } + + return result; +} + +static void destroy_eventHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + HTTPHeaders_Free(handleData->eventHTTPrequestHeaders); + handleData->eventHTTPrequestHeaders = NULL; +} + +static HTTP_HEADERS_RESULT addUserAgentHeaderInfo(IOTHUB_CLIENT_CORE_LL_HANDLE hClient, HTTP_HEADERS_HANDLE eventHTTPrequestHeaders) +{ + void* product_info; + HTTP_HEADERS_RESULT result; + if ((IoTHubClientCore_LL_GetOption(hClient, OPTION_PRODUCT_INFO, &product_info) == IOTHUB_CLIENT_ERROR) || (product_info == NULL)) + { + result = HTTPHeaders_AddHeaderNameValuePair(eventHTTPrequestHeaders, "User-Agent", CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION); + } + else + { + result = HTTPHeaders_AddHeaderNameValuePair(eventHTTPrequestHeaders, "User-Agent", STRING_c_str((STRING_HANDLE)product_info)); + } + return result; +} + +static bool create_eventHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId, bool is_x509_used) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_021: [ IoTHubTransportHttp_Register shall create a set of HTTP headers (further called "event HTTP request headers") consisting of the following fixed field names and values: "iothub-to":"/devices/" + URL_ENCODED(deviceId) + "/messages/events"; "Authorization":"" + "Accept":"application/json" + "Connection":"Keep-Alive" ]*/ + bool result; + handleData->eventHTTPrequestHeaders = HTTPHeaders_Alloc(); + if (handleData->eventHTTPrequestHeaders == NULL) + { + LogError("HTTPHeaders_Alloc failed."); + result = false; + } + else + { + STRING_HANDLE temp = STRING_construct("/devices/"); + if (temp == NULL) + { + LogError("STRING_construct failed."); + result = false; + destroy_eventHTTPrequestHeaders(handleData); + } + else + { + STRING_HANDLE urlEncodedDeviceId; + if (!( + ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) && + (STRING_concat_with_STRING(temp, urlEncodedDeviceId) == 0) && + (STRING_concat(temp, EVENT_ENDPOINT) == 0) + )) + { + LogError("deviceId construction failed."); + result = false; + destroy_eventHTTPrequestHeaders(handleData); + } + else + { + if (!( + (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "iothub-to", STRING_c_str(temp)) == HTTP_HEADERS_OK) && + (is_x509_used || (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Authorization", " ") == HTTP_HEADERS_OK)) && + (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Accept", "application/json") == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Connection", "Keep-Alive") == HTTP_HEADERS_OK) && + (addUserAgentHeaderInfo(handleData->iotHubClientHandle, handleData->eventHTTPrequestHeaders) == HTTP_HEADERS_OK) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_022: [ If creating the event HTTP request headers fails, then IoTHubTransportHttp_Register shall fail and return NULL.] */ + LogError("adding header properties failed."); + result = false; + destroy_eventHTTPrequestHeaders(handleData); + } + else + { + result = true; + } + } + STRING_delete(urlEncodedDeviceId); + STRING_delete(temp); + } + } + return result; +} + +static void destroy_messageHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + HTTPHeaders_Free(handleData->messageHTTPrequestHeaders); + handleData->messageHTTPrequestHeaders = NULL; +} + +static bool create_messageHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData, bool is_x509_used) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_132: [ IoTHubTransportHttp_Register shall create a set of HTTP headers (further called "message HTTP request headers") consisting of the following fixed field names and values: + "Authorization": "" ]*/ + bool result; + handleData->messageHTTPrequestHeaders = HTTPHeaders_Alloc(); + if (handleData->messageHTTPrequestHeaders == NULL) + { + LogError("HTTPHeaders_Alloc failed."); + result = false; + } + else + { + if (!( + (addUserAgentHeaderInfo(handleData->iotHubClientHandle, handleData->messageHTTPrequestHeaders) == HTTP_HEADERS_OK) && + (is_x509_used || (HTTPHeaders_AddHeaderNameValuePair(handleData->messageHTTPrequestHeaders, "Authorization", " ") == HTTP_HEADERS_OK)) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_023: [ If creating message HTTP request headers then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + destroy_messageHTTPrequestHeaders(handleData); + LogError("adding header properties failed."); + result = false; + } + else + { + result = true; + } + } + return result; +} + +static void destroy_abandonHTTPrelativePathBegin(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->abandonHTTPrelativePathBegin); + handleData->abandonHTTPrelativePathBegin = NULL; +} + +static bool create_abandonHTTPrelativePathBegin(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_024: [ IoTHubTransportHttp_Register shall create a STRING containing: "/devices/" + URL_ENCODED(device id) +"/messages/deviceBound/" called abandonHTTPrelativePathBegin. ]*/ + bool result; + handleData->abandonHTTPrelativePathBegin = STRING_construct("/devices/"); + if (handleData->abandonHTTPrelativePathBegin == NULL) + { + result = false; + } + else + { + STRING_HANDLE urlEncodedDeviceId; + if (!( + ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) && + (STRING_concat_with_STRING(handleData->abandonHTTPrelativePathBegin, urlEncodedDeviceId) == 0) && + (STRING_concat(handleData->abandonHTTPrelativePathBegin, MESSAGE_ENDPOINT_HTTP_ETAG) == 0) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_025: [ If creating the abandonHTTPrelativePathBegin fails then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + LogError("unable to create abandon path string."); + STRING_delete(handleData->abandonHTTPrelativePathBegin); + result = false; + } + else + { + result = true; + } + STRING_delete(urlEncodedDeviceId); + } + return result; +} + +static void destroy_SASObject(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + HTTPAPIEX_SAS_Destroy(handleData->sasObject); + handleData->sasObject = NULL; +} + +static bool create_deviceSASObject(HTTPTRANSPORT_PERDEVICE_DATA* handleData, STRING_HANDLE hostName, const char * deviceId, const char * deviceKey) +{ + STRING_HANDLE keyName; + bool result; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_026: [IoTHubTransportHttp_Create shall invoke URL_EncodeString with an argument of device id.]*/ + keyName = URL_EncodeString(deviceId); + if (keyName == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_027: [If the encode fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + LogError("URL_EncodeString keyname failed"); + result = false; + } + else + { + STRING_HANDLE uriResource; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_028: [IoTHubTransportHttp_Create shall invoke STRING_clone using the previously created hostname.]*/ + uriResource = STRING_clone(hostName); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_029: [If the clone fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + if (uriResource != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_030: [IoTHubTransportHttp_Create shall invoke STRING_concat with arguments uriResource and the string "/devices/".]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_141: [If the concat fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_031: [IoTHubTransportHttp_Create shall invoke STRING_concat_with_STRING with arguments uriResource and keyName.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_032: [If the STRING_concat_with_STRING fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + if ((STRING_concat(uriResource, "/devices/") == 0) && + (STRING_concat_with_STRING(uriResource, keyName) == 0)) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_033: [IoTHubTransportHttp_Create shall invoke STRING_construct with an argument of config->upperConfig->deviceKey.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_034: [If the STRING_construct fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + STRING_HANDLE key = STRING_construct(deviceKey); + if (key != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_035: [The keyName is shortened to zero length, if that fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + if (STRING_empty(keyName) != 0) + { + LogError("Unable to form the device key name for the SAS"); + result = false; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_036: [IoTHubTransportHttp_Create shall invoke HTTPAPIEX_SAS_Create with arguments key, uriResource, and zero length keyName.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_037: [If the HTTPAPIEX_SAS_Create fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/ + handleData->sasObject = HTTPAPIEX_SAS_Create(key, uriResource, keyName); + result = (handleData->sasObject != NULL) ? (true) : (false); + } + STRING_delete(key); + } + else + { + LogError("STRING_construct Key failed"); + result = false; + } + } + else + { + LogError("STRING_concat uri resource failed"); + result = false; + } + STRING_delete(uriResource); + } + else + { + LogError("STRING_staticclone uri resource failed"); + result = false; + } + STRING_delete(keyName); + } + return result; +} + +static void destroy_deviceId(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->deviceId); + handleData->deviceId = NULL; +} + +static bool create_deviceId(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_133: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceId") from config->deviceConfig->deviceId. ]*/ + bool result; + handleData->deviceId = STRING_construct(deviceId); + if (handleData->deviceId == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_134: [ If deviceId is not created, then IoTHubTransportHttp_Register shall fail and return NULL. */ + LogError("STRING_construct deviceId failed"); + result = false; + } + else + { + result = true; + } + return result; +} + +static void destroy_deviceKey(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->deviceKey); + handleData->deviceKey = NULL; +} + +static void destroy_deviceSas(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->deviceSasToken); + handleData->deviceSasToken = NULL; +} + +static bool create_deviceKey(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceKey) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_135: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceKey") from deviceKey. ]*/ + bool result; + handleData->deviceKey = STRING_construct(deviceKey); + if (handleData->deviceKey == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_136: [ If deviceKey is not created, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + LogError("STRING_construct deviceKey failed"); + result = false; + } + else + { + result = true; + } + return result; +} + +static void destroy_deviceSasToken(HTTPTRANSPORT_PERDEVICE_DATA* handleData) +{ + STRING_delete(handleData->deviceSasToken); + handleData->deviceSasToken = NULL; +} + +static bool create_deviceSasToken(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceSasToken) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_03_135: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceSasToken") from deviceSasToken. ]*/ + bool result; + handleData->deviceSasToken = STRING_construct(deviceSasToken); + if (handleData->deviceSasToken == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_03_136: [ If deviceSasToken is not created, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + LogError("STRING_construct deviceSasToken failed"); + result = false; + } + else + { + result = true; + } + return result; +} + +/* +* List queries Find by handle and find by device name +*/ + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/ +static bool findDeviceHandle(const void* element, const void* value) +{ + bool result; + /* data stored at element is device handle */ + const IOTHUB_DEVICE_HANDLE * guess = (const IOTHUB_DEVICE_HANDLE *)element; + IOTHUB_DEVICE_HANDLE match = (IOTHUB_DEVICE_HANDLE)value; + result = (*guess == match) ? true : false; + return result; +} + +static bool findDeviceById(const void* element, const void* value) +{ + bool result; + const char* deviceId = (const char *)value; + const HTTPTRANSPORT_PERDEVICE_DATA * perDeviceElement = *(const HTTPTRANSPORT_PERDEVICE_DATA **)element; + + result = (strcmp(STRING_c_str(perDeviceElement->deviceId), deviceId) == 0); + + return result; +} + +static IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + HTTPTRANSPORT_PERDEVICE_DATA* result; + if (handle == NULL || device == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_142: [ If handle is NULL or device is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/ + LogError("Transport handle is NULL"); + result = NULL; + } + else if (device->deviceId == NULL || ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) || waitingToSend == NULL || iotHubClientHandle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_015: [ If IOTHUB_DEVICE_CONFIG fields deviceKey and deviceSasToken are NULL, then IoTHubTransportHttp_Register shall assume a x509 authentication. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_014: [ If IOTHUB_DEVICE_CONFIG field deviceId is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_016: [ If parameter waitingToSend is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_143: [ If parameter iotHubClientHandle is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/ + LogError("invalid parameters detected TRANSPORT_LL_HANDLE handle=%p, const IOTHUB_DEVICE_CONFIG* device=%p, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle=%p, PDLIST_ENTRY waitingToSend=%p", + handle, device, iotHubClientHandle, waitingToSend); + result = NULL; + } + else + { + HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/ + void* listItem = VECTOR_find_if(handleData->perDeviceList, findDeviceById, device->deviceId); + if (listItem != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/ + LogError("Transport already has device registered by id: [%s]", device->deviceId); + result = NULL; + } + else + { + bool was_create_deviceKey_ok = false; + bool was_create_deviceSasToken_ok = false; + bool was_sasObject_ok = false; + bool was_x509_ok = false; /*there's nothing "created" in the case of x509, it is a flag indicating that x509 is used*/ + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_038: [ Otherwise, IoTHubTransportHttp_Register shall allocate the IOTHUB_DEVICE_HANDLE structure. ]*/ + bool was_resultCreated_ok = ((result = (HTTPTRANSPORT_PERDEVICE_DATA *)malloc(sizeof(HTTPTRANSPORT_PERDEVICE_DATA))) != NULL); + bool was_create_deviceId_ok = was_resultCreated_ok && create_deviceId(result, device->deviceId); + + if (was_create_deviceId_ok) + { + if (device->deviceSasToken != NULL) + { + /*device->deviceKey is certainly NULL (because of the validation condition (else if (device->deviceId == NULL || ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) || waitingToSend == NULL || iotHubClientHandle == NULL))that does not accept that both satoken AND devicekey are non-NULL*/ + was_create_deviceSasToken_ok = create_deviceSasToken(result, device->deviceSasToken); + result->deviceKey = NULL; + result->sasObject = NULL; + } + else /*when deviceSasToken == NULL*/ + { + if (device->deviceKey != NULL) + { + was_create_deviceKey_ok = create_deviceKey(result, device->deviceKey); + result->deviceSasToken = NULL; + } + else + { + /*when both of them are NULL*/ + was_x509_ok = true; + result->deviceKey = NULL; + result->deviceSasToken = NULL; + } + } + } + + bool was_eventHTTPrelativePath_ok = (was_create_deviceKey_ok || was_create_deviceSasToken_ok || was_x509_ok) && create_eventHTTPrelativePath(result, device->deviceId); + bool was_messageHTTPrelativePath_ok = was_eventHTTPrelativePath_ok && create_messageHTTPrelativePath(result, device->deviceId); + bool was_eventHTTPrequestHeaders_ok; + if (was_messageHTTPrelativePath_ok) + { + result->iotHubClientHandle = iotHubClientHandle; + was_eventHTTPrequestHeaders_ok = create_eventHTTPrequestHeaders(result, device->deviceId, was_x509_ok); + } + else + { + was_eventHTTPrequestHeaders_ok = false; + } + bool was_messageHTTPrequestHeaders_ok = was_eventHTTPrequestHeaders_ok && create_messageHTTPrequestHeaders(result, was_x509_ok); + bool was_abandonHTTPrelativePathBegin_ok = was_messageHTTPrequestHeaders_ok && create_abandonHTTPrelativePathBegin(result, device->deviceId); + + if (was_x509_ok) + { + result->sasObject = NULL; /**/ + was_sasObject_ok = true; + } + else + { + if (!was_create_deviceSasToken_ok) + { + was_sasObject_ok = was_abandonHTTPrelativePathBegin_ok && create_deviceSASObject(result, handleData->hostName, device->deviceId, device->deviceKey); + } + } + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_041: [ IoTHubTransportHttp_Register shall call VECTOR_push_back to store the new device information. ]*/ + bool was_list_add_ok = (was_sasObject_ok || was_create_deviceSasToken_ok || was_x509_ok) && (VECTOR_push_back(handleData->perDeviceList, &result, 1) == 0); + + if (was_list_add_ok) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_043: [ Upon success, IoTHubTransportHttp_Register shall store the transport handle, iotHubClientHandle, and the waitingToSend queue in the device handle return a non-NULL value. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_040: [ IoTHubTransportHttp_Register shall put event HTTP relative path, message HTTP relative path, event HTTP request headers, message HTTP request headers, abandonHTTPrelativePathBegin, HTTPAPIEX_SAS_HANDLE, and the device handle into a device structure. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_128: [ IoTHubTransportHttp_Register shall mark this device as unsubscribed. ]*/ + result->DoWork_PullMessage = false; + result->isFirstPoll = true; + result->waitingToSend = waitingToSend; + DList_InitializeListHead(&(result->eventConfirmations)); + result->transportHandle = (HTTPTRANSPORT_HANDLE_DATA *)handle; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_042: [ If the singlylinkedlist_add fails then IoTHubTransportHttp_Register shall fail and return NULL. ]*/ + if (was_sasObject_ok) destroy_SASObject(result); + if (was_abandonHTTPrelativePathBegin_ok) destroy_abandonHTTPrelativePathBegin(result); + if (was_messageHTTPrelativePath_ok) destroy_messageHTTPrelativePath(result); + if (was_eventHTTPrequestHeaders_ok) destroy_eventHTTPrequestHeaders(result); + if (was_messageHTTPrequestHeaders_ok) destroy_messageHTTPrequestHeaders(result); + if (was_eventHTTPrelativePath_ok) destroy_eventHTTPrelativePath(result); + if (was_create_deviceId_ok) destroy_deviceId(result); + if (was_create_deviceKey_ok) destroy_deviceKey(result); + if (was_create_deviceSasToken_ok) destroy_deviceSasToken(result); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_039: [ If the allocating the device handle fails then IoTHubTransportHttp_Register shall fail and return NULL. ] */ + if (was_resultCreated_ok) free(result); + result = NULL; + } + } + + } + return (IOTHUB_DEVICE_HANDLE)result; +} + + +static void destroy_perDeviceData(HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem) +{ + destroy_deviceId(perDeviceItem); + destroy_deviceKey(perDeviceItem); + destroy_deviceSas(perDeviceItem); + destroy_eventHTTPrelativePath(perDeviceItem); + destroy_messageHTTPrelativePath(perDeviceItem); + destroy_eventHTTPrequestHeaders(perDeviceItem); + destroy_messageHTTPrequestHeaders(perDeviceItem); + destroy_abandonHTTPrelativePathBegin(perDeviceItem); + destroy_SASObject(perDeviceItem); +} + +static IOTHUB_DEVICE_HANDLE* get_perDeviceDataItem(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + HTTPTRANSPORT_PERDEVICE_DATA* deviceHandleData = (HTTPTRANSPORT_PERDEVICE_DATA*)deviceHandle; + IOTHUB_DEVICE_HANDLE* listItem; + + HTTPTRANSPORT_HANDLE_DATA* handleData = deviceHandleData->transportHandle; + + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_find_if(handleData->perDeviceList, findDeviceHandle, deviceHandle); + if (listItem == NULL) + { + LogError("device handle not found in transport device list"); + listItem = NULL; + } + else + { + /* sucessfully found device in list. */ + } + + return listItem; +} + +static void IoTHubTransportHttp_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + if (deviceHandle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_044: [ If deviceHandle is NULL, then IoTHubTransportHttp_Unregister shall do nothing. ]*/ + LogError("Unregister a NULL device handle"); + } + else + { + HTTPTRANSPORT_PERDEVICE_DATA* deviceHandleData = (HTTPTRANSPORT_PERDEVICE_DATA*)deviceHandle; + HTTPTRANSPORT_HANDLE_DATA* handleData = deviceHandleData->transportHandle; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_045: [ IoTHubTransportHttp_Unregister shall locate deviceHandle in the transport device list by calling list_find_if. ]*/ + IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(deviceHandle); + if (listItem == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_046: [ If the device structure is not found, then this function shall fail and do nothing. ]*/ + LogError("Device Handle [%p] not found in transport", deviceHandle); + } + else + { + HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem); + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_047: [ IoTHubTransportHttp_Unregister shall free all the resources used in the device structure. ]*/ + destroy_perDeviceData(perDeviceItem); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_048: [ IoTHubTransportHttp_Unregister shall call singlylinkedlist_remove to remove device from devices list. ]*/ + VECTOR_erase(handleData->perDeviceList, listItem, 1); + free(deviceHandleData); + } + } + + return; +} + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ +/*Codes_SRS_TRANSPORTMULTITHTTP_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransportHttp_Create shall use it as hostname] */ +static void destroy_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData) +{ + STRING_delete(handleData->hostName); + handleData->hostName = NULL; +} + +static bool create_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config) +{ + bool result; + if (config->upperConfig->protocolGatewayHostName != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransportHttp_Create shall use it as hostname] */ + handleData->hostName = STRING_construct(config->upperConfig->protocolGatewayHostName); + result = (handleData->hostName != NULL); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ + handleData->hostName = STRING_construct(config->upperConfig->iotHubName); + + if (handleData->hostName == NULL) + { + result = false; + } + else + { + if ((STRING_concat(handleData->hostName, ".") != 0) || + (STRING_concat(handleData->hostName, config->upperConfig->iotHubSuffix) != 0)) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_006: [ If creating the hostname fails then IoTHubTransportHttp_Create shall fail and return NULL. ] */ + destroy_hostName(handleData); + result = false; + } + else + { + result = true; + } + } + } + return result; +} + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_007: [Otherwise, IoTHubTransportHttp_Create shall create a HTTPAPIEX_HANDLE by a call to HTTPAPIEX_Create passing for hostName the hostname so far constructed by IoTHubTransportHttp_Create.]*/ +static void destroy_httpApiExHandle(HTTPTRANSPORT_HANDLE_DATA* handleData) +{ + HTTPAPIEX_Destroy(handleData->httpApiExHandle); + handleData->httpApiExHandle = NULL; +} + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_007: [ IoTHubTransportHttp_Create shall create a HTTPAPIEX_HANDLE by a call to HTTPAPIEX_Create passing for hostName the hostname so far constructed by IoTHubTransportHttp_Create. ]*/ +static bool create_httpApiExHandle(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config) +{ + bool result; + (void)config; + handleData->httpApiExHandle = HTTPAPIEX_Create(STRING_c_str(handleData->hostName)); + if (handleData->httpApiExHandle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_008: [ If creating the HTTPAPIEX_HANDLE fails then IoTHubTransportHttp_Create shall fail and return NULL. ] */ + result = false; + } + else + { + result = true; + } + return result; +} + +static void destroy_perDeviceList(HTTPTRANSPORT_HANDLE_DATA* handleData) +{ + VECTOR_destroy(handleData->perDeviceList); + handleData->perDeviceList = NULL; +} + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_009: [ IoTHubTransportHttp_Create shall call singlylinkedlist_create to create a list of registered devices. ]*/ +static bool create_perDeviceList(HTTPTRANSPORT_HANDLE_DATA* handleData) +{ + bool result; + handleData->perDeviceList = VECTOR_create(sizeof(IOTHUB_DEVICE_HANDLE)); + if (handleData == NULL || handleData->perDeviceList == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_010: [ If creating the list fails, then IoTHubTransportHttp_Create shall fail and return NULL. ]*/ + result = false; + } + else + { + result = true; + } + return result; +} + + +static TRANSPORT_LL_HANDLE IoTHubTransportHttp_Create(const IOTHUBTRANSPORT_CONFIG* config) +{ + HTTPTRANSPORT_HANDLE_DATA* result; + if (config == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_001: [If parameter config is NULL, then IoTHubTransportHttp_Create shall return NULL.]*/ + LogError("invalid arg (configuration is missing)"); + result = NULL; + } + else if (config->upperConfig == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_002: [ If field transportConfig is NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/ + LogError("invalid arg (upperConfig is NULL)"); + result = NULL; + } + else if (config->upperConfig->protocol == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/ + LogError("invalid arg (protocol is NULL)"); + result = NULL; + } + else if (config->upperConfig->iotHubName == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/ + LogError("invalid arg (iotHubName is NULL)"); + result = NULL; + } + else if (config->upperConfig->iotHubSuffix == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/ + LogError("invalid arg (iotHubSuffix is NULL)"); + result = NULL; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_130: [ IoTHubTransportHttp_Create shall allocate memory for the handle. ]*/ + result = (HTTPTRANSPORT_HANDLE_DATA*)malloc(sizeof(HTTPTRANSPORT_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_131: [ If allocation fails, IoTHubTransportHttp_Create shall fail and return NULL. ]*/ + LogError("unable to malloc"); + } + else + { + bool was_hostName_ok = create_hostName(result, config); + bool was_httpApiExHandle_ok = was_hostName_ok && create_httpApiExHandle(result, config); + bool was_perDeviceList_ok = was_httpApiExHandle_ok && create_perDeviceList(result); + + + if (was_perDeviceList_ok) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_011: [ Otherwise, IoTHubTransportHttp_Create shall succeed and return a non-NULL value. ]*/ + result->doBatchedTransfers = false; + result->getMinimumPollingTime = DEFAULT_GETMINIMUMPOLLINGTIME; + } + else + { + if (was_httpApiExHandle_ok) destroy_httpApiExHandle(result); + if (was_hostName_ok) destroy_hostName(result); + + free(result); + result = NULL; + } + } + } + return result; +} + +static void IoTHubTransportHttp_Destroy(TRANSPORT_LL_HANDLE handle) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_012: [ IoTHubTransportHttp_Destroy shall do nothing is handle is NULL. ]*/ + if (handle != NULL) + { + HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle; + IOTHUB_DEVICE_HANDLE* listItem; + + size_t deviceListSize = VECTOR_size(handleData->perDeviceList); + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_013: [ Otherwise, IoTHubTransportHttp_Destroy shall free all the resources currently in use. ]*/ + for (size_t i = 0; i < deviceListSize; i++) + { + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i); + HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA*)(*listItem); + destroy_perDeviceData(perDeviceItem); + free(perDeviceItem); + } + + destroy_hostName((HTTPTRANSPORT_HANDLE_DATA *)handle); + destroy_httpApiExHandle((HTTPTRANSPORT_HANDLE_DATA *)handle); + destroy_perDeviceList((HTTPTRANSPORT_HANDLE_DATA *)handle); + free(handle); + } +} + +static int IoTHubTransportHttp_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + int result; + if (handle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_103: [ If parameter deviceHandle is NULL then IoTHubTransportHttp_Subscribe shall fail and return a non-zero value. ]*/ + LogError("invalid arg passed to IoTHubTransportHttp_Subscribe"); + result = __FAILURE__; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_104: [ IoTHubTransportHttp_Subscribe shall locate deviceHandle in the transport device list by calling list_find_if. ]*/ + IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle); + + if (listItem == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_105: [ If the device structure is not found, then this function shall fail and return a non-zero value. ]*/ + LogError("did not find device in transport handle"); + result = __FAILURE__; + } + else + { + HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem; + + perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_106: [ Otherwise, IoTHubTransportHttp_Subscribe shall set the device so that subsequent calls to DoWork should execute HTTP requests. ]*/ + perDeviceItem->DoWork_PullMessage = true; + } + result = 0; + } + return result; +} + +static void IoTHubTransportHttp_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_107: [ If parameter deviceHandle is NULL then IoTHubTransportHttp_Unsubscribe shall fail do nothing. ]*/ + if (handle != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_108: [ IoTHubTransportHttp_Unsubscribe shall locate deviceHandle in the transport device list by calling list_find_if. ]*/ + IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_109: [ If the device structure is not found, then this function shall fail and do nothing. ]*/ + if (listItem != NULL) + { + HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_110: [ Otherwise, IoTHubTransportHttp_Subscribe shall set the device so that subsequent calls to DoWork shall not execute HTTP requests. ]*/ + perDeviceItem->DoWork_PullMessage = false; + } + else + { + LogError("Device not found to unsuscribe."); + } + } + else + { + LogError("Null handle passed to Unsuscribe."); + } +} + +static int IoTHubTransportHttp_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_02_003: [ IoTHubTransportHttp_Subscribe_DeviceTwin shall return a non-zero value. ]*/ + (void)handle; + int result = __FAILURE__; + LogError("IoTHubTransportHttp_Subscribe_DeviceTwin Not supported"); + return result; +} + +static void IoTHubTransportHttp_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + /*Codes_SRS_TRANSPORTMULTITHTTP_02_004: [ IoTHubTransportHttp_Unsubscribe_DeviceTwin shall return ]*/ + LogError("IoTHubTransportHttp_Unsubscribe_DeviceTwin Not supported"); +} + +static int IoTHubTransportHttp_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + int result = __FAILURE__; + LogError("not implemented (yet)"); + return result; +} + +static void IoTHubTransportHttp_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("not implemented (yet)"); +} + +static int IoTHubTransportHttp_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + (void)handle; + (void)methodId; + (void)response; + (void)response_size; + (void)status_response; + LogError("not implemented (yet)"); + return __FAILURE__; +} + +/*produces a representation of the properties, if they exist*/ +/*if they do not exist, produces ""*/ +static int concat_Properties(STRING_HANDLE existing, MAP_HANDLE map, size_t* propertiesMessageSizeContribution) +{ + int result; + const char*const* keys; + const char*const* values; + size_t count; + if (Map_GetInternals(map, &keys, &values, &count) != MAP_OK) + { + result = __FAILURE__; + LogError("error while Map_GetInternals"); + } + else + { + + if (count == 0) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_064: [If IoTHubMessage does not have properties, then "properties":{...} shall be missing from the payload*/ + /*no properties - do nothing with existing*/ + result = 0; + *propertiesMessageSizeContribution = 0; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_058: [If IoTHubMessage has properties, then they shall be serialized at the same level as "body" using the following pattern: "properties":{"iothub-app-name1":"value1","iothub-app-name2":"value2*/ + if (STRING_concat(existing, ",\"properties\":") != 0) + { + /*go ahead and return it*/ + result = __FAILURE__; + LogError("failed STRING_concat"); + } + else if (appendMapToJSON(existing, keys, values, count) != 0) + { + result = __FAILURE__; + LogError("unable to append the properties"); + } + else + { + /*all is fine*/ + size_t i; + *propertiesMessageSizeContribution = 0; + for (i = 0; i < count; i++) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_063: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */ + *propertiesMessageSizeContribution += (strlen(keys[i]) + strlen(values[i]) + MAXIMUM_PROPERTY_OVERHEAD); + } + result = 0; + } + } + } + return result; +} + +/*produces a JSON representation of the map : {"a": "value_of_a","b":"value_of_b"}*/ +static int appendMapToJSON(STRING_HANDLE existing, const char* const* keys, const char* const* values, size_t count) /*under consideration: move to MAP module when it has more than 1 user*/ +{ + int result; + if (STRING_concat(existing, "{") != 0) + { + /*go on and return it*/ + LogError("STRING_construct failed"); + result = __FAILURE__; + } + else + { + size_t i; + for (i = 0; i < count; i++) + { + if (!( + (STRING_concat(existing, (i == 0) ? "\"" IOTHUB_APP_PREFIX : ",\"" IOTHUB_APP_PREFIX) == 0) && + (STRING_concat(existing, keys[i]) == 0) && + (STRING_concat(existing, "\":\"") == 0) && + (STRING_concat(existing, values[i]) == 0) && + (STRING_concat(existing, "\"") == 0) + )) + { + LogError("unable to STRING_concat"); + break; + } + } + + if (i < count) + { + result = __FAILURE__; + /*error, let it go through*/ + } + else if (STRING_concat(existing, "}") != 0) + { + LogError("unable to STRING_concat"); + result = __FAILURE__; + } + else + { + /*all is fine*/ + result = 0; + } + } + return result; +} + +/*makes the following string:{"body":"base64 encoding of the message content"[,"properties":{"a":"valueOfA"}]}*/ +/*return NULL if there was a failure, or a non-NULL STRING_HANDLE that contains the intended data*/ +static STRING_HANDLE make1EventJSONitem(PDLIST_ENTRY item, size_t *messageSizeContribution) +{ + STRING_HANDLE result; /*temp wants to contain :{"body":"base64 encoding of the message content"[,"properties":{"a":"valueOfA"}]}*/ + IOTHUB_MESSAGE_LIST* message = containingRecord(item, IOTHUB_MESSAGE_LIST, entry); + IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message->messageHandle); + + switch (contentType) + { + case IOTHUBMESSAGE_BYTEARRAY: + { + result = STRING_construct("{\"body\":\""); + if (result == NULL) + { + LogError("unable to STRING_construct"); + } + else + { + const unsigned char* source; + size_t size; + + if (IoTHubMessage_GetByteArray(message->messageHandle, &source, &size) != IOTHUB_MESSAGE_OK) + { + LogError("unable to get the data for the message."); + STRING_delete(result); + result = NULL; + } + else + { + STRING_HANDLE encoded = Base64_Encode_Bytes(source, size); + if (encoded == NULL) + { + LogError("unable to Base64_Encode_Bytes."); + STRING_delete(result); + result = NULL; + } + else + { + size_t propertiesSize = 0; + if (!( + (STRING_concat_with_STRING(result, encoded) == 0) && + (STRING_concat(result, "\"") == 0) && /*\" because closing value*/ + (concat_Properties(result, IoTHubMessage_Properties(message->messageHandle), &propertiesSize) == 0) && + (STRING_concat(result, "},") == 0) /*the last comma shall be replaced by a ']' by DaCr's suggestion (which is awesome enough to receive credits in the source code)*/ + )) + { + STRING_delete(result); + result = NULL; + LogError("unable to STRING_concat_with_STRING."); + } + else + { + /*all is fine... */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_062: [The message size is computed from the length of the payload + 384.] */ + *messageSizeContribution = size + MAXIMUM_PAYLOAD_OVERHEAD + propertiesSize; + } + STRING_delete(encoded); + } + } + } + break; + } + /*Codes_SRS_TRANSPORTMULTITHTTP_17_057: [If a messages to be send has type IOTHUBMESSAGE_STRING, then its serialization shall be {"body":"JSON encoding of the string", "base64Encoded":false}] */ + case IOTHUBMESSAGE_STRING: + { + result = STRING_construct("{\"body\":"); + if (result == NULL) + { + LogError("unable to STRING_construct"); + } + else + { + const char* source = IoTHubMessage_GetString(message->messageHandle); + if (source == NULL) + { + LogError("unable to IoTHubMessage_GetString"); + STRING_delete(result); + result = NULL; + } + else + { + STRING_HANDLE asJson = STRING_new_JSON(source); + if (asJson == NULL) + { + LogError("unable to STRING_new_JSON"); + STRING_delete(result); + result = NULL; + } + else + { + size_t propertiesSize = 0; + if (!( + (STRING_concat_with_STRING(result, asJson) == 0) && + (STRING_concat(result, ",\"base64Encoded\":false") == 0) && + (concat_Properties(result, IoTHubMessage_Properties(message->messageHandle), &propertiesSize) == 0) && + (STRING_concat(result, "},") == 0) /*the last comma shall be replaced by a ']' by DaCr's suggestion (which is awesome enough to receive credits in the source code)*/ + )) + { + LogError("unable to STRING_concat_with_STRING"); + STRING_delete(result); + result = NULL; + } + else + { + /*result has the intended content*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_062: [The message size is computed from the length of the payload + 384.] */ + *messageSizeContribution = strlen(source) + MAXIMUM_PAYLOAD_OVERHEAD + propertiesSize; + } + STRING_delete(asJson); + } + } + } + break; + } + default: + { + LogError("an unknown message type was encountered (%d)", contentType); + result = NULL; /*unknown message type*/ + break; + } + } + return result; +} + +#define MAKE_PAYLOAD_RESULT_VALUES \ + MAKE_PAYLOAD_OK, /*returned when there is a payload to be later send by HTTP*/ \ + MAKE_PAYLOAD_NO_ITEMS, /*returned when there are no items to be send*/ \ + MAKE_PAYLOAD_ERROR, /*returned when there were errors*/ \ + MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT /*returned when the first item doesn't fit*/ + +DEFINE_ENUM(MAKE_PAYLOAD_RESULT, MAKE_PAYLOAD_RESULT_VALUES); + +/*this function assembles several {"body":"base64 encoding of the message content"," base64Encoded": true} into 1 payload*/ +/*Codes_SRS_TRANSPORTMULTITHTTP_17_056: [IoTHubTransportHttp_DoWork shall build the following string:[{"body":"base64 encoding of the message1 content"},{"body":"base64 encoding of the message2 content"}...]]*/ +static MAKE_PAYLOAD_RESULT makePayload(HTTPTRANSPORT_PERDEVICE_DATA* deviceData, STRING_HANDLE* payload) +{ + MAKE_PAYLOAD_RESULT result; + size_t allMessagesSize = 0; + *payload = STRING_construct("["); + if (*payload == NULL) + { + LogError("unable to STRING_construct"); + result = MAKE_PAYLOAD_ERROR; + } + else + { + bool isFirst = true; + PDLIST_ENTRY actual; + bool keepGoing = true; /*keepGoing gets sometimes to false from within the loop*/ + /*either all the items enter the list or only some*/ + result = MAKE_PAYLOAD_OK; /*optimistically initializing it*/ + while (keepGoing && ((actual = deviceData->waitingToSend->Flink) != deviceData->waitingToSend)) + { + size_t messageSize; + STRING_HANDLE temp = make1EventJSONitem(actual, &messageSize); + if (isFirst) + { + isFirst = false; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/ + if (temp == NULL) /*first item failed to create, nothing to send*/ + { + result = MAKE_PAYLOAD_ERROR; + STRING_delete(*payload); + *payload = NULL; + keepGoing = false; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_065: [If the oldest message in waitingToSend causes the message size to exceed the message size limit then it shall be removed from waitingToSend, and IoTHubClientCore_LL_SendComplete shall be called. Parameter PDLIST_ENTRY completed shall point to a list containing only the oldest item, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_BATCHSTATE_FAILED.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_061: [The message size shall be limited to 255KB - 1 byte.]*/ + if (messageSize > MAXIMUM_MESSAGE_SIZE) + { + PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), head); + result = MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT; + STRING_delete(*payload); + *payload = NULL; + keepGoing = false; + } + else + { + if (STRING_concat_with_STRING(*payload, temp) != 0) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/ + result = MAKE_PAYLOAD_ERROR; + STRING_delete(*payload); + *payload = NULL; + keepGoing = false; + } + else + { + /*first item was put nicely in the payload*/ + PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), head); + allMessagesSize += messageSize; + } + } + STRING_delete(temp); + } + } + else + { + /*there is at least 1 item already in the payload*/ + if (temp == NULL) + { + /*there are multiple payloads encoded, the last one had an internal error, just go with those - closing the payload happens "after the loop"*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/ + result = MAKE_PAYLOAD_OK; + keepGoing = false; + } + else + { + if (allMessagesSize + messageSize > MAXIMUM_MESSAGE_SIZE) + { + /*this item doesn't make it to the payload, but the payload is valid so far*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/ + result = MAKE_PAYLOAD_OK; + keepGoing = false; + } + else if (STRING_concat_with_STRING(*payload, temp) != 0) + { + /*should still send what there is so far...*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/ + result = MAKE_PAYLOAD_OK; + keepGoing = false; + } + else + { + /*cool, the payload made it there, let's continue... */ + PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), head); + allMessagesSize += messageSize; + } + STRING_delete(temp); + } + } + } + + /*closing the payload*/ + if (result == MAKE_PAYLOAD_OK) + { + ((char*)STRING_c_str(*payload))[STRING_length(*payload) - 1] = ']'; /*TODO - do this in STRING_HANDLE*/ + } + else + { + /*no need to close anything*/ + } + } + return result; +} + +static void reversePutListBackIn(PDLIST_ENTRY source, PDLIST_ENTRY destination) +{ + /*this function takes a list, and inserts it in another list. When done in the context of this file, it reverses the effects of a not-able-to-send situation*/ + DList_AppendTailList(destination->Flink, source); + DList_RemoveEntryList(source); + DList_InitializeListHead(source); +} + +static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + + if (DList_IsListEmpty(deviceData->waitingToSend)) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_060: [If the list is empty then IoTHubTransportHttp_DoWork shall proceed to the following action.] */ + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_053: [If option SetBatching is true then _Dowork shall send batched event message as specced below.] */ + if (handleData->doBatchedTransfers) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_054: [Request HTTP headers shall have the value of "Content-Type" created or updated to "application/vnd.microsoft.iothub.json" by a call to HTTPHeaders_ReplaceHeaderNameValuePair.] */ + if (HTTPHeaders_ReplaceHeaderNameValuePair(deviceData->eventHTTPrequestHeaders, CONTENT_TYPE, APPLICATION_VND_MICROSOFT_IOTHUB_JSON) != HTTP_HEADERS_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_055: [If updating Content-Type fails for any reason, then _DoWork shall advance to the next action.] */ + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair"); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_059: [It shall inspect the "waitingToSend" DLIST passed in config structure.] */ + STRING_HANDLE payload; + switch (makePayload(deviceData, &payload)) + { + case MAKE_PAYLOAD_OK: + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_068: [Once a final payload has been obtained, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters:] */ + BUFFER_HANDLE temp = BUFFER_new(); + if (temp == NULL) + { + LogError("unable to BUFFER_new"); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/ + reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend); + } + else + { + if (BUFFER_build(temp, (const unsigned char*)STRING_c_str(payload), STRING_length(payload)) != 0) + { + LogError("unable to BUFFER_build"); + //items go back to waitingToSend + /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/ + reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend); + } + else + { + unsigned int statusCode; + if (HTTPAPIEX_SAS_ExecuteRequest( + deviceData->sasObject, + handleData->httpApiExHandle, + HTTPAPI_REQUEST_POST, + STRING_c_str(deviceData->eventHTTPrelativePath), + deviceData->eventHTTPrequestHeaders, + temp, + &statusCode, + NULL, + NULL + ) != HTTPAPIEX_OK) + { + LogError("unable to HTTPAPIEX_ExecuteRequest"); + //items go back to waitingToSend + /*Codes_SRS_TRANSPORTMULTITHTTP_17_069: [if HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */ + reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend); + } + else + { + if (statusCode < 300) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_070: [If HTTPAPIEX_SAS_ExecuteRequest does not fail and http status code <300 then IoTHubTransportHttp_DoWork shall call IoTHubClientCore_LL_SendComplete. Parameter PDLIST_ENTRY completed shall point to a list containing all the items batched, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_OK. The batched items shall be removed from waitingToSend.] */ + IoTHubClientCore_LL_SendComplete(iotHubClientHandle, &(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_OK); + } + else + { + //items go back to waitingToSend + /*Codes_SRS_TRANSPORTMULTITHTTP_17_069: [if HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */ + LogError("unexpected HTTP status code (%u)", statusCode); + reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend); + } + } + } + BUFFER_delete(temp); + } + STRING_delete(payload); + break; + } + case MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT: + { + IoTHubClientCore_LL_SendComplete(iotHubClientHandle, &(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR); /*takes care of emptying the list too*/ + break; + } + case MAKE_PAYLOAD_ERROR: + { + LogError("unrecoverable errors while building a batch message"); + break; + } + case MAKE_PAYLOAD_NO_ITEMS: + { + /*do nothing*/ + break; + } + default: + { + LogError("internal error: switch's default branch reached when never intended"); + break; + } + } + } + } + else + { + const unsigned char* messageContent = NULL; + size_t messageSize = 0; + size_t originalMessageSize = 0; + IOTHUB_MESSAGE_LIST* message = containingRecord(deviceData->waitingToSend->Flink, IOTHUB_MESSAGE_LIST, entry); + IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message->messageHandle); + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_073: [The message size is computed from the length of the payload + 384.]*/ + if (!( + (((contentType == IOTHUBMESSAGE_BYTEARRAY) && + (IoTHubMessage_GetByteArray(message->messageHandle, &messageContent, &originalMessageSize) == IOTHUB_MESSAGE_OK)) + ? ((void)(messageSize = originalMessageSize + MAXIMUM_PAYLOAD_OVERHEAD), 1) + : 0) + + || + + ((contentType == IOTHUBMESSAGE_STRING) && + ((void)(messageContent = (const unsigned char*)IoTHubMessage_GetString(message->messageHandle)), + ((void)(messageSize = MAXIMUM_PAYLOAD_OVERHEAD + (originalMessageSize = ((messageContent == NULL) + ? 0 + : strlen((const char*)messageContent))))), + messageContent != NULL) + ) + )) + { + LogError("unable to get the message content"); + /*go on...*/ + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_075: [If the oldest message in waitingToSend causes the message to exceed the message size limit then it shall be removed from waitingToSend, and IoTHubClientCore_LL_SendComplete shall be called. Parameter PDLIST_ENTRY completed shall point to a list containing only the oldest item, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_BATCHSTATE_FAILED.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_072: [The message size shall be limited to 255KB -1 bytes.] */ + if (messageSize > MAXIMUM_MESSAGE_SIZE) + { + PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), head); + IoTHubClientCore_LL_SendComplete(iotHubClientHandle, &(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR); /*takes care of emptying the list too*/ + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_071: [If option SetBatching is false then _Dowork shall send individual event message as specced below.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_076: [A clone of the event HTTP request headers shall be created.]*/ + HTTP_HEADERS_HANDLE clonedEventHTTPrequestHeaders = HTTPHeaders_Clone(deviceData->eventHTTPrequestHeaders); + if (clonedEventHTTPrequestHeaders == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("HTTPHeaders_Clone failed"); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_077: [The cloned HTTP headers shall have the HTTP header "Content-Type" set to "application/octet-stream".] */ + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, CONTENT_TYPE, APPLICATION_OCTET_STREAM) != HTTP_HEADERS_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("HTTPHeaders_ReplaceHeaderNameValuePair failed"); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_078: [Every message property "property":"value" shall be added to the HTTP headers as an individual header "iothub-app-property":"value".] */ + MAP_HANDLE map = IoTHubMessage_Properties(message->messageHandle); + const char*const* keys; + const char*const* values; + size_t count; + if (Map_GetInternals(map, &keys, &values, &count) != MAP_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("unable to Map_GetInternals"); + } + else + { + size_t i; + bool goOn = true; + const char* msgId; + const char* corrId; + const char* userDefinedContentType; + const char* contentEncoding; + + for (i = 0; (i < count) && goOn; i++) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_074: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */ + messageSize += (strlen(values[i]) + strlen(keys[i]) + MAXIMUM_PROPERTY_OVERHEAD); + if (messageSize > MAXIMUM_MESSAGE_SIZE) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_072: [The message size shall be limited to 255KB -1 bytes.] */ + PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), head); + IoTHubClientCore_LL_SendComplete(iotHubClientHandle, &(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR); /*takes care of emptying the list too*/ + goOn = false; + } + else + { + STRING_HANDLE temp = STRING_construct(IOTHUB_APP_PREFIX); + if (temp == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("unable to STRING_construct"); + goOn = false; + } + else + { + if (STRING_concat(temp, keys[i]) != 0) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("unable to STRING_concat"); + goOn = false; + } + else + { + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, STRING_c_str(temp), values[i]) != HTTP_HEADERS_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair"); + goOn = false; + } + } + STRING_delete(temp); + } + } + } + + // Add the Message Id and the Correlation Id + msgId = IoTHubMessage_GetMessageId(message->messageHandle); + if (goOn && msgId != NULL) + { + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_MESSAGE_ID, msgId) != HTTP_HEADERS_OK) + { + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair"); + goOn = false; + } + } + + corrId = IoTHubMessage_GetCorrelationId(message->messageHandle); + if (goOn && corrId != NULL) + { + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_CORRELATION_ID, corrId) != HTTP_HEADERS_OK) + { + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair"); + goOn = false; + } + } + + // Codes_SRS_TRANSPORTMULTITHTTP_09_001: [ If the IoTHubMessage being sent contains property `content-type` it shall be added to the HTTP headers as "iothub-contenttype":"value". ] + userDefinedContentType = IoTHubMessage_GetContentTypeSystemProperty(message->messageHandle); + if (goOn && userDefinedContentType != NULL) + { + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_CONTENT_TYPE_D2C, userDefinedContentType) != HTTP_HEADERS_OK) + { + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair (content-type)"); + goOn = false; + } + } + + // Codes_SRS_TRANSPORTMULTITHTTP_09_002: [ If the IoTHubMessage being sent contains property `content-encoding` it shall be added to the HTTP headers as "iothub-contentencoding":"value". ] + contentEncoding = IoTHubMessage_GetContentEncodingSystemProperty(message->messageHandle); + if (goOn && contentEncoding != NULL) + { + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_CONTENT_ENCODING_D2C, contentEncoding) != HTTP_HEADERS_OK) + { + LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair (content-encoding)"); + goOn = false; + } + } + + if (!goOn) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */ + } + else + { + BUFFER_HANDLE toBeSend = BUFFER_new(); + if (toBeSend == NULL) + { + LogError("unable to BUFFER_new"); + } + else + { + if (BUFFER_build(toBeSend, messageContent, originalMessageSize) != 0) + { + LogError("unable to BUFFER_build"); + } + else + { + unsigned int statusCode = 0; + HTTPAPIEX_RESULT r; + if (deviceData->deviceSasToken != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/ + if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, "Authorization", STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK) + { + r = HTTPAPIEX_ERROR; + /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/ + LogError("Unable to replace the old SAS Token."); + } + + /*Codes_SRS_TRANSPORTMULTITHTTP_03_003: [If a deviceSasToken exists, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_ExecuteRequest passing the following parameters] */ + else if ((r = HTTPAPIEX_ExecuteRequest( + handleData->httpApiExHandle, + HTTPAPI_REQUEST_POST, + STRING_c_str(deviceData->eventHTTPrelativePath), + clonedEventHTTPrequestHeaders, + toBeSend, + &statusCode, + NULL, + NULL + )) != HTTPAPIEX_OK) + { + LogError("Unable to HTTPAPIEX_ExecuteRequest."); + } + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_080: [If a deviceSasToken does not exist, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters] */ + if ((r = HTTPAPIEX_SAS_ExecuteRequest( + deviceData->sasObject, + handleData->httpApiExHandle, + HTTPAPI_REQUEST_POST, + STRING_c_str(deviceData->eventHTTPrelativePath), + clonedEventHTTPrequestHeaders, + toBeSend, + &statusCode, + NULL, + NULL + )) != HTTPAPIEX_OK) + { + LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); + } + } + if (r == HTTPAPIEX_OK) + { + if (statusCode < 300) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_082: [If HTTPAPIEX_SAS_ExecuteRequest does not fail and http status code <300 then IoTHubTransportHttp_DoWork shall call IoTHubClientCore_LL_SendComplete. Parameter PDLIST_ENTRY completed shall point to a list the item send, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_OK. The item shall be removed from waitingToSend.] */ + PDLIST_ENTRY justSent = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/ + DList_InsertTailList(&(deviceData->eventConfirmations), justSent); + IoTHubClientCore_LL_SendComplete(iotHubClientHandle, &(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_OK); /*takes care of emptying the list too*/ + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_081: [If HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */ + LogError("unexpected HTTP status code (%u)", statusCode); + } + } + } + BUFFER_delete(toBeSend); + } + } + } + } + HTTPHeaders_Free(clonedEventHTTPrequestHeaders); + } + } + } + } + } +} + +static bool abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* ETag, IOTHUBMESSAGE_DISPOSITION_RESULT action) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_097: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: + -requestType: POST + -relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14" + - requestHttpHeadersHandle: an HTTP headers instance containing the following + Authorization: " " + If-Match: value of ETag + - requestContent: NULL + - statusCode: a pointer to unsigned int which might be examined for logging + - responseHeadearsHandle: NULL + - responseContent: NULL]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_099: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: + -requestType: DELETE + -relativePath: abandon relative path begin + value of ETag + "?api-version=2016-11-14" + - requestHttpHeadersHandle: an HTTP headers instance containing the following + Authorization: " " + If-Match: value of ETag + - requestContent: NULL + - statusCode: a pointer to unsigned int which might be used by logging + - responseHeadearsHandle: NULL + - responseContent: NULL]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_101: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: + -requestType: DELETE + -relativePath: abandon relative path begin + value of ETag +"?api-version=2016-11-14" + "&reject" + - requestHttpHeadersHandle: an HTTP headers instance containing the following + Authorization: " " + If-Match: value of ETag + - requestContent: NULL + - statusCode: a pointer to unsigned int which might be used by logging + - responseHeadearsHandle: NULL + - responseContent: NULL]*/ + + bool result; + STRING_HANDLE fullAbandonRelativePath = STRING_clone(deviceData->abandonHTTPrelativePathBegin); + if (fullAbandonRelativePath == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to STRING_clone"); + result = false; + } + else + { + STRING_HANDLE ETagUnquoted = STRING_construct_n(ETag + 1, strlen(ETag) - 2); /*skip first character which is '"' and the last one (which is also '"')*/ + if (ETagUnquoted == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to STRING_construct_n"); + result = false; + } + else + { + if (!( + (STRING_concat_with_STRING(fullAbandonRelativePath, ETagUnquoted) == 0) && + (STRING_concat(fullAbandonRelativePath, (action == IOTHUBMESSAGE_ABANDONED) ? "/abandon" API_VERSION : ((action == IOTHUBMESSAGE_REJECTED) ? API_VERSION "&reject" : API_VERSION)) == 0) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to STRING_concat"); + result = false; + } + else + { + HTTP_HEADERS_HANDLE abandonRequestHttpHeaders = HTTPHeaders_Alloc(); + if (abandonRequestHttpHeaders == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to HTTPHeaders_Alloc"); + result = false; + } + else + { + if (!( + (addUserAgentHeaderInfo(deviceData->iotHubClientHandle, abandonRequestHttpHeaders) == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(abandonRequestHttpHeaders, "Authorization", " ") == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(abandonRequestHttpHeaders, "If-Match", ETag) == HTTP_HEADERS_OK) + )) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to HTTPHeaders_AddHeaderNameValuePair"); + result = false; + } + else + { + unsigned int statusCode = 0; + HTTPAPIEX_RESULT r; + if (deviceData->deviceSasToken != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/ + if (HTTPHeaders_ReplaceHeaderNameValuePair(abandonRequestHttpHeaders, "Authorization", STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK) + { + r = HTTPAPIEX_ERROR; + /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/ + LogError("Unable to replace the old SAS Token."); + result = false; + } + else if ((r = HTTPAPIEX_ExecuteRequest( + handleData->httpApiExHandle, + (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE, /*-requestType: POST */ + STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14" */ + abandonRequestHttpHeaders, /*- requestHttpHeadersHandle: an HTTP headers instance containing the following */ + NULL, /*- requestContent: NULL */ + &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */ + NULL, /*- responseHeadearsHandle: NULL */ + NULL /*- responseContent: NULL] */ + )) != HTTPAPIEX_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("Unable to HTTPAPIEX_ExecuteRequest."); + result = false; + } + } + else if ((r = HTTPAPIEX_SAS_ExecuteRequest( + deviceData->sasObject, + handleData->httpApiExHandle, + (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE, /*-requestType: POST */ + STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14" */ + abandonRequestHttpHeaders, /*- requestHttpHeadersHandle: an HTTP headers instance containing the following */ + NULL, /*- requestContent: NULL */ + &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */ + NULL, /*- responseHeadearsHandle: NULL */ + NULL /*- responseContent: NULL] */ + )) != HTTPAPIEX_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); + result = false; + } + if (r == HTTPAPIEX_OK) + { + if (statusCode != 204) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + LogError("unexpected status code returned %u (was expecting 204)", statusCode); + result = false; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ + /*all is fine*/ + result = true; + } + } + else + { + result = false; + } + } + HTTPHeaders_Free(abandonRequestHttpHeaders); + } + } + STRING_delete(ETagUnquoted); + } + STRING_delete(fullAbandonRelativePath); + } + return result; +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + IOTHUB_CLIENT_RESULT result; + if (message_data == NULL) + { + /* Codes_SRS_TRANSPORTMULTITHTTP_10_001: [If messageData is NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("invalid argument messageData is NULL"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (message_data->messageHandle == NULL) + { + /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("invalid message handle"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)(message_data->transportContext); + if (tc == NULL) + { + /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("invalid transport context data"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if ((tc->handleData == NULL) || (tc->deviceData == NULL) || (tc->etagValue == NULL)) + { + /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */ + LogError("invalid transport context data"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if (abandonOrAcceptMessage(tc->handleData, tc->deviceData, tc->etagValue, disposition)) + { + result = IOTHUB_CLIENT_OK; + } + else + { + /* Codes_SRS_TRANSPORTMULTITHTTP_10_003: [IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR if the POST message fails, otherwise return IOTHUB_CLIENT_OK.] */ + LogError("HTTP Transport layer failed to report %s disposition", ENUM_TO_STRING(IOTHUBMESSAGE_DISPOSITION_RESULT, disposition)); + result = IOTHUB_CLIENT_ERROR; + } + } + free(tc->etagValue); + free(tc); + } + IoTHubMessage_Destroy(message_data->messageHandle); + } + free(message_data); + } + return result; +} + +static MESSAGE_CALLBACK_INFO* MESSAGE_CALLBACK_INFO_Create(IOTHUB_MESSAGE_HANDLE received_message, HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* etagValue) +{ + MESSAGE_CALLBACK_INFO* result = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO)); + if (result == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */ + LogError("malloc failed"); + } + else + { + MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)malloc(sizeof(MESSAGE_DISPOSITION_CONTEXT)); + if (tc == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */ + LogError("malloc failed"); + free(result); + result = NULL; + } + else + { + result->messageHandle = IoTHubMessage_Clone(received_message); + if (result->messageHandle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_10_007: [If assembling a message clone, _DoWork shall "abandon" the message.]*/ + LogError("IoTHubMessage_Clone failed"); + free(tc); + free(result); + result = NULL; + } + else + { + if (mallocAndStrcpy_s(&tc->etagValue, etagValue) != 0) + { + LogError("mallocAndStrcpy_s failed"); + free(tc); + free(result); + result = NULL; + } + else + { + tc->handleData = handleData; + tc->deviceData = deviceData; + + result->transportContext = tc; + } + } + } + } + return result; +} + +static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_083: [ If device is not subscribed then _DoWork shall advance to the next action. ] */ + if (deviceData->DoWork_PullMessage) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_123: [After client creation, the first GET shall be allowed no matter what the value of GetMinimumPollingTime.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_124: [If time is not available then all calls shall be treated as if they are the first one.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_122: [A GET request that happens earlier than GetMinimumPollingTime shall be ignored.] */ + time_t timeNow = get_time(NULL); + bool isPollingAllowed = deviceData->isFirstPoll || (timeNow == (time_t)(-1)) || (get_difftime(timeNow, deviceData->lastPollTime) > handleData->getMinimumPollingTime); + if (isPollingAllowed) + { + HTTP_HEADERS_HANDLE responseHTTPHeaders = HTTPHeaders_Alloc(); + if (responseHTTPHeaders == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ + LogError("unable to HTTPHeaders_Alloc"); + } + else + { + BUFFER_HANDLE responseContent = BUFFER_new(); + if (responseContent == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ + LogError("unable to BUFFER_new"); + } + else + { + unsigned int statusCode = 0; + HTTPAPIEX_RESULT r; + if (deviceData->deviceSasToken != NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/ + if (HTTPHeaders_ReplaceHeaderNameValuePair(deviceData->messageHTTPrequestHeaders, "Authorization", STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK) + { + r = HTTPAPIEX_ERROR; + /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/ + LogError("Unable to replace the old SAS Token."); + } + else if ((r = HTTPAPIEX_ExecuteRequest( + handleData->httpApiExHandle, + HTTPAPI_REQUEST_GET, /*requestType: GET*/ + STRING_c_str(deviceData->messageHTTPrelativePath), /*relativePath: the message HTTP relative path*/ + deviceData->messageHTTPrequestHeaders, /*requestHttpHeadersHandle: message HTTP request headers created by _Create*/ + NULL, /*requestContent: NULL*/ + &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/ + responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/ + responseContent /*responseContent: a new instance of buffer*/ + )) != HTTPAPIEX_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ + LogError("Unable to HTTPAPIEX_ExecuteRequest."); + } + } + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_084: [Otherwise, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters + requestType: GET + relativePath: the message HTTP relative path + requestHttpHeadersHandle: message HTTP request headers created by _Create + requestContent: NULL + statusCode: a pointer to unsigned int which shall be later examined + responseHeadearsHandle: a new instance of HTTP headers + responseContent: a new instance of buffer] + */ + else if ((r = HTTPAPIEX_SAS_ExecuteRequest( + deviceData->sasObject, + handleData->httpApiExHandle, + HTTPAPI_REQUEST_GET, /*requestType: GET*/ + STRING_c_str(deviceData->messageHTTPrelativePath), /*relativePath: the message HTTP relative path*/ + deviceData->messageHTTPrequestHeaders, /*requestHttpHeadersHandle: message HTTP request headers created by _Create*/ + NULL, /*requestContent: NULL*/ + &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/ + responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/ + responseContent /*responseContent: a new instance of buffer*/ + )) != HTTPAPIEX_OK) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ + LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); + } + if (r == HTTPAPIEX_OK) + { + /*HTTP dialogue was succesfull*/ + if (timeNow == (time_t)(-1)) + { + deviceData->isFirstPoll = true; + } + else + { + deviceData->isFirstPoll = false; + deviceData->lastPollTime = timeNow; + } + if (statusCode == 204) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_086: [If the HTTPAPIEX_SAS_ExecuteRequest executed successfully then status code shall be examined. Any status code different than 200 causes _DoWork to advance to the next action.] */ + /*this is an expected status code, means "no commands", but logging that creates panic*/ + + /*do nothing, advance to next action*/ + } + else if (statusCode != 200) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_086: [If the HTTPAPIEX_SAS_ExecuteRequest executed successfully then status code shall be examined. Any status code different than 200 causes _DoWork to advance to the next action.] */ + LogError("expected status code was 200, but actually was received %u... moving on", statusCode); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_087: [If status code is 200, then _DoWork shall make a copy of the value of the "ETag" http header.]*/ + const char* etagValue = HTTPHeaders_FindHeaderValue(responseHTTPHeaders, "ETag"); + if (etagValue == NULL) + { + LogError("unable to find a received header called \"E-Tag\""); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_088: [If no such header is found or is invalid, then _DoWork shall advance to the next action.]*/ + size_t etagsize = strlen(etagValue); + if ( + (etagsize < 2) || + (etagValue[0] != '"') || + (etagValue[etagsize - 1] != '"') + ) + { + LogError("ETag is not a valid quoted string"); + } + else + { + const unsigned char* resp_content; + size_t resp_len; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_089: [_DoWork shall assemble an IOTHUBMESSAGE_HANDLE from the received HTTP content (using the responseContent buffer).] */ + resp_content = BUFFER_u_char(responseContent); + resp_len = BUFFER_length(responseContent); + IOTHUB_MESSAGE_HANDLE receivedMessage = IoTHubMessage_CreateFromByteArray(resp_content, resp_len); + if (receivedMessage == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_092: [If assembling the message fails in any way, then _DoWork shall "abandon" the message.]*/ + LogError("unable to IoTHubMessage_CreateFromByteArray, trying to abandon the message... "); + if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED)) + { + LogError("HTTP Transport layer failed to report ABANDON disposition"); + } + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_090: [All the HTTP headers of the form iothub-app-name:somecontent shall be transformed in message properties {name, somecontent}.]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_091: [The HTTP header of iothub-messageid shall be set in the MessageId.]*/ + size_t nHeaders; + if (HTTPHeaders_GetHeaderCount(responseHTTPHeaders, &nHeaders) != HTTP_HEADERS_OK) + { + LogError("unable to get the count of HTTP headers"); + if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED)) + { + LogError("HTTP Transport layer failed to report ABANDON disposition"); + } + } + else + { + size_t i; + MAP_HANDLE properties = (nHeaders > 0) ? IoTHubMessage_Properties(receivedMessage) : NULL; + for (i = 0; i < nHeaders; i++) + { + char* completeHeader; + if (HTTPHeaders_GetHeader(responseHTTPHeaders, i, &completeHeader) != HTTP_HEADERS_OK) + { + break; + } + else + { + if (strncmp(IOTHUB_APP_PREFIX, completeHeader, strlen(IOTHUB_APP_PREFIX)) == 0) + { + /*looks like a property headers*/ + /*there's a guaranteed ':' in the completeHeader, by HTTP_HEADERS module*/ + char* whereIsColon = strchr(completeHeader, ':'); + if (whereIsColon != NULL) + { + *whereIsColon = '\0'; /*cut it down*/ + if (Map_AddOrUpdate(properties, completeHeader + strlen(IOTHUB_APP_PREFIX), whereIsColon + 2) != MAP_OK) /*whereIsColon+1 is a space because HTTPEHADERS outputs a ": " between name and value*/ + { + free(completeHeader); + break; + } + } + } + else if (strncmp(IOTHUB_MESSAGE_ID, completeHeader, strlen(IOTHUB_MESSAGE_ID)) == 0) + { + char* whereIsColon = strchr(completeHeader, ':'); + if (whereIsColon != NULL) + { + *whereIsColon = '\0'; /*cut it down*/ + if (IoTHubMessage_SetMessageId(receivedMessage, whereIsColon + 2) != IOTHUB_MESSAGE_OK) + { + free(completeHeader); + break; + } + } + } + else if (strncmp(IOTHUB_CORRELATION_ID, completeHeader, strlen(IOTHUB_CORRELATION_ID)) == 0) + { + char* whereIsColon = strchr(completeHeader, ':'); + if (whereIsColon != NULL) + { + *whereIsColon = '\0'; /*cut it down*/ + if (IoTHubMessage_SetCorrelationId(receivedMessage, whereIsColon + 2) != IOTHUB_MESSAGE_OK) + { + free(completeHeader); + break; + } + } + } + // Codes_SRS_TRANSPORTMULTITHTTP_09_003: [ The HTTP header value of `ContentType` shall be set in the `IoTHubMessage_SetContentTypeSystemProperty`. ] + else if (strncmp(IOTHUB_CONTENT_TYPE_C2D, completeHeader, strlen(IOTHUB_CONTENT_TYPE_C2D)) == 0) + { + char* whereIsColon = strchr(completeHeader, ':'); + if (whereIsColon != NULL) + { + *whereIsColon = '\0'; /*cut it down*/ + if (IoTHubMessage_SetContentTypeSystemProperty(receivedMessage, whereIsColon + 2) != IOTHUB_MESSAGE_OK) + { + LogError("Failed setting IoTHubMessage content-type"); + free(completeHeader); + break; + } + } + } + // Codes_SRS_TRANSPORTMULTITHTTP_09_004: [ The HTTP header value of `ContentEncoding` shall be set in the `IoTHub_SetContentEncoding`. ] + else if (strncmp(IOTHUB_CONTENT_ENCODING_C2D, completeHeader, strlen(IOTHUB_CONTENT_ENCODING_C2D)) == 0) + { + char* whereIsColon = strchr(completeHeader, ':'); + if (whereIsColon != NULL) + { + *whereIsColon = '\0'; /*cut it down*/ + if (IoTHubMessage_SetContentEncodingSystemProperty(receivedMessage, whereIsColon + 2) != IOTHUB_MESSAGE_OK) + { + LogError("Failed setting IoTHubMessage content-encoding"); + free(completeHeader); + break; + } + } + } + + free(completeHeader); + } + } + + if (i < nHeaders) + { + if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED)) + { + LogError("HTTP Transport layer failed to report ABANDON disposition"); + } + } + else + { + MESSAGE_CALLBACK_INFO* messageData = MESSAGE_CALLBACK_INFO_Create(receivedMessage, handleData, deviceData, etagValue); + if (messageData == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */ + LogError("failed to assemble callback info"); + if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED)) + { + LogError("HTTP Transport layer failed to report ABANDON disposition"); + } + } + else + { + bool abandon; + if (IoTHubClientCore_LL_MessageCallback(iotHubClientHandle, messageData)) + { + abandon = false; + } + else + { + LogError("IoTHubClientCore_LL_MessageCallback failed"); + abandon = true; + } + + /*Codes_SRS_TRANSPORTMULTITHTTP_17_096: [If IoTHubClientCore_LL_MessageCallback returns false then _DoWork shall "abandon" the message.] */ + if (abandon) + { + (void)IoTHubTransportHttp_SendMessageDisposition(messageData, IOTHUBMESSAGE_ABANDONED); + } + } + } + } + IoTHubMessage_Destroy(receivedMessage); + } + } + } + } + } + BUFFER_delete(responseContent); + } + HTTPHeaders_Free(responseHTTPHeaders); + } + } + else + { + /*isPollingAllowed is false... */ + /*do nothing "shall be ignored*/ + } + } +} + +static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportHttp_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + (void)handle; + (void)item_type; + (void)iothub_item; + LogError("Currently Not Supported."); + return IOTHUB_PROCESS_ERROR; +} + +static void IoTHubTransportHttp_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_049: [ If handle is NULL, then IoTHubTransportHttp_DoWork shall do nothing. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_140: [ If iotHubClientHandle is NULL, then IoTHubTransportHttp_DoWork shall do nothing. ]*/ + + (void)iotHubClientHandle; // use the perDevice handle. + if (handle != NULL) + { + HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle; + IOTHUB_DEVICE_HANDLE* listItem; + size_t deviceListSize = VECTOR_size(handleData->perDeviceList); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_052: [ IoTHubTransportHttp_DoWork shall perform a round-robin loop through every deviceHandle in the transport device list, using the iotHubClientHandle field saved in the IOTHUB_DEVICE_HANDLE. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_050: [ IoTHubTransportHttp_DoWork shall call loop through the device list. ] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_051: [ IF the list is empty, then IoTHubTransportHttp_DoWork shall do nothing. ]*/ + for (size_t i = 0; i < deviceListSize; i++) + { + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i); + HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = *(HTTPTRANSPORT_PERDEVICE_DATA**)(listItem); + DoEvent(handleData, perDeviceItem, perDeviceItem->iotHubClientHandle); + DoMessages(handleData, perDeviceItem, perDeviceItem->iotHubClientHandle); + + } + } + else + { + LogError("Invalid Argument NULL call on DoWork."); + } +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + IOTHUB_CLIENT_RESULT result; + + if (handle == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_111: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter. ]*/ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Invalid handle to IoTHubClient HTTP transport instance."); + } + else if (iotHubClientStatus == NULL) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Invalid pointer to output parameter IOTHUB_CLIENT_STATUS."); + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_138: [ IoTHubTransportHttp_GetSendStatus shall locate deviceHandle in the transport device list by calling list_find_if. ]*/ + IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle); + if (listItem == NULL) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_139: [ If the device structure is not found, then this function shall fail and return with IOTHUB_CLIENT_INVALID_ARG. ]*/ + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("Device not found in transport list."); + } + else + { + HTTPTRANSPORT_PERDEVICE_DATA* deviceData = (HTTPTRANSPORT_PERDEVICE_DATA*)(*listItem); + /* Codes_SRS_TRANSPORTMULTITHTTP_17_113: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent. ] */ + if (!DList_IsListEmpty(deviceData->waitingToSend)) + { + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY; + } + /* Codes_SRS_TRANSPORTMULTITHTTP_17_112: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent. ] */ + else + { + *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE; + } + result = IOTHUB_CLIENT_OK; + } + } + + return result; +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_114: [If handle parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_115: [If option parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_116: [If value parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */ + if ( + (handle == NULL) || + (option == NULL) || + (value == NULL) + ) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("invalid parameter (NULL) passed to IoTHubTransportHttp_SetOption"); + } + else + { + HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle; + /*Codes_SRS_TRANSPORTMULTITHTTP_17_120: ["Batching"] */ + if (strcmp(OPTION_BATCHING, option) == 0) + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_117: [If optionName is an option handled by IoTHubTransportHttp then it shall be set.] */ + handleData->doBatchedTransfers = *(bool*)value; + result = IOTHUB_CLIENT_OK; + } + /*Codes_SRS_TRANSPORTMULTITHTTP_17_121: ["MinimumPollingTime"] */ + else if (strcmp(OPTION_MIN_POLLING_TIME, option) == 0) + { + handleData->getMinimumPollingTime = *(unsigned int*)value; + result = IOTHUB_CLIENT_OK; + } + else + { + /*Codes_SRS_TRANSPORTMULTITHTTP_17_126: [ "TrustedCerts"] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_127: [ NULL shall be allowed. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_129: [ This option shall passed down to the lower layer by calling HTTPAPIEX_SetOption. ]*/ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_118: [Otherwise, IoTHubTransport_Http shall call HTTPAPIEX_SetOption with the same parameters and return the translated code.] */ + HTTPAPIEX_RESULT HTTPAPIEX_result = HTTPAPIEX_SetOption(handleData->httpApiExHandle, option, value); + /*Codes_SRS_TRANSPORTMULTITHTTP_17_119: [The following table translates HTTPAPIEX return codes to IOTHUB_CLIENT_RESULT return codes:] */ + if (HTTPAPIEX_result == HTTPAPIEX_OK) + { + result = IOTHUB_CLIENT_OK; + } + else if (HTTPAPIEX_result == HTTPAPIEX_INVALID_ARG) + { + result = IOTHUB_CLIENT_INVALID_ARG; + LogError("HTTPAPIEX_SetOption failed"); + } + else + { + result = IOTHUB_CLIENT_ERROR; + LogError("HTTPAPIEX_SetOption failed"); + } + } + } + return result; +} + +static STRING_HANDLE IoTHubTransportHttp_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + STRING_HANDLE result; + /*Codes_SRS_TRANSPORTMULTITHTTP_02_001: [ If handle is NULL then IoTHubTransportHttp_GetHostname shall fail and return NULL. ]*/ + if (handle == NULL) + { + LogError("invalid parameter handle=%p", handle); + result = NULL; + } + /*Codes_SRS_TRANSPORTMULTITHTTP_02_002: [ Otherwise IoTHubTransportHttp_GetHostname shall return a non-NULL STRING_HANDLE containing the hostname. ]*/ + else if ((result = STRING_clone(((HTTPTRANSPORT_HANDLE_DATA*)(handle))->hostName)) == NULL) + { + LogError("Cannot provide the target host name (STRING_clone failed)."); + } + + return result; +} + +static int IoTHubTransportHttp_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + int result; + (void)handle; + (void)retryPolicy; + (void)retryTimeoutLimitInSeconds; + + /* Retry Policy is not currently not available for HTTP */ + + result = 0; + return result; +} + +static int IotHubTransportHttp_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("HTTP does not support input queues"); + return __FAILURE__; +} + +static void IotHubTransportHttp_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("HTTP does not support input queues"); +} + + +/*Codes_SRS_TRANSPORTMULTITHTTP_17_125: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for its fields:] */ +static TRANSPORT_PROVIDER thisTransportProvider = +{ + IoTHubTransportHttp_SendMessageDisposition, /*pfIotHubTransport_SendMessageDisposition IoTHubTransport_SendMessageDisposition;*/ + IoTHubTransportHttp_Subscribe_DeviceMethod, /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/ + IoTHubTransportHttp_Unsubscribe_DeviceMethod, /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/ + IoTHubTransportHttp_DeviceMethod_Response, /*pfIoTHubTransport_DeviceMethod_Response IoTHubTransport_DeviceMethod_Response;*/ + IoTHubTransportHttp_Subscribe_DeviceTwin, /*pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin;*/ + IoTHubTransportHttp_Unsubscribe_DeviceTwin, /*pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin;*/ + IoTHubTransportHttp_ProcessItem, /*pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem;*/ + IoTHubTransportHttp_GetHostname, /*pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname;*/ + IoTHubTransportHttp_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption;*/ + IoTHubTransportHttp_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create;*/ + IoTHubTransportHttp_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy;*/ + IoTHubTransportHttp_Register, /*pfIotHubTransport_Register IoTHubTransport_Register;*/ + IoTHubTransportHttp_Unregister, /*pfIotHubTransport_Unregister IoTHubTransport_Unegister;*/ + IoTHubTransportHttp_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe;*/ + IoTHubTransportHttp_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe;*/ + IoTHubTransportHttp_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork;*/ + IoTHubTransportHttp_SetRetryPolicy, /*pfIoTHubTransport_DoWork IoTHubTransport_SetRetryPolicy;*/ + IoTHubTransportHttp_GetSendStatus, /*pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus;*/ + IotHubTransportHttp_Subscribe_InputQueue, /*pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; */ + IotHubTransportHttp_Unsubscribe_InputQueue /*pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue; */ +}; + +const TRANSPORT_PROVIDER* HTTP_Protocol(void) +{ + return &thisTransportProvider; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransportmqtt.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "iothubtransportmqtt.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" +#include "internal/iothubtransport_mqtt_common.h" +#include "azure_c_shared_utility/xlogging.h" + +static XIO_HANDLE getIoTransportProvider(const char* fully_qualified_name, const MQTT_TRANSPORT_PROXY_OPTIONS* mqtt_transport_proxy_options) +{ + XIO_HANDLE result; + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_001: [ `getIoTransportProvider` shall obtain the TLS IO interface handle by calling `platform_get_default_tlsio`. ]*/ + const IO_INTERFACE_DESCRIPTION* io_interface_description = platform_get_default_tlsio(); + (void)mqtt_transport_proxy_options; + + if (io_interface_description == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_013: [ If `platform_get_default_tlsio` returns NULL, `getIoTransportProvider` shall return NULL. ] */ + LogError("Failure constructing the provider interface"); + result = NULL; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_002: [ The TLS IO parameters shall be a `TLSIO_CONFIG` structure filled as below: ]*/ + TLSIO_CONFIG tls_io_config; + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_003: [ - `hostname` shall be set to `fully_qualified_name`. ]*/ + tls_io_config.hostname = fully_qualified_name; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_004: [ - `port` shall be set to 8883. ]*/ + tls_io_config.port = 8883; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_005: [ - `underlying_io_interface` shall be set to NULL. ]*/ + tls_io_config.underlying_io_interface = NULL; + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_01_006: [ - `underlying_io_parameters` shall be set to NULL. ]*/ + tls_io_config.underlying_io_parameters = NULL; + + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_012: [ `getIoTransportProvider` shall return the `XIO_HANDLE` returned by `xio_create`. ] */ + result = xio_create(io_interface_description, &tls_io_config); + } + return result; +} + +static TRANSPORT_LL_HANDLE IoTHubTransportMqtt_Create(const IOTHUBTRANSPORT_CONFIG* config) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_001: [IoTHubTransportMqtt_Create shall create a TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Create function.] */ + return IoTHubTransport_MQTT_Common_Create(config, getIoTransportProvider); +} + +static void IoTHubTransportMqtt_Destroy(TRANSPORT_LL_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [ IoTHubTransportMqtt_Destroy shall destroy the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Destroy function. ] */ + IoTHubTransport_MQTT_Common_Destroy(handle); +} + +static int IoTHubTransportMqtt_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_005: [ IoTHubTransportMqtt_Subscribe shall subscribe the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Subscribe function. ] */ + return IoTHubTransport_MQTT_Common_Subscribe(handle); +} + +static void IoTHubTransportMqtt_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_006: [ IoTHubTransportMqtt_Unsubscribe shall unsubscribe the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Unsubscribe function. ] */ + IoTHubTransport_MQTT_Common_Unsubscribe(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_026: [ IoTHubTransportMqtt_Subscribe_DeviceMethod shall subscribe the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Subscribe_DeviceMethod function. ] */ +static int IoTHubTransportMqtt_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [ IoTHubTransportMqtt_Unsubscribe_DeviceMethod shall call into the IoTHubMqttAbstract_Unsubscribe_DeviceMethod function. ] */ +static void IoTHubTransportMqtt_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_025: [ IoTHubTransportMqtt_Subscribe_DeviceTwin shall call into the IoTHubMqttAbstract_Subscribe_DeviceTwin function. ] */ +static int IoTHubTransportMqtt_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_024: [ IoTHubTransportMqtt_Unsubscribe_DeviceTwin shall shall call into the IoTHubMqttAbstract_Unsubscribe_DeviceTwin function. ] */ +static void IoTHubTransportMqtt_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_023: [ IoTHubTransportMqtt_DeviceMethod_Response shall call into the IoTHubMqttAbstract_DeviceMethod_Response function. ] */ +static int IoTHubTransportMqtt_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + return IoTHubTransport_MQTT_Common_DeviceMethod_Response(handle, methodId, response, response_size, status_response); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_10_001: [IoTHubTransportMqtt_SendMessageDisposition shall send the message disposition by calling into the IoTHubMqttAbstract_SendMessageDisposition function.] */ +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + return IoTHubTransport_MQTT_Common_SendMessageDisposition(message_data, disposition); +} + +static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportMqtt_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + return IoTHubTransport_MQTT_Common_ProcessItem(handle, item_type, iothub_item); +} + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_054: [ IoTHubTransportMqtt_DoWork shall subscribe to the Notification and get_state Topics if they are defined. ] */ +static void IoTHubTransportMqtt_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_007: [ IoTHubTransportMqtt_DoWork shall call into the IoTHubMqttAbstract_DoWork function. ] */ + IoTHubTransport_MQTT_Common_DoWork(handle, iotHubClientHandle); +} + +static int IoTHubTransportMqtt_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_25_012: [** IoTHubTransportMqtt_SetRetryPolicy shall call into the IoTHubMqttAbstract_SetRetryPolicy function. ] */ + return IoTHubTransport_MQTT_Common_SetRetryPolicy(handle, retryPolicy, retryTimeoutLimitInSeconds); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_008: [ IoTHubTransportMqtt_GetSendStatus shall get the send status by calling into the IoTHubMqttAbstract_GetSendStatus function. ] */ + return IoTHubTransport_MQTT_Common_GetSendStatus(handle, iotHubClientStatus); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_009: [ IoTHubTransportMqtt_SetOption shall set the options by calling into the IoTHubMqttAbstract_SetOption function. ] */ + return IoTHubTransport_MQTT_Common_SetOption(handle, option, value); +} + +static IOTHUB_DEVICE_HANDLE IoTHubTransportMqtt_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [ IoTHubTransportMqtt_Register shall register the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Register function. ] */ + return IoTHubTransport_MQTT_Common_Register(handle, device, iotHubClientHandle, waitingToSend); +} + +static void IoTHubTransportMqtt_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_004: [ IoTHubTransportMqtt_Unregister shall register the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Unregister function. ] */ + IoTHubTransport_MQTT_Common_Unregister(deviceHandle); +} + +static STRING_HANDLE IoTHubTransportMqtt_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_010: [ IoTHubTransportMqtt_GetHostname shall get the hostname by calling into the IoTHubMqttAbstract_GetHostname function. ] */ + return IoTHubTransport_MQTT_Common_GetHostname(handle); +} + +static int IotHubTransportMqtt_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_31_14: [ IoTHubTransportMqtt_Subscribe shall subscribe the TRANSPORT_LL_HANDLE by calling into IoTHubTransport_MQTT_Common_Subscribe_InputQueue. ] */ + return IoTHubTransport_MQTT_Common_Subscribe_InputQueue(handle); +} + +static void IotHubTransportMqtt_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + /* Codes_SRS_IOTHUBTRANSPORTAMQP_31_015: [IotHubTransportMQTT_Unsubscribe_InputQueue shall unsubscribe by calling into IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue. ] */ + IoTHubTransport_MQTT_Common_Unsubscribe_InputQueue(handle); +} + + +static TRANSPORT_PROVIDER myfunc = +{ + IoTHubTransportMqtt_SendMessageDisposition, /*pfIotHubTransport_SendMessageDisposition IoTHubTransport_SendMessageDisposition;*/ + IoTHubTransportMqtt_Subscribe_DeviceMethod, /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/ + IoTHubTransportMqtt_Unsubscribe_DeviceMethod, /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/ + IoTHubTransportMqtt_DeviceMethod_Response, /*pfIoTHubTransport_DeviceMethod_Response IoTHubTransport_DeviceMethod_Response;*/ + IoTHubTransportMqtt_Subscribe_DeviceTwin, /*pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin;*/ + IoTHubTransportMqtt_Unsubscribe_DeviceTwin, /*pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin;*/ + IoTHubTransportMqtt_ProcessItem, /*pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem;*/ + IoTHubTransportMqtt_GetHostname, /*pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname;*/ + IoTHubTransportMqtt_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption;*/ + IoTHubTransportMqtt_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create;*/ + IoTHubTransportMqtt_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy;*/ + IoTHubTransportMqtt_Register, /*pfIotHubTransport_Register IoTHubTransport_Register;*/ + IoTHubTransportMqtt_Unregister, /*pfIotHubTransport_Unregister IoTHubTransport_Unegister;*/ + IoTHubTransportMqtt_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe;*/ + IoTHubTransportMqtt_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe;*/ + IoTHubTransportMqtt_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork;*/ + IoTHubTransportMqtt_SetRetryPolicy, /*pfIoTHubTransport_DoWork IoTHubTransport_SetRetryPolicy;*/ + IoTHubTransportMqtt_GetSendStatus, /*pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus;*/ + IotHubTransportMqtt_Subscribe_InputQueue, /*pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; */ + IotHubTransportMqtt_Unsubscribe_InputQueue /*pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue; */ +}; + +/* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_022: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER */ +extern const TRANSPORT_PROVIDER* MQTT_Protocol(void) +{ + return &myfunc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/iothubtransportmqtt_websockets.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,262 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/wsio.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/http_proxy_io.h" +#include "iothubtransportmqtt_websockets.h" +#include "internal/iothubtransport_mqtt_common.h" + +static XIO_HANDLE getWebSocketsIOTransport(const char* fully_qualified_name, const MQTT_TRANSPORT_PROXY_OPTIONS* mqtt_transport_proxy_options) +{ + XIO_HANDLE result; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_001: [ `getIoTransportProvider` shall obtain the WebSocket IO interface handle by calling `wsio_get_interface_description`. ]*/ + const IO_INTERFACE_DESCRIPTION* io_interface_description = wsio_get_interface_description(); + TLSIO_CONFIG tls_io_config; + HTTP_PROXY_IO_CONFIG http_proxy_io_config; + + if (io_interface_description == NULL) + { + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_013: [ If `wsio_get_interface_description` returns NULL `getIoTransportProvider` shall return NULL. ] */ + LogError("Failure constructing the provider interface"); + result = NULL; + } + else + { + WSIO_CONFIG ws_io_config; + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_003: [ - `hostname` shall be set to `fully_qualified_name`. ]*/ + ws_io_config.hostname = fully_qualified_name; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_004: [ - `port` shall be set to 443. ]*/ + ws_io_config.port = 443; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_005: [ - `protocol` shall be set to `MQTT`. ]*/ + ws_io_config.protocol = "MQTT"; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_006: [ - `resource_name` shall be set to `/$iothub/websocket`. ]*/ + ws_io_config.resource_name = "/$iothub/websocket"; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_007: [ - `underlying_io_interface` shall be set to the TLS IO interface description. ]*/ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_009: [ `getIoTransportProvider` shall obtain the TLS IO interface handle by calling `platform_get_default_tlsio`. ]*/ + ws_io_config.underlying_io_interface = platform_get_default_tlsio(); + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_029: [ If `platform_get_default_tlsio` returns NULL, NULL shall be set in the WebSocket IO parameters structure for the interface description and parameters. ]*/ + if (ws_io_config.underlying_io_interface == NULL) + { + ws_io_config.underlying_io_parameters = NULL; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_008: [ - `underlying_io_parameters` shall be set to the TLS IO arguments. ]*/ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_010: [ The TLS IO parameters shall be a TLSIO_CONFIG structure filled as below: ]*/ + ws_io_config.underlying_io_parameters = &tls_io_config; + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_011: [ - `hostname` shall be set to `fully_qualified_name`. ]*/ + tls_io_config.hostname = fully_qualified_name; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_012: [ - `port` shall be set to 443. ]*/ + tls_io_config.port = 443; + + if (mqtt_transport_proxy_options != NULL) + { + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_015: [ - If `mqtt_transport_proxy_options` is not NULL, `underlying_io_interface` shall be set to the HTTP proxy IO interface description. ]*/ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_022: [ `getIoTransportProvider` shall obtain the HTTP proxy IO interface handle by calling `http_proxy_io_get_interface_description`. ]*/ + tls_io_config.underlying_io_interface = http_proxy_io_get_interface_description(); + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_028: [ If `http_proxy_io_get_interface_description` returns NULL, NULL shall be set in the TLS IO parameters structure for the interface description and parameters. ]*/ + if (tls_io_config.underlying_io_interface == NULL) + { + tls_io_config.underlying_io_parameters = NULL; + } + else + { + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_016: [ - If `mqtt_transport_proxy_options` is not NULL `underlying_io_parameters` shall be set to the HTTP proxy IO arguments. ]*/ + tls_io_config.underlying_io_parameters = &http_proxy_io_config; + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_023: [ The HTTP proxy IO arguments shall be an `HTTP_PROXY_IO_CONFIG` structure, filled as below: ]*/ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_026: [ - `proxy_hostname`, `proxy_port`, `username` and `password` shall be copied from the `mqtt_transport_proxy_options` argument. ]*/ + http_proxy_io_config.proxy_hostname = mqtt_transport_proxy_options->host_address; + http_proxy_io_config.proxy_port = mqtt_transport_proxy_options->port; + http_proxy_io_config.username = mqtt_transport_proxy_options->username; + http_proxy_io_config.password = mqtt_transport_proxy_options->password; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_024: [ - `hostname` shall be set to `fully_qualified_name`. ]*/ + http_proxy_io_config.hostname = fully_qualified_name; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_025: [ - `port` shall be set to 443. ]*/ + http_proxy_io_config.port = 443; + } + } + else + { + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_013: [ - If `mqtt_transport_proxy_options` is NULL, `underlying_io_interface` shall be set to NULL ]*/ + tls_io_config.underlying_io_interface = NULL; + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_014: [ - If `mqtt_transport_proxy_options` is NULL `underlying_io_parameters` shall be set to NULL. ]*/ + tls_io_config.underlying_io_parameters = NULL; + } + } + + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_012: [ `getIoTransportProvider` shall return the `XIO_HANDLE` returned by `xio_create`. ] */ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_01_002: [ `getIoTransportProvider` shall call `xio_create` while passing the WebSocket IO interface description to it and the WebSocket configuration as a WSIO_CONFIG structure, filled as below ]*/ + result = xio_create(io_interface_description, &ws_io_config); + } + return result; +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_001: [ IoTHubTransportMqtt_WS_Create shall create a TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Create function. ] */ +static TRANSPORT_LL_HANDLE IoTHubTransportMqtt_WS_Create(const IOTHUBTRANSPORT_CONFIG* config) +{ + return IoTHubTransport_MQTT_Common_Create(config, getWebSocketsIOTransport); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_002: [ IoTHubTransportMqtt_WS_Destroy shall destroy the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Destroy function. ] */ +static void IoTHubTransportMqtt_WS_Destroy(TRANSPORT_LL_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Destroy(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_005: [ IoTHubTransportMqtt_WS_Subscribe shall subscribe the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Subscribe function. ] */ +static int IoTHubTransportMqtt_WS_Subscribe(IOTHUB_DEVICE_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_Subscribe(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_006: [ IoTHubTransportMqtt_WS_Unsubscribe shall unsubscribe the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Unsubscribe function. ] */ +static void IoTHubTransportMqtt_WS_Unsubscribe(IOTHUB_DEVICE_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Unsubscribe(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_015: [ IoTHubTransportMqtt_WS_Subscribe_DeviceMethod shall call into the IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod function ] */ +static int IoTHubTransportMqtt_WS_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_Subscribe_DeviceMethod(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_016: [ IoTHubTransportMqtt_WS_Unsubscribe_DeviceMethod shall call into the IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod ] */ +static void IoTHubTransportMqtt_WS_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod(handle); +} + +static int IoTHubTransportMqtt_WS_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response) +{ + return IoTHubTransport_MQTT_Common_DeviceMethod_Response(handle, methodId, response, response_size, status_response); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_017: [ IoTHubTransportMqtt_WS_Subscribe_DeviceTwin shall call into the IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin ] */ +static int IoTHubTransportMqtt_WS_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_Subscribe_DeviceTwin(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_018: [ IoTHubTransportMqtt_WS_Unsubscribe_DeviceTwin shall call into the IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin ] */ +static void IoTHubTransportMqtt_WS_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle) +{ + IoTHubTransport_MQTT_Common_Unsubscribe_DeviceTwin(handle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_014: [ IoTHubTransportMqtt_WS_ProcessItem shall call into the IoTHubTransport_MQTT_Common_DoWork function ] */ +static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportMqtt_WS_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item) +{ + return IoTHubTransport_MQTT_Common_ProcessItem(handle, item_type, iothub_item); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_007: [ IoTHubTransportMqtt_WS_DoWork shall call into the IoTHubMqttAbstract_DoWork function. ] */ +static void IoTHubTransportMqtt_WS_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + IoTHubTransport_MQTT_Common_DoWork(handle, iotHubClientHandle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_008: [ IoTHubTransportMqtt_WS_GetSendStatus shall get the send status by calling into the IoTHubMqttAbstract_GetSendStatus function. ] */ +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_WS_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus) +{ + return IoTHubTransport_MQTT_Common_GetSendStatus(handle, iotHubClientStatus); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_009: [ IoTHubTransportMqtt_WS_SetOption shall set the options by calling into the IoTHubMqttAbstract_SetOption function. ] */ +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_WS_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value) +{ + return IoTHubTransport_MQTT_Common_SetOption(handle, option, value); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_003: [ IoTHubTransportMqtt_WS_Register shall register the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Register function. ]*/ +static IOTHUB_DEVICE_HANDLE IoTHubTransportMqtt_WS_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) +{ + return IoTHubTransport_MQTT_Common_Register(handle, device, iotHubClientHandle, waitingToSend); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_004: [ IoTHubTransportMqtt_WS_Unregister shall register the TRANSPORT_LL_HANDLE by calling into the IoTHubMqttAbstract_Unregister function. ] */ +static void IoTHubTransportMqtt_WS_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + IoTHubTransport_MQTT_Common_Unregister(deviceHandle); +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_010: [ IoTHubTransportMqtt_WS_GetHostname shall get the hostname by calling into the IoTHubMqttAbstract_GetHostname function. ] */ +static STRING_HANDLE IoTHubTransportMqtt_WS_GetHostname(TRANSPORT_LL_HANDLE handle) +{ + return IoTHubTransport_MQTT_Common_GetHostname(handle); +} + +static int IoTHubTransportMqtt_WS_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitinSeconds) +{ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_25_012: [** IoTHubTransportMqtt_WS_SetRetryPolicy shall call into the IoTHubMqttAbstract_SetRetryPolicy function.]*/ + return IoTHubTransport_MQTT_Common_SetRetryPolicy(handle, retryPolicy, retryTimeoutLimitinSeconds); +} + +static IOTHUB_CLIENT_RESULT IoTHubTransportMqtt_WS_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition) +{ + /* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_10_001: [IoTHubTransportMqtt_WS_SendMessageDisposition shall send the message disposition by calling into the IoTHubTransport_MQTT_Common_SendMessageDisposition()]*/ + return IoTHubTransport_MQTT_Common_SendMessageDisposition(message_data, disposition); +} + +static int IoTHubTransportMqtt_WS_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + (void)handle; + LogError("IoTHubTransportMqtt_WS_Subscribe_InputQueue not implemented\n"); + return __FAILURE__; +} + +static void IoTHubTransportMqtt_WS_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle) +{ + LogError("IoTHubTransportMqtt_WS_Unsubscribe_InputQueue not implemented\n"); + (void)handle; +} + +/* Codes_SRS_IOTHUB_MQTT_WEBSOCKET_TRANSPORT_07_011: [ This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for its fields: +IoTHubTransport_SendMessageDisposition = IoTHubTransport_WS_SendMessageDisposition +IoTHubTransport_Subscribe_DeviceMethod = IoTHubTransport_WS_Subscribe_DeviceMethod +IoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_WS_Unsubscribe_DeviceMethod +IoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_WS_Subscribe_DeviceTwin +IoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_WS_Unsubscribe_DeviceTwin +IoTHubTransport_GetHostname = IoTHubTransportMqtt_WS_GetHostname +IoTHubTransport_Create = IoTHubTransportMqtt_WS_Create +IoTHubTransport_Destroy = IoTHubTransportMqtt_WS_Destroy +IoTHubTransport_Subscribe = IoTHubTransportMqtt_WS_Subscribe +IoTHubTransport_Unsubscribe = IoTHubTransportMqtt_WS_Unsubscribe +IoTHubTransport_DoWork = IoTHubTransportMqtt_WS_DoWork +IoTHubTransport_SetOption = IoTHubTransportMqtt_WS_SetOption ] */ +static TRANSPORT_PROVIDER thisTransportProvider_WebSocketsOverTls = { + IoTHubTransportMqtt_WS_SendMessageDisposition, + IoTHubTransportMqtt_WS_Subscribe_DeviceMethod, + IoTHubTransportMqtt_WS_Unsubscribe_DeviceMethod, + IoTHubTransportMqtt_WS_DeviceMethod_Response, + IoTHubTransportMqtt_WS_Subscribe_DeviceTwin, + IoTHubTransportMqtt_WS_Unsubscribe_DeviceTwin, + IoTHubTransportMqtt_WS_ProcessItem, + IoTHubTransportMqtt_WS_GetHostname, + IoTHubTransportMqtt_WS_SetOption, + IoTHubTransportMqtt_WS_Create, + IoTHubTransportMqtt_WS_Destroy, + IoTHubTransportMqtt_WS_Register, + IoTHubTransportMqtt_WS_Unregister, + IoTHubTransportMqtt_WS_Subscribe, + IoTHubTransportMqtt_WS_Unsubscribe, + IoTHubTransportMqtt_WS_DoWork, + IoTHubTransportMqtt_WS_SetRetryPolicy, + IoTHubTransportMqtt_WS_GetSendStatus, + IoTHubTransportMqtt_WS_Subscribe_InputQueue, + IoTHubTransportMqtt_WS_Unsubscribe_InputQueue +}; + +const TRANSPORT_PROVIDER* MQTT_WebSocket_Protocol(void) +{ + return &thisTransportProvider_WebSocketsOverTls; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/message_queue.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,813 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" + +typedef struct MESSAGE_QUEUE_TAG MESSAGE_QUEUE; + +#include "internal/message_queue.h" + +#define RESULT_OK 0 +#define INDEFINITE_TIME ((time_t)(-1)) + +static const char* SAVED_OPTION_MAX_RETRY_COUNT = "SAVED_OPTION_MAX_RETRY_COUNT"; +static const char* SAVED_OPTION_MAX_ENQUEUE_TIME_SECS = "SAVED_OPTION_MAX_ENQUEUE_TIME_SECS"; +static const char* SAVED_OPTION_MAX_PROCESSING_TIME_SECS = "SAVED_OPTION_MAX_PROCESSING_TIME_SECS"; + + +struct MESSAGE_QUEUE_TAG +{ + size_t max_message_enqueued_time_secs; + size_t max_message_processing_time_secs; + size_t max_retry_count; + + PROCESS_MESSAGE_CALLBACK on_process_message_callback; + void* on_process_message_context; + + SINGLYLINKEDLIST_HANDLE pending; + SINGLYLINKEDLIST_HANDLE in_progress; +}; + +typedef struct MESSAGE_QUEUE_ITEM_TAG +{ + MQ_MESSAGE_HANDLE message; + MESSAGE_PROCESSING_COMPLETED_CALLBACK on_message_processing_completed_callback; + void* user_context; + time_t enqueue_time; + time_t processing_start_time; + size_t number_of_attempts; +} MESSAGE_QUEUE_ITEM; + + + +// ---------- Helper Functions ---------- // + +static bool find_item_by_message_ptr(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + MESSAGE_QUEUE_ITEM* current_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + MQ_MESSAGE_HANDLE* target_item = (MQ_MESSAGE_HANDLE*)match_context; + + return (current_item->message == target_item); +} + +static void fire_message_callback(MESSAGE_QUEUE_ITEM* mq_item, MESSAGE_QUEUE_RESULT result, void* reason) +{ + if (mq_item->on_message_processing_completed_callback != NULL) + { + if (result == MESSAGE_QUEUE_RETRYABLE_ERROR) + { + result = MESSAGE_QUEUE_ERROR; + } + + mq_item->on_message_processing_completed_callback(mq_item->message, result, reason, mq_item->user_context); + } +} + +static bool should_retry_sending(MESSAGE_QUEUE_HANDLE message_queue, MESSAGE_QUEUE_ITEM* mq_item, MESSAGE_QUEUE_RESULT result) +{ + return (result == MESSAGE_QUEUE_RETRYABLE_ERROR && mq_item->number_of_attempts <= message_queue->max_retry_count); +} + +static int retry_sending_message(MESSAGE_QUEUE_HANDLE message_queue, LIST_ITEM_HANDLE list_item) +{ + int result; + MESSAGE_QUEUE_ITEM* mq_item; + + mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + + if (singlylinkedlist_remove(message_queue->in_progress, list_item)) + { + LogError("Failed removing message from in-progress list"); + result = __FAILURE__; + } + else if (singlylinkedlist_add(message_queue->pending, (const void*)mq_item) == NULL) + { + LogError("Failed moving message back to pending list"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + return result; +} + +static void dequeue_message_and_fire_callback(SINGLYLINKEDLIST_HANDLE list, LIST_ITEM_HANDLE list_item, MESSAGE_QUEUE_RESULT result, void* reason) +{ + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + + // Codes_SRS_MESSAGE_QUEUE_09_045: [If `message` is present in `message_queue->in_progress`, it shall be removed] + if (singlylinkedlist_remove(list, list_item)) + { + LogError("failed removing message from list (%p)", list); + } + + // Codes_SRS_MESSAGE_QUEUE_09_049: [Otherwise `mq_item->on_message_processing_completed_callback` shall be invoked passing `mq_item->message`, `result`, `reason` and `mq_item->user_context`] + fire_message_callback(mq_item, result, reason); + + // Codes_SRS_MESSAGE_QUEUE_09_050: [The `mq_item` related to `message` shall be freed] + free(mq_item); +} + +static void on_process_message_completed_callback(MESSAGE_QUEUE_HANDLE message_queue, MQ_MESSAGE_HANDLE message, MESSAGE_QUEUE_RESULT result, USER_DEFINED_REASON reason) +{ + // Codes_SRS_MESSAGE_QUEUE_09_069: [If `message` or `message_queue` are NULL, on_message_processing_completed_callback shall return immediately] + if (message == NULL || message_queue == NULL) + { + LogError("on_process_message_completed_callback invoked with NULL arguments (message=%p, message_queue=%p)", message, message_queue); + } + else + { + LIST_ITEM_HANDLE list_item; + + if ((list_item = singlylinkedlist_find(message_queue->in_progress, find_item_by_message_ptr, message)) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_044: [If `message` is not present in `message_queue->in_progress`, it shall be ignored] + LogError("on_process_message_completed_callback invoked for a message not in the in-progress list (%p)", message); + } + else + { + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + + // Codes_SRS_MESSAGE_QUEUE_09_047: [If `result` is MESSAGE_QUEUE_RETRYABLE_ERROR and `mq_item->number_of_attempts` is less than or equal `message_queue->max_retry_count`, the `message` shall be moved to `message_queue->pending` to be re-sent] + // Codes_SRS_MESSAGE_QUEUE_09_048: [If `result` is MESSAGE_QUEUE_RETRYABLE_ERROR and `mq_item->number_of_attempts` is greater than `message_queue->max_retry_count`, result shall be changed to MESSAGE_QUEUE_ERROR] + if (!should_retry_sending(message_queue, mq_item, result) || retry_sending_message(message_queue, list_item) != RESULT_OK) + { + dequeue_message_and_fire_callback(message_queue->in_progress, list_item, result, reason); + } + } + } +} + +static void process_timeouts(MESSAGE_QUEUE_HANDLE message_queue) +{ + time_t current_time; + + if ((current_time = get_time(NULL)) == INDEFINITE_TIME) + { + LogError("failed processing timeouts (get_time failed)"); + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_035: [If `message_queue->max_message_enqueued_time_secs` is greater than zero, `message_queue->in_progress` and `message_queue->pending` items shall be checked for timeout] + if (message_queue->max_message_enqueued_time_secs > 0) + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(message_queue->pending); + + while (list_item != NULL) + { + LIST_ITEM_HANDLE current_list_item = list_item; + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(current_list_item); + + list_item = singlylinkedlist_get_next_item(list_item); + + if (mq_item == NULL) + { + LogError("failed processing timeouts (unexpected NULL pointer to MESSAGE_QUEUE_ITEM)"); + } + else if (get_difftime(current_time, mq_item->enqueue_time) >= message_queue->max_message_enqueued_time_secs) + { + // Codes_SRS_MESSAGE_QUEUE_09_036: [If any items are in `message_queue` lists for `message_queue->max_message_enqueued_time_secs` or more, they shall be removed and `message_queue->on_message_processing_completed_callback` invoked with MESSAGE_QUEUE_TIMEOUT] + dequeue_message_and_fire_callback(message_queue->pending, current_list_item, MESSAGE_QUEUE_TIMEOUT, NULL); + } + else + { + // The pending list order is already based on enqueue time, so if one message is not expired, later ones won't be either. + break; + } + } + + list_item = singlylinkedlist_get_head_item(message_queue->in_progress); + + while (list_item != NULL) + { + LIST_ITEM_HANDLE current_list_item = list_item; + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(current_list_item); + + list_item = singlylinkedlist_get_next_item(list_item); + + if (mq_item == NULL) + { + LogError("failed processing timeouts (unexpected NULL pointer to MESSAGE_QUEUE_ITEM)"); + } + else if (get_difftime(current_time, mq_item->enqueue_time) >= message_queue->max_message_enqueued_time_secs) + { + // Codes_SRS_MESSAGE_QUEUE_09_038: [If any items are in `message_queue->in_progress` for `message_queue->max_message_processing_time_secs` or more, they shall be removed and `message_queue->on_message_processing_completed_callback` invoked with MESSAGE_QUEUE_TIMEOUT] + dequeue_message_and_fire_callback(message_queue->in_progress, current_list_item, MESSAGE_QUEUE_TIMEOUT, NULL); + } + } + } + + // Codes_SRS_MESSAGE_QUEUE_09_037: [If `message_queue->max_message_processing_time_secs` is greater than zero, `message_queue->in_progress` items shall be checked for timeout] + if (message_queue->max_message_processing_time_secs > 0) + { + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(message_queue->in_progress); + + while (list_item != NULL) + { + LIST_ITEM_HANDLE current_list_item = list_item; + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(current_list_item); + + list_item = singlylinkedlist_get_next_item(list_item); + + if (mq_item == NULL) + { + LogError("failed processing timeouts (unexpected NULL pointer to MESSAGE_QUEUE_ITEM)"); + } + else if (get_difftime(current_time, mq_item->processing_start_time) >= message_queue->max_message_processing_time_secs) + { + dequeue_message_and_fire_callback(message_queue->in_progress, current_list_item, MESSAGE_QUEUE_TIMEOUT, NULL); + } + else + { + // The in-progress list order is already based on start-processing time, so if one message is not expired, later ones won't be either. + break; + } + } + } + } +} + +static void process_pending_messages(MESSAGE_QUEUE_HANDLE message_queue) +{ + LIST_ITEM_HANDLE list_item; + + while ((list_item = singlylinkedlist_get_head_item(message_queue->pending)) != NULL) + { + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + + if (mq_item == NULL) + { + LogError("internal error, failed to retrieve list node value"); + break; + } + else if (singlylinkedlist_remove(message_queue->pending, list_item) != 0) + { + LogError("failed moving message out of pending list (%p)", mq_item->message); + + // Codes_SRS_MESSAGE_QUEUE_09_042: [If any failures occur, `mq_item->on_message_processing_completed_callback` shall be invoked with MESSAGE_QUEUE_ERROR and `mq_item` freed] + if (mq_item->on_message_processing_completed_callback != NULL) + { + mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context); + } + + // Not freeing since this would cause a memory A/V on the next call. + + break; // Trying to avoid an infinite loop + } + // Codes_SRS_MESSAGE_QUEUE_09_040: [`mq_item->processing_start_time` shall be set using get_time()] + else if ((mq_item->processing_start_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_SRS_MESSAGE_QUEUE_09_041: [If get_time() fails, `mq_item` shall be removed from `message_queue->in_progress`] + LogError("failed setting message processing_start_time (%p)", mq_item->message); + + // Codes_SRS_MESSAGE_QUEUE_09_042: [If any failures occur, `mq_item->on_message_processing_completed_callback` shall be invoked with MESSAGE_QUEUE_ERROR and `mq_item` freed] + if (mq_item->on_message_processing_completed_callback != NULL) + { + mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context); + } + + free(mq_item); + } + // Codes_SRS_MESSAGE_QUEUE_09_039: [Each `mq_item` in `message_queue->pending` shall be moved to `message_queue->in_progress`] + else if (singlylinkedlist_add(message_queue->in_progress, (const void*)mq_item) == NULL) + { + LogError("failed moving message to in-progress list (%p)", mq_item->message); + + // Codes_SRS_MESSAGE_QUEUE_09_042: [If any failures occur, `mq_item->on_message_processing_completed_callback` shall be invoked with MESSAGE_QUEUE_ERROR and `mq_item` freed] + if (mq_item->on_message_processing_completed_callback != NULL) + { + mq_item->on_message_processing_completed_callback(mq_item->message, MESSAGE_QUEUE_ERROR, NULL, mq_item->user_context); + } + + free(mq_item); + } + else + { + mq_item->number_of_attempts++; + + // Codes_SRS_MESSAGE_QUEUE_09_043: [If no failures occur, `message_queue->on_process_message_callback` shall be invoked passing `mq_item->message` and `on_process_message_completed_callback`] + message_queue->on_process_message_callback(message_queue, mq_item->message, on_process_message_completed_callback, mq_item->user_context); + } + } +} + +static void* cloneOption(const char* name, const void* value) +{ + void* result; + + if (name == NULL || value == NULL) + { + LogError("invalid argument (name=%p, value=%p)", name, value); + result = NULL; + } + else if (strcmp(SAVED_OPTION_MAX_ENQUEUE_TIME_SECS, name) == 0 || + strcmp(SAVED_OPTION_MAX_PROCESSING_TIME_SECS, name) == 0 || + strcmp(SAVED_OPTION_MAX_RETRY_COUNT, name) == 0) + { + if ((result = malloc(sizeof(size_t))) == NULL) + { + LogError("failed cloning option %s (malloc failed)", name); + } + else + { + memcpy(result, value, sizeof(size_t)); + } + } + else + { + LogError("option %s is invalid", name); + result = NULL; + } + + return result; +} + +static void destroyOption(const char* name, const void* value) +{ + if (name == NULL || value == NULL) + { + LogError("invalid argument (name=%p, value=%p)", name, value); + } + else if (strcmp(SAVED_OPTION_MAX_ENQUEUE_TIME_SECS, name) == 0 || + strcmp(SAVED_OPTION_MAX_PROCESSING_TIME_SECS, name) == 0 || + strcmp(SAVED_OPTION_MAX_RETRY_COUNT, name) == 0) + { + free((void*)value); + } + else + { + LogError("option %s is invalid", name); + } +} + + + +// ---------- Public APIs ---------- // + +void message_queue_remove_all(MESSAGE_QUEUE_HANDLE message_queue) +{ + // Codes_SRS_MESSAGE_QUEUE_09_026: [If `message_queue` is NULL, message_queue_retrieve_options shall return] + if (message_queue != NULL) + { + LIST_ITEM_HANDLE list_item; + + // Codes_SRS_MESSAGE_QUEUE_09_027: [Each `mq_item` in `message_queue->pending` and `message_queue->in_progress` lists shall be removed] + while ((list_item = singlylinkedlist_get_head_item(message_queue->in_progress)) != NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_028: [`message_queue->on_message_processing_completed_callback` shall be invoked with MESSAGE_QUEUE_CANCELLED for each `mq_item` removed] + // Codes_SRS_MESSAGE_QUEUE_09_029: [Each `mq_item` shall be freed] + dequeue_message_and_fire_callback(message_queue->in_progress, list_item, MESSAGE_QUEUE_CANCELLED, NULL); + } + + while ((list_item = singlylinkedlist_get_head_item(message_queue->pending)) != NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_028: [`message_queue->on_message_processing_completed_callback` shall be invoked with MESSAGE_QUEUE_CANCELLED for each `mq_item` removed] + // Codes_SRS_MESSAGE_QUEUE_09_029: [Each `mq_item` shall be freed] + dequeue_message_and_fire_callback(message_queue->pending, list_item, MESSAGE_QUEUE_CANCELLED, NULL); + } + } +} + +static int move_messages_between_lists(SINGLYLINKEDLIST_HANDLE from_list, SINGLYLINKEDLIST_HANDLE to_list) +{ + int result; + LIST_ITEM_HANDLE list_item; + + result = RESULT_OK; + + while ((list_item = singlylinkedlist_get_head_item(from_list)) != NULL) + { + if (singlylinkedlist_remove(from_list, list_item) != 0) + { + LogError("failed removing message from list"); + result = __FAILURE__; + } + else + { + MESSAGE_QUEUE_ITEM* mq_item = (MESSAGE_QUEUE_ITEM*)singlylinkedlist_item_get_value(list_item); + + if (singlylinkedlist_add(to_list, (const void*)mq_item) != 0) + { + LogError("failed moving message to list"); + + fire_message_callback(mq_item, MESSAGE_QUEUE_CANCELLED, NULL); + + free(mq_item); + + result = __FAILURE__; + + break; + } + else + { + mq_item->number_of_attempts = 0; + mq_item->processing_start_time = INDEFINITE_TIME; + } + } + } + + return result; +} + +int message_queue_move_all_back_to_pending(MESSAGE_QUEUE_HANDLE message_queue) +{ + int result; + + if (message_queue == NULL) + { + LogError("invalid argument (message_queue is NULL)"); + result = __FAILURE__; + } + else + { + SINGLYLINKEDLIST_HANDLE temp_list; + + if ((temp_list = singlylinkedlist_create()) == NULL) + { + LogError("failed creating temporary list"); + result = __FAILURE__; + } + else + { + if (move_messages_between_lists(message_queue->in_progress, temp_list) != 0) + { + LogError("failed moving in-progress message to temporary list"); + result = __FAILURE__; + } + else if (move_messages_between_lists(message_queue->pending, temp_list) != 0) + { + LogError("failed moving pending message to temporary list"); + result = __FAILURE__; + } + else if (move_messages_between_lists(temp_list, message_queue->pending) != 0) + { + LogError("failed moving pending message to temporary list"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (result != RESULT_OK) + { + LIST_ITEM_HANDLE list_item; + + while ((list_item = singlylinkedlist_get_head_item(temp_list)) != NULL) + { + dequeue_message_and_fire_callback(temp_list, list_item, MESSAGE_QUEUE_CANCELLED, NULL); + } + } + + singlylinkedlist_destroy(temp_list); + } + } + + return result; +} + +void message_queue_destroy(MESSAGE_QUEUE_HANDLE message_queue) +{ + // Codes_SRS_MESSAGE_QUEUE_09_013: [If `message_queue` is NULL, message_queue_destroy shall return immediately] + if (message_queue != NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_014: [message_queue_destroy shall invoke message_queue_remove_all] + message_queue_remove_all(message_queue); + + // Codes_SRS_MESSAGE_QUEUE_09_015: [message_queue_destroy shall free all memory allocated and pointed by `message_queue`] + if (message_queue->pending != NULL) + { + singlylinkedlist_destroy(message_queue->pending); + } + + if (message_queue->in_progress != NULL) + { + singlylinkedlist_destroy(message_queue->in_progress); + } + + free(message_queue); + } +} + +MESSAGE_QUEUE_HANDLE message_queue_create(MESSAGE_QUEUE_CONFIG* config) +{ + MESSAGE_QUEUE* result; + + // Codes_SRS_MESSAGE_QUEUE_09_001: [If `config` is NULL, message_queue_create shall fail and return NULL] + if (config == NULL) + { + LogError("invalid configuration (NULL)"); + result = NULL; + } + // Codes_SRS_MESSAGE_QUEUE_09_002: [If `config->on_process_message_callback` is NULL, message_queue_create shall fail and return NULL] + else if (config->on_process_message_callback == NULL) + { + LogError("invalid configuration (on_process_message_callback is NULL)"); + result = NULL; + } + // Codes_SRS_MESSAGE_QUEUE_09_004: [Memory shall be allocated for the MESSAGE_QUEUE data structure (aka `message_queue`)] + else if ((result = (MESSAGE_QUEUE*)malloc(sizeof(MESSAGE_QUEUE))) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_005: [If `instance` cannot be allocated, message_queue_create shall fail and return NULL] + LogError("failed allocating MESSAGE_QUEUE"); + result = NULL; + } + else + { + memset(result, 0, sizeof(MESSAGE_QUEUE)); + + // Codes_SRS_MESSAGE_QUEUE_09_006: [`message_queue->pending` shall be set using singlylinkedlist_create()] + if ((result->pending = singlylinkedlist_create()) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_007: [If singlylinkedlist_create fails, message_queue_create shall fail and return NULL] + LogError("failed allocating MESSAGE_QUEUE pending list"); + // Codes_SRS_MESSAGE_QUEUE_09_011: [If any failures occur, message_queue_create shall release all memory it has allocated] + message_queue_destroy(result); + result = NULL; + } + // Codes_SRS_MESSAGE_QUEUE_09_008: [`message_queue->in_progress` shall be set using singlylinkedlist_create()] + else if ((result->in_progress = singlylinkedlist_create()) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_009: [If singlylinkedlist_create fails, message_queue_create shall fail and return NULL] + LogError("failed allocating MESSAGE_QUEUE in-progress list"); + // Codes_SRS_MESSAGE_QUEUE_09_011: [If any failures occur, message_queue_create shall release all memory it has allocated] + message_queue_destroy(result); + result = NULL; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_010: [All arguments in `config` shall be saved into `message_queue`] + // Codes_SRS_MESSAGE_QUEUE_09_012: [If no failures occur, message_queue_create shall return the `message_queue` pointer] + + result->max_message_enqueued_time_secs = config->max_message_enqueued_time_secs; + result->max_message_processing_time_secs = config->max_message_processing_time_secs; + result->max_retry_count = config->max_retry_count; + result->on_process_message_callback = config->on_process_message_callback; + } + } + + return result; +} + +int message_queue_add(MESSAGE_QUEUE_HANDLE message_queue, MQ_MESSAGE_HANDLE message, MESSAGE_PROCESSING_COMPLETED_CALLBACK on_message_processing_completed_callback, void* user_context) +{ + int result; + + // Codes_SRS_MESSAGE_QUEUE_09_016: [If `message_queue` or `message` are NULL, message_queue_add shall fail and return non-zero] + if (message_queue == NULL || message == NULL) + { + LogError("invalid argument (message_queue=%p, message=%p)", message_queue, message); + result = __FAILURE__; + } + else + { + MESSAGE_QUEUE_ITEM* mq_item; + + // Codes_SRS_MESSAGE_QUEUE_09_017: [message_queue_add shall allocate a structure (aka `mq_item`) to save the `message`] + if ((mq_item = (MESSAGE_QUEUE_ITEM*)malloc(sizeof(MESSAGE_QUEUE_ITEM))) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_018: [If `mq_item` cannot be allocated, message_queue_add shall fail and return non-zero] + LogError("failed creating container for message"); + result = __FAILURE__; + } + else + { + memset(mq_item, 0, sizeof(MESSAGE_QUEUE_ITEM)); + + // Codes_SRS_MESSAGE_QUEUE_09_019: [`mq_item->enqueue_time` shall be set using get_time()] + if ((mq_item->enqueue_time = get_time(NULL)) == INDEFINITE_TIME) + { + // Codes_SRS_MESSAGE_QUEUE_09_020: [If get_time fails, message_queue_add shall fail and return non-zero] + LogError("failed setting message enqueue time"); + // Codes_SRS_MESSAGE_QUEUE_09_024: [If any failures occur, message_queue_add shall release all memory it has allocated] + free(mq_item); + result = __FAILURE__; + } + // Codes_SRS_MESSAGE_QUEUE_09_021: [`mq_item` shall be added to `message_queue->pending` list] + else if (singlylinkedlist_add(message_queue->pending, (const void*)mq_item) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_022: [`mq_item` fails to be added to `message_queue->pending`, message_queue_add shall fail and return non-zero] + LogError("failed enqueing message"); + // Codes_SRS_MESSAGE_QUEUE_09_024: [If any failures occur, message_queue_add shall release all memory it has allocated] + free(mq_item); + result = __FAILURE__; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_023: [`message` shall be saved into `mq_item->message`] + mq_item->message = message; + mq_item->on_message_processing_completed_callback = on_message_processing_completed_callback; + mq_item->user_context = user_context; + mq_item->processing_start_time = INDEFINITE_TIME; + // Codes_SRS_MESSAGE_QUEUE_09_025: [If no failures occur, message_queue_add shall return 0] + result = RESULT_OK; + } + } + } + + return result; +} + +int message_queue_is_empty(MESSAGE_QUEUE_HANDLE message_queue, bool* is_empty) +{ + int result; + + // Codes_SRS_MESSAGE_QUEUE_09_030: [If `message_queue` or `is_empty` are NULL, message_queue_is_empty shall fail and return non-zero] + if (message_queue == NULL || is_empty == NULL) + { + LogError("invalid argument (message_queue=%p, is_empty=%p)", message_queue, is_empty); + result = __FAILURE__; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_031: [If `message_queue->pending` and `message_queue->in_progress` are empty, `is_empty` shall be set to true] + // Codes_SRS_MESSAGE_QUEUE_09_032: [Otherwise `is_empty` shall be set to false] + *is_empty = (singlylinkedlist_get_head_item(message_queue->pending) == NULL && singlylinkedlist_get_head_item(message_queue->in_progress) == NULL); + // Codes_SRS_MESSAGE_QUEUE_09_033: [If no failures occur, message_queue_is_empty shall return 0] + result = RESULT_OK; + } + + return result; +} + +void message_queue_do_work(MESSAGE_QUEUE_HANDLE message_queue) +{ + // Codes_SRS_MESSAGE_QUEUE_09_034: [If `message_queue` is NULL, message_queue_do_work shall return immediately] + if (message_queue != NULL) + { + process_timeouts(message_queue); + process_pending_messages(message_queue); + } +} + +int message_queue_set_max_message_enqueued_time_secs(MESSAGE_QUEUE_HANDLE message_queue, size_t seconds) +{ + int result; + + // Codes_SRS_MESSAGE_QUEUE_09_051: [If `message_queue` is NULL, message_queue_set_max_message_enqueued_time_secs shall fail and return non-zero] + if (message_queue == NULL) + { + LogError("invalid argument (message_queue is NULL)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_053: [`seconds` shall be saved into `message_queue->max_message_enqueued_time_secs`] + message_queue->max_message_enqueued_time_secs = seconds; + // Codes_SRS_MESSAGE_QUEUE_09_054: [If no failures occur, message_queue_set_max_message_enqueued_time_secs shall return 0] + result = RESULT_OK; + } + + return result; +} + +int message_queue_set_max_message_processing_time_secs(MESSAGE_QUEUE_HANDLE message_queue, size_t seconds) +{ + int result; + + // Codes_SRS_MESSAGE_QUEUE_09_055: [If `message_queue` is NULL, message_queue_set_max_message_processing_time_secs shall fail and return non-zero] + if (message_queue == NULL) + { + LogError("invalid argument (message_queue is NULL)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_057: [`seconds` shall be saved into `message_queue->max_message_processing_time_secs`] + message_queue->max_message_processing_time_secs = seconds; + // Codes_SRS_MESSAGE_QUEUE_09_058: [If no failures occur, message_queue_set_max_message_processing_time_secs shall return 0] + result = RESULT_OK; + } + + return result; +} + +int message_queue_set_max_retry_count(MESSAGE_QUEUE_HANDLE message_queue, size_t max_retry_count) +{ + int result; + + // Codes_SRS_MESSAGE_QUEUE_09_059: [If `message_queue` is NULL, message_queue_set_max_retry_count shall fail and return non-zero] + if (message_queue == NULL) + { + LogError("invalid argument (message_queue is NULL)"); + result = __FAILURE__; + } + else + { + // Codes_SRS_MESSAGE_QUEUE_09_061: [If no failures occur, message_queue_set_max_retry_count shall return 0] + message_queue->max_retry_count = max_retry_count; + // Codes_SRS_MESSAGE_QUEUE_09_060: [`max_retry_count` shall be saved into `message_queue->max_retry_count`] + result = RESULT_OK; + } + + return result; +} + +static int setOption(void* handle, const char* name, const void* value) +{ + int result; + + if (handle == NULL || name == NULL || value == NULL) + { + LogError("invalid argument (handle=%p, name=%p, value=%p)", handle, name, value); + result = __FAILURE__; + } + else if (strcmp(SAVED_OPTION_MAX_ENQUEUE_TIME_SECS, name) == 0) + { + if (message_queue_set_max_message_enqueued_time_secs((MESSAGE_QUEUE_HANDLE)handle, *(size_t*)value) != RESULT_OK) + { + LogError("failed setting option %s", name); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(SAVED_OPTION_MAX_PROCESSING_TIME_SECS, name) == 0) + { + if (message_queue_set_max_message_processing_time_secs((MESSAGE_QUEUE_HANDLE)handle, *(size_t*)value) != RESULT_OK) + { + LogError("failed setting option %s", name); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else if (strcmp(SAVED_OPTION_MAX_RETRY_COUNT, name) == 0) + { + if (message_queue_set_max_retry_count((MESSAGE_QUEUE_HANDLE)handle, *(size_t*)value) != RESULT_OK) + { + LogError("failed setting option %s", name); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + LogError("option %s is invalid", name); + result = __FAILURE__; + } + + return result; +} + +OPTIONHANDLER_HANDLE message_queue_retrieve_options(MESSAGE_QUEUE_HANDLE message_queue) +{ + OPTIONHANDLER_HANDLE result; + + // Codes_SRS_MESSAGE_QUEUE_09_062: [If `message_queue` is NULL, message_queue_retrieve_options shall fail and return NULL] + if (message_queue == NULL) + { + LogError("invalid argument (message_queue is NULL)"); + result = NULL; + } + // Codes_SRS_MESSAGE_QUEUE_09_063: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create] + else if ((result = OptionHandler_Create(cloneOption, destroyOption, setOption)) == NULL) + { + // Codes_SRS_MESSAGE_QUEUE_09_064: [If an OPTIONHANDLER_HANDLE instance fails to be created, message_queue_retrieve_options shall fail and return NULL] + LogError("failed creating OPTIONHANDLER_HANDLE"); + } + // Codes_SRS_MESSAGE_QUEUE_09_065: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption] + else if (OptionHandler_AddOption(result, SAVED_OPTION_MAX_ENQUEUE_TIME_SECS, &message_queue->max_message_enqueued_time_secs) != OPTIONHANDLER_OK) + { + LogError("failed retrieving options (failed adding %s)", SAVED_OPTION_MAX_ENQUEUE_TIME_SECS); + // Codes_SRS_MESSAGE_QUEUE_09_067: [If message_queue_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(result); + // Codes_SRS_MESSAGE_QUEUE_09_066: [If OptionHandler_AddOption fails, message_queue_retrieve_options shall fail and return NULL] + result = NULL; + } + else if (OptionHandler_AddOption(result, SAVED_OPTION_MAX_PROCESSING_TIME_SECS, &message_queue->max_message_processing_time_secs) != OPTIONHANDLER_OK) + { + LogError("failed retrieving options (failed adding %s)", SAVED_OPTION_MAX_PROCESSING_TIME_SECS); + // Codes_SRS_MESSAGE_QUEUE_09_067: [If message_queue_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(result); + // Codes_SRS_MESSAGE_QUEUE_09_066: [If OptionHandler_AddOption fails, message_queue_retrieve_options shall fail and return NULL] + result = NULL; + } + else if (OptionHandler_AddOption(result, SAVED_OPTION_MAX_RETRY_COUNT, &message_queue->max_retry_count) != OPTIONHANDLER_OK) + { + LogError("failed retrieving options (failed adding %s)", SAVED_OPTION_MAX_PROCESSING_TIME_SECS); + // Codes_SRS_MESSAGE_QUEUE_09_067: [If message_queue_retrieve_options fails, any allocated memory shall be freed] + OptionHandler_Destroy(result); + // Codes_SRS_MESSAGE_QUEUE_09_066: [If OptionHandler_AddOption fails, message_queue_retrieve_options shall fail and return NULL] + result = NULL; + } + + // Codes_SRS_MESSAGE_QUEUE_09_068: [If no failures occur, message_queue_retrieve_options shall return the OPTIONHANDLER_HANDLE instance] + return result; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/uamqp_messaging.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1140 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#ifdef __cplusplus +#include <cstdlib> +#include <cstdint> +#include <cinttypes> +#else +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#endif + +#include "internal/uamqp_messaging.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/uuid.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "iothub_message.h" +#ifndef RESULT_OK +#define RESULT_OK 0 +#endif + +#define MESSAGE_ID_MAX_SIZE 128 + +#define AMQP_DIAGNOSTIC_ID_KEY "Diagnostic-Id" +#define AMQP_DIAGNOSTIC_CONTEXT_KEY "Correlation-Context" +#define AMQP_DIAGNOSTIC_CREATION_TIME_UTC_KEY "creationtimeutc" + +static int encode_callback(void* context, const unsigned char* bytes, size_t length) +{ + BINARY_DATA* message_body_binary = (BINARY_DATA*)context; + (void)memcpy((unsigned char*)message_body_binary->bytes + message_body_binary->length, bytes, length); + message_body_binary->length += length; + return 0; +} + +// Codes_SRS_UAMQP_MESSAGING_31_112: [If optional message-id is present in the message, encode it into the AMQP message.] +static int set_message_id_if_needed(IOTHUB_MESSAGE_HANDLE messageHandle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* messageId; + + if (NULL != (messageId = IoTHubMessage_GetMessageId(messageHandle))) + { + AMQP_VALUE uamqp_message_id; + + if ((uamqp_message_id = amqpvalue_create_string(messageId)) == NULL) + { + LogError("Failed amqpvalue_create_string for message_id"); + result = __FAILURE__; + } + else if (properties_set_message_id(uamqp_message_properties, uamqp_message_id) != 0) + { + LogError("Failed properties_set_message_id"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (NULL != uamqp_message_id) + { + amqpvalue_destroy(uamqp_message_id); + } + } + else + { + result = RESULT_OK; + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_113: [If optional correlation-id is present in the message, encode it into the AMQP message.] +static int set_message_correlation_id_if_needed(IOTHUB_MESSAGE_HANDLE messageHandle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* correlationId; + + if (NULL != (correlationId = IoTHubMessage_GetCorrelationId(messageHandle))) + { + AMQP_VALUE uamqp_correlation_id; + + if ((uamqp_correlation_id = amqpvalue_create_string(correlationId)) == NULL) + { + LogError("Failed amqpvalue_create_string for message_id"); + result = __FAILURE__; + } + else if (properties_set_correlation_id(uamqp_message_properties, uamqp_correlation_id) != 0) + { + LogError("Failed properties_set_correlation_id"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (uamqp_correlation_id != NULL) + { + amqpvalue_destroy(uamqp_correlation_id); + } + } + else + { + result = RESULT_OK; + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_114: [If optional content-type is present in the message, encode it into the AMQP message.] +static int set_message_content_type_if_needed(IOTHUB_MESSAGE_HANDLE messageHandle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* content_type; + + if ((content_type = IoTHubMessage_GetContentTypeSystemProperty(messageHandle)) != NULL) + { + if (properties_set_content_type(uamqp_message_properties, content_type) != 0) + { + LogError("Failed properties_set_content_type"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + result = RESULT_OK; + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_115: [If optional content-encoding is present in the message, encode it into the AMQP message.] +static int set_message_content_encoding_if_needed(IOTHUB_MESSAGE_HANDLE messageHandle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* content_encoding; + + if ((content_encoding = IoTHubMessage_GetContentEncodingSystemProperty(messageHandle)) != NULL) + { + if (properties_set_content_encoding(uamqp_message_properties, content_encoding) != 0) + { + LogError("Failed properties_set_content_encoding"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + else + { + result = RESULT_OK; + } + + return result; +} + + +// Codes_SRS_UAMQP_MESSAGING_31_116: [Gets message properties associated with the IOTHUB_MESSAGE_HANDLE to encode, returning the properties and their encoded length.] +static int create_message_properties_to_encode(IOTHUB_MESSAGE_HANDLE messageHandle, AMQP_VALUE *message_properties, size_t *message_properties_length) +{ + PROPERTIES_HANDLE uamqp_message_properties = NULL; + int result; + + if ((uamqp_message_properties = properties_create()) == NULL) + { + LogError("Failed on properties_create()"); + result = __FAILURE__; + } + else if (set_message_id_if_needed(messageHandle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed on set_message_id_if_needed()"); + result = __FAILURE__; + } + else if (set_message_correlation_id_if_needed(messageHandle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed on set_message_correlation_id_if_needed()"); + result = __FAILURE__; + } + else if (set_message_content_type_if_needed(messageHandle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed on set_message_content_type_if_needed()"); + result = __FAILURE__; + } + else if (set_message_content_encoding_if_needed(messageHandle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed on set_message_content_encoding_if_needed()"); + result = __FAILURE__; + } + else if ((*message_properties = amqpvalue_create_properties(uamqp_message_properties)) == NULL) + { + LogError("Failed on amqpvalue_create_properties()"); + result = __FAILURE__; + } + else if ((amqpvalue_get_encoded_size(*message_properties, message_properties_length)) != 0) + { + LogError("Failed on amqpvalue_get_encoded_size()"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (NULL != uamqp_message_properties) + { + properties_destroy(uamqp_message_properties); + } + + return result; +} + +// Adds fault injection properties to an AMQP message. +static int add_fault_injection_properties(MESSAGE_HANDLE message_batch_container, const char* const* property_keys, const char* const* property_values, size_t property_count) +{ + int result; + AMQP_VALUE uamqp_map; + + if ((uamqp_map = amqpvalue_create_map()) == NULL) + { + LogError("Failed to create uAMQP map for the properties."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + + for (size_t i = 0; result == RESULT_OK && i < property_count; i++) + { + AMQP_VALUE map_key_value = NULL; + AMQP_VALUE map_value_value = NULL; + + if ((map_key_value = amqpvalue_create_string(property_keys[i])) == NULL) + { + LogError("Failed to create uAMQP property key name."); + result = __FAILURE__; + } + else if ((map_value_value = amqpvalue_create_string(property_values[i])) == NULL) + { + LogError("Failed to create uAMQP property key value."); + result = __FAILURE__; + } + else if (amqpvalue_set_map_value(uamqp_map, map_key_value, map_value_value) != 0) + { + LogError("Failed to set key/value into the the uAMQP property map."); + result = __FAILURE__; + } + + if (map_key_value != NULL) + amqpvalue_destroy(map_key_value); + + if (map_value_value != NULL) + amqpvalue_destroy(map_value_value); + } + + if (result == RESULT_OK) + { + if (message_set_application_properties(message_batch_container, uamqp_map) != 0) + { + LogError("Failed to transfer the message properties to the uAMQP message."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + amqpvalue_destroy(uamqp_map); + } + + return result; +} + +// To test AMQP fault injection, we currently must have the error properties be specified on the batch_container +// (not one of the messages sent in this container). As the SDK layer does not support options for configuring +// this envelope (this is AMQP/batching specific), we will instead intercept fault messages and apply to the container. +static int override_fault_injection_properties_if_needed(MESSAGE_HANDLE message_batch_container, const char* const* property_keys, const char* const* property_values, size_t property_count, bool *override_for_fault_injection) +{ + int result; + + if ((property_count == 0) || (strcmp(property_keys[0], "AzIoTHub_FaultOperationType") != 0)) + { + *override_for_fault_injection = false; + result = RESULT_OK; + } + else + { + *override_for_fault_injection = true; + result = add_fault_injection_properties(message_batch_container, property_keys, property_values, property_count); + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_117: [Get application message properties associated with the IOTHUB_MESSAGE_HANDLE to encode, returning the properties and their encoded length.] +static int create_application_properties_to_encode(MESSAGE_HANDLE message_batch_container, IOTHUB_MESSAGE_HANDLE messageHandle, AMQP_VALUE *application_properties, size_t *application_properties_length) +{ + MAP_HANDLE properties_map; + const char* const* property_keys; + const char* const* property_values; + size_t property_count = 0; + AMQP_VALUE uamqp_properties_map = NULL; + int result; + + if ((properties_map = IoTHubMessage_Properties(messageHandle)) == NULL) + { + LogError("Failed to get property map from IoTHub message."); + result = __FAILURE__; + } + else if (Map_GetInternals(properties_map, &property_keys, &property_values, &property_count) != 0) + { + LogError("Failed reading the incoming uAMQP message properties"); + result = __FAILURE__; + } + else if (property_count > 0) + { + size_t i; + if ((uamqp_properties_map = amqpvalue_create_map()) == NULL) + { + LogError("amqpvalue_create_map failed"); + result = __FAILURE__; + } + else + { + bool override_for_fault_injection = false; + result = override_fault_injection_properties_if_needed(message_batch_container, property_keys, property_values, property_count, &override_for_fault_injection); + + if (override_for_fault_injection == false) + { + for (i = 0; i < property_count; i++) + { + AMQP_VALUE map_property_key; + AMQP_VALUE map_property_value; + + if ((map_property_key = amqpvalue_create_string(property_keys[i])) == NULL) + { + LogError("Failed amqpvalue_create_string for key"); + result = __FAILURE__; + break; + } + + if ((map_property_value = amqpvalue_create_string(property_values[i])) == NULL) + { + LogError("Failed amqpvalue_create_string for value"); + amqpvalue_destroy(map_property_key); + result = __FAILURE__; + break; + } + + if (amqpvalue_set_map_value(uamqp_properties_map, map_property_key, map_property_value) != 0) + { + LogError("Failed amqpvalue_set_map_value"); + amqpvalue_destroy(map_property_key); + amqpvalue_destroy(map_property_value); + result = __FAILURE__; + break; + } + + amqpvalue_destroy(map_property_key); + amqpvalue_destroy(map_property_value); + } + + if (RESULT_OK == result) + { + if ((*application_properties = amqpvalue_create_application_properties(uamqp_properties_map)) == NULL) + { + LogError("Failed amqpvalue_create_application_properties"); + result = __FAILURE__; + } + else if (amqpvalue_get_encoded_size(*application_properties, application_properties_length) != 0) + { + LogError("Failed amqpvalue_get_encoded_size"); + result = __FAILURE__; + } + } + } + } + } + else + { + result = RESULT_OK; + } + + if (NULL != uamqp_properties_map) + { + amqpvalue_destroy(uamqp_properties_map); + } + + return result; +} + +static int add_map_item(AMQP_VALUE map, const char* name, const char* value) +{ + int result; + AMQP_VALUE amqp_value_name; + + if ((amqp_value_name = amqpvalue_create_symbol(name)) == NULL) + { + LogError("Failed creating AMQP_VALUE for name"); + result = __FAILURE__; + } + else + { + AMQP_VALUE amqp_value_value = NULL; + + if (value == NULL && (amqp_value_value = amqpvalue_create_null()) == NULL) + { + LogError("Failed creating AMQP_VALUE for NULL value"); + result = __FAILURE__; + } + else if (value != NULL && (amqp_value_value = amqpvalue_create_string(value)) == NULL) + { + LogError("Failed creating AMQP_VALUE for value"); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_map_value(map, amqp_value_name, amqp_value_value) != 0) + { + LogError("Failed adding key/value pair to map"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + amqpvalue_destroy(amqp_value_value); + } + + amqpvalue_destroy(amqp_value_name); + } + + return result; +} + +static int create_message_annotations_to_encode(IOTHUB_MESSAGE_HANDLE messageHandle, AMQP_VALUE *message_annotations, size_t *message_annotations_length) +{ + AMQP_VALUE message_annotations_map = NULL; + int result; + const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData; + + if ((diagnosticData = IoTHubMessage_GetDiagnosticPropertyData(messageHandle)) != NULL && + diagnosticData->diagnosticId != NULL && diagnosticData->diagnosticCreationTimeUtc != NULL) + { + // Codes_SRS_UAMQP_MESSAGING_32_001: [If optional diagnostic properties are present in the iot hub message, encode them into the AMQP message as annotation properties. Errors stop processing on this message.] + if ((message_annotations_map = amqpvalue_create_map()) == NULL) + { + LogError("Failed amqpvalue_create_map for annotations"); + result = __FAILURE__; + } + else + { + char* diagContextBuffer = NULL; + + if (add_map_item(message_annotations_map, AMQP_DIAGNOSTIC_ID_KEY, diagnosticData->diagnosticId) != RESULT_OK) + { + LogError("Failed adding diagnostic id"); + result = __FAILURE__; + } + else if ((diagContextBuffer = (char*)malloc(strlen(AMQP_DIAGNOSTIC_CREATION_TIME_UTC_KEY) + 1 + + strlen(diagnosticData->diagnosticCreationTimeUtc) + 1)) == NULL) + { + LogError("Failed malloc for diagnostic context"); + result = __FAILURE__; + } + else if (sprintf(diagContextBuffer, "%s=%s", AMQP_DIAGNOSTIC_CREATION_TIME_UTC_KEY, diagnosticData->diagnosticCreationTimeUtc) < 0) + { + LogError("Failed sprintf diagnostic context"); + result = __FAILURE__; + } + else if (add_map_item(message_annotations_map, AMQP_DIAGNOSTIC_CONTEXT_KEY, diagContextBuffer) != RESULT_OK) + { + LogError("Failed adding diagnostic context"); + result = __FAILURE__; + } + else if((*message_annotations = amqpvalue_create_message_annotations(message_annotations_map)) == NULL) + { + LogError("Failed creating message annotations"); + result = __FAILURE__; + } + else if (amqpvalue_get_encoded_size(*message_annotations, message_annotations_length) != 0) + { + LogError("Failed getting size of annotations"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + free(diagContextBuffer); + amqpvalue_destroy(message_annotations_map); + } + } + else + { + // Codes_SRS_UAMQP_MESSAGING_32_002: [If optional diagnostic properties are not present in the iot hub message, no error should happen.] + result = RESULT_OK; + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_118: [Gets data associated with IOTHUB_MESSAGE_HANDLE to encode, either from underlying byte array or string format.] +static int create_data_to_encode(IOTHUB_MESSAGE_HANDLE messageHandle, AMQP_VALUE *data_value, size_t *data_length) +{ + int result; + + IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(messageHandle); + const char* messageContent = NULL; + size_t messageContentSize = 0; + + if ((contentType == IOTHUBMESSAGE_BYTEARRAY) && + IoTHubMessage_GetByteArray(messageHandle, (const unsigned char **)&messageContent, &messageContentSize) != IOTHUB_MESSAGE_OK) + { + LogError("Failed getting the BYTE array representation of the IOTHUB_MESSAGE_HANDLE instance."); + result = __FAILURE__; + } + else if ((contentType == IOTHUBMESSAGE_STRING) && + ((messageContent = IoTHubMessage_GetString(messageHandle)) == NULL)) + { + LogError("Failed getting the STRING representation of the IOTHUB_MESSAGE_HANDLE instance."); + result = __FAILURE__; + } + else if (contentType == IOTHUBMESSAGE_UNKNOWN) + { + LogError("Cannot parse IOTHUB_MESSAGE_HANDLE with content type IOTHUBMESSAGE_UNKNOWN."); + result = __FAILURE__; + } + else + { + if (contentType == IOTHUBMESSAGE_STRING) + { + messageContentSize = strlen(messageContent); + } + + data bin_data; + bin_data.bytes = (const unsigned char *)messageContent; + bin_data.length = (uint32_t)messageContentSize; + + if ((*data_value = amqpvalue_create_data(bin_data)) == NULL) + { + LogError("amqpvalue_create_data failed"); + result = __FAILURE__; + } + else if (amqpvalue_get_encoded_size(*data_value, data_length) != 0) + { + LogError("amqpvalue_get_encoded_size failed"); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + } + + return result; +} + +// Codes_SRS_UAMQP_MESSAGING_31_120: [Create a blob that contains AMQP encoding of IOTHUB_MESSAGE_HANDLE.] +// Codes_SRS_UAMQP_MESSAGING_31_121: [Any errors during `message_create_uamqp_encoding_from_iothub_message` stop processing on this message.] +int message_create_uamqp_encoding_from_iothub_message(MESSAGE_HANDLE message_batch_container, IOTHUB_MESSAGE_HANDLE message_handle, BINARY_DATA* body_binary_data) +{ + int result; + + AMQP_VALUE message_properties = NULL; + AMQP_VALUE application_properties = NULL; + AMQP_VALUE message_annotations = NULL; + AMQP_VALUE data_value = NULL; + size_t message_properties_length = 0; + size_t application_properties_length = 0; + size_t message_annotations_length = 0; + size_t data_length = 0; + + body_binary_data->bytes = NULL; + body_binary_data->length = 0; + + if (create_message_properties_to_encode(message_handle, &message_properties, &message_properties_length) != RESULT_OK) + { + LogError("create_message_properties_to_encode() failed"); + result = __FAILURE__; + } + else if (create_application_properties_to_encode(message_batch_container, message_handle, &application_properties, &application_properties_length) != RESULT_OK) + { + LogError("create_application_properties_to_encode() failed"); + result = __FAILURE__; + } + else if (create_message_annotations_to_encode(message_handle, &message_annotations, &message_annotations_length) != RESULT_OK) + { + LogError("create_message_annotations_to_encode() failed"); + result = __FAILURE__; + } + else if (create_data_to_encode(message_handle, &data_value, &data_length) != RESULT_OK) + { + LogError("create_data_to_encode() failed"); + result = __FAILURE__; + } + else if ((body_binary_data->bytes = malloc(message_properties_length + application_properties_length + data_length + message_annotations_length)) == NULL) + { + LogError("malloc of %d bytes failed", message_properties_length + application_properties_length + data_length + message_annotations_length); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_31_119: [Invoke underlying AMQP encode routines on data waiting to be encoded.] + else if (amqpvalue_encode(message_properties, &encode_callback, body_binary_data) != RESULT_OK) + { + LogError("amqpvalue_encode() for message properties failed"); + result = __FAILURE__; + } + else if ((application_properties_length > 0) && (amqpvalue_encode(application_properties, &encode_callback, body_binary_data) != RESULT_OK)) + { + LogError("amqpvalue_encode() for application properties failed"); + result = __FAILURE__; + } + else if (message_annotations_length > 0 && amqpvalue_encode(message_annotations, &encode_callback, body_binary_data) != RESULT_OK) + { + LogError("amqpvalue_encode() for message annotations failed"); + result = __FAILURE__; + } + else if (RESULT_OK != amqpvalue_encode(data_value, &encode_callback, body_binary_data)) + { + LogError("amqpvalue_encode() for data value failed"); + result = __FAILURE__; + } + else + { + body_binary_data->length = message_properties_length + application_properties_length + data_length + message_annotations_length; + result = RESULT_OK; + } + + if (NULL != data_value) + { + amqpvalue_destroy(data_value); + } + + if (NULL != application_properties) + { + amqpvalue_destroy(application_properties); + } + + if (NULL != message_annotations) + { + amqpvalue_destroy(message_annotations); + } + + if (NULL != message_properties) + { + amqpvalue_destroy(message_properties); + } + + return result; +} + +static int readMessageIdFromuAQMPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + AMQP_VALUE uamqp_message_property; + + // Codes_SRS_UAMQP_MESSAGING_09_010: [The message-id property shall be read from the uAMQP message by calling properties_get_message_id.] + if (properties_get_message_id(uamqp_message_properties, &uamqp_message_property) != 0 || uamqp_message_property == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_011: [If the uAMQP message does not contain property `message ID`, it shall be skipped as it is optional.] + result = RESULT_OK; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_012: [The type of the message-id property value shall be obtained using amqpvalue_get_type().] + AMQP_TYPE value_type = amqpvalue_get_type(uamqp_message_property); + + // Codes_SRS_UAMQP_MESSAGING_09_013: [If the type of the message-id property value is AMQP_TYPE_NULL, message_create_IoTHubMessage_from_uamqp_message() shall skip processing the message-id (as it is optional) and continue normally.] + if (value_type != AMQP_TYPE_NULL) + { + + char* string_value; + char string_buffer[MESSAGE_ID_MAX_SIZE]; + bool free_string_value = false; + + memset(string_buffer, 0, MESSAGE_ID_MAX_SIZE); + + if (value_type == AMQP_TYPE_STRING) + { + // Codes_SRS_UAMQP_MESSAGING_09_014: [The message-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_string(uamqp_message_property, (const char**)(&string_value)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_015: [If message-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'message-id' property (string)"); + string_value = NULL; + } + } + else if (value_type == AMQP_TYPE_ULONG) + { + uint64_t ulong_value; + + // Codes_SRS_UAMQP_MESSAGING_09_014: [The message-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_ulong(uamqp_message_property, &ulong_value) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_015: [If message-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'message-id' property (ulong)"); + string_value = NULL; + } + else if (sprintf(string_buffer, "%" PRIu64, ulong_value) < 0) + { + LogError("Failed converting 'message-id' (ulong) to string"); + string_value = NULL; + } + else + { + string_value = string_buffer; + } + } + else if (value_type == AMQP_TYPE_UUID) + { + uuid uuid_value; + + // Codes_SRS_UAMQP_MESSAGING_09_014: [The message-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_uuid(uamqp_message_property, &uuid_value) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_015: [If message-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'message-id' property (UUID)"); + string_value = NULL; + } + else if ((string_value = UUID_to_string((UUID*)uuid_value)) == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_015: [If message-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get the string representation of 'message-id' UUID"); + string_value = NULL; + } + else + { + free_string_value = true; + } + } + else + { + LogError("Unrecognized type for message-id (%d)", value_type); + string_value = NULL; + } + + if (string_value != NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_016: [The message-id property shall be set on the IOTHUB_MESSAGE_HANDLE instance by calling IoTHubMessage_SetMessageId(), passing the value read from the uAMQP message.] + if (IoTHubMessage_SetMessageId(iothub_message_handle, string_value) != IOTHUB_MESSAGE_OK) + { + // Codes_SRS_UAMQP_MESSAGING_09_017: [If IoTHubMessage_SetMessageId fails, IoTHubMessage_CreateFromuAMQPMessage() shall fail and return immediately.] + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'message-id' property."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (free_string_value) + { + // Only certain code paths allocate this. + free(string_value); + } + } + else + { + LogError("Unexpected null string for message-id"); + result = __FAILURE__; + } + } + else + { + result = RESULT_OK; + } + } + + return result; +} + +static int readCorrelationIdFromuAQMPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + AMQP_VALUE uamqp_message_property; + + // Codes_SRS_UAMQP_MESSAGING_09_018: [The correlation-id property shall be read from the uAMQP message by calling properties_get_correlation_id.] + if (properties_get_correlation_id(uamqp_message_properties, &uamqp_message_property) != 0 || uamqp_message_property == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_019: [If the uAMQP message does not contain property `correlation ID`, it shall be skipped as it is optional.] + result = RESULT_OK; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_020: [The type of the correlation-id property value shall be obtained using amqpvalue_get_type().] + AMQP_TYPE value_type = amqpvalue_get_type(uamqp_message_property); + + // Codes_SRS_UAMQP_MESSAGING_09_021: [If the type of the correlation-id property value is AMQP_TYPE_NULL, IoTHubMessage_CreateFromuAMQPMessage() shall skip processing the correlation-id (as it is optional) and continue normally.] + if (value_type != AMQP_TYPE_NULL) + { + char* string_value; + char string_buffer[MESSAGE_ID_MAX_SIZE]; + bool free_string_value = false; + + memset(string_buffer, 0, MESSAGE_ID_MAX_SIZE); + + if (value_type == AMQP_TYPE_STRING) + { + // Codes_SRS_UAMQP_MESSAGING_09_022: [The correlation-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_string(uamqp_message_property, (const char**)(&string_value)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_023: [If correlation-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'correlation-id' property (string)"); + string_value = NULL; + result = __FAILURE__; + } + } + else if (value_type == AMQP_TYPE_ULONG) + { + uint64_t ulong_value; + + // Codes_SRS_UAMQP_MESSAGING_09_022: [The correlation-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_ulong(uamqp_message_property, &ulong_value) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_023: [If correlation-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'correlation-id' property (ulong)"); + string_value = NULL; + result = __FAILURE__; + } + else if (sprintf(string_buffer, "%" PRIu64, ulong_value) < 0) + { + LogError("Failed converting 'correlation-id' (ulong) to string"); + string_value = NULL; + result = __FAILURE__; + } + else + { + string_value = string_buffer; + } + } + else if (value_type == AMQP_TYPE_UUID) + { + uuid uuid_value; + + // Codes_SRS_UAMQP_MESSAGING_09_022: [The correlation-id value shall be retrieved from the AMQP_VALUE as char sequence] + if (amqpvalue_get_uuid(uamqp_message_property, &uuid_value) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_023: [If correlation-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get value of uAMQP message 'correlation-id' property (UUID)"); + string_value = NULL; + result = __FAILURE__; + } + else if ((string_value = UUID_to_string((UUID*)uuid_value)) == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_023: [If correlation-id fails to be obtained, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get the string representation of 'correlation-id' UUID"); + string_value = NULL; + result = __FAILURE__; + } + else + { + free_string_value = true; + } + } + else + { + LogError("Unrecognized type for correlation-id (%d)", value_type); + string_value = NULL; + result = __FAILURE__; + } + + if (string_value != NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_024: [The correlation-id property shall be set on the IOTHUB_MESSAGE_HANDLE by calling IoTHubMessage_SetCorrelationId, passing the value read from the uAMQP message.] + if (IoTHubMessage_SetCorrelationId(iothub_message_handle, string_value) != IOTHUB_MESSAGE_OK) + { + // Codes_SRS_UAMQP_MESSAGING_09_025: [If IoTHubMessage_SetCorrelationId fails, IoTHubMessage_CreateFromuAMQPMessage() shall fail and return immediately.] + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'correlation-id' property."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; + } + + if (free_string_value) + { + // Only certain code paths allocate this. + free(string_value); + } + } + else + { + LogError("Unexpected null string for correlation-id"); + result = __FAILURE__; + } + } + else + { + result = RESULT_OK; + } + } + + return result; +} + +static int readPropertiesFromuAMQPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, MESSAGE_HANDLE uamqp_message) +{ + int result; + PROPERTIES_HANDLE uamqp_message_properties; + + // Codes_SRS_UAMQP_MESSAGING_09_008: [The uAMQP message properties shall be retrieved using message_get_properties().] + if (message_get_properties(uamqp_message, &uamqp_message_properties) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_009: [If message_get_properties() fails, message_create_IoTHubMessage_from_uamqp_message shall fail and return immediately.] + LogError("Failed to get property properties map from uAMQP message."); + result = __FAILURE__; + } + else + { + result = RESULT_OK; // Properties 'message-id' and 'correlation-id' are optional according to the AMQP 1.0 spec. + + if (readMessageIdFromuAQMPMessage(iothub_message_handle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed readMessageIdFromuAQMPMessage."); + result = __FAILURE__; + } + else if (readCorrelationIdFromuAQMPMessage(iothub_message_handle, uamqp_message_properties) != RESULT_OK) + { + LogError("Failed readPropertiesFromuAMQPMessage."); + result = __FAILURE__; + } + else + { + const char* uamqp_message_property_value = NULL; + + // Codes_SRS_UAMQP_MESSAGING_09_100: [If the uAMQP message contains property `content-type`, it shall be set on IOTHUB_MESSAGE_HANDLE] + // Codes_SRS_UAMQP_MESSAGING_31_122: [If the uAMQP message does not contain property `content-type`, it shall be skipped as it is optional] + if (properties_get_content_type(uamqp_message_properties, &uamqp_message_property_value) == 0 && uamqp_message_property_value != NULL) + { + if (IoTHubMessage_SetContentTypeSystemProperty(iothub_message_handle, uamqp_message_property_value) != IOTHUB_MESSAGE_OK) + { + // Codes_SRS_UAMQP_MESSAGING_09_102: [If setting the `content-type` property on IOTHUB_MESSAGE_HANDLE fails, readPropertiesFromuAMQPMessage() shall fail and return immediately.] + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'content-type' property."); + result = __FAILURE__; + } + } + + uamqp_message_property_value = NULL; + + // Codes_SRS_UAMQP_MESSAGING_09_103: [If the uAMQP message contains property `content-encoding`, it shall be set on IOTHUB_MESSAGE_HANDLE] + // Codes_SRS_UAMQP_MESSAGING_31_123: [If the uAMQP message does not contain property `content-encoding`, it shall be skipped as it is optional] + if (properties_get_content_encoding(uamqp_message_properties, &uamqp_message_property_value) == 0 && uamqp_message_property_value != NULL) + { + if (IoTHubMessage_SetContentEncodingSystemProperty(iothub_message_handle, uamqp_message_property_value) != IOTHUB_MESSAGE_OK) + { + // Codes_SRS_UAMQP_MESSAGING_09_105: [If setting the `content-encoding` property on IOTHUB_MESSAGE_HANDLE fails, readPropertiesFromuAMQPMessage() shall fail and return immediately.] + LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'content-encoding' property."); + result = __FAILURE__; + } + } + } + + // Codes_SRS_UAMQP_MESSAGING_09_026: [message_create_IoTHubMessage_from_uamqp_message() shall destroy the uAMQP message properties (obtained with message_get_properties()) by calling properties_destroy().] + properties_destroy(uamqp_message_properties); + } + + return result; +} + +static int readApplicationPropertiesFromuAMQPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, MESSAGE_HANDLE uamqp_message) +{ + int result; + AMQP_VALUE uamqp_app_properties = NULL; + AMQP_VALUE uamqp_app_properties_ipdv = NULL; + uint32_t property_count = 0; + MAP_HANDLE iothub_message_properties_map; + + // Codes_SRS_UAMQP_MESSAGING_09_027: [The IOTHUB_MESSAGE_HANDLE properties shall be retrieved using IoTHubMessage_Properties.] + if ((iothub_message_properties_map = IoTHubMessage_Properties(iothub_message_handle)) == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_028: [If IoTHubMessage_Properties fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to get property map from IoTHub message."); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_09_029: [The uAMQP message application properties shall be retrieved using message_get_application_properties.] + else if ((result = message_get_application_properties(uamqp_message, &uamqp_app_properties)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_030: [If message_get_application_properties fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed reading the incoming uAMQP message properties (return code %d).", result); + result = __FAILURE__; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_031: [If message_get_application_properties succeeds but returns a NULL application properties map (there are no properties), message_create_IoTHubMessage_from_uamqp_message() shall skip processing the properties and continue normally.] + if (uamqp_app_properties == NULL) + { + result = 0; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_032: [The actual uAMQP message application properties should be extracted from the result of message_get_application_properties using amqpvalue_get_inplace_described_value.] + if ((uamqp_app_properties_ipdv = amqpvalue_get_inplace_described_value(uamqp_app_properties)) == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_033: [If amqpvalue_get_inplace_described_value fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed getting the map of uAMQP message application properties (return code %d).", result); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_09_034: [The number of items in the uAMQP message application properties shall be obtained using amqpvalue_get_map_pair_count.] + else if ((result = amqpvalue_get_map_pair_count(uamqp_app_properties_ipdv, &property_count)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_035: [If amqpvalue_get_map_pair_count fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed reading the number of values in the uAMQP property map (return code %d).", result); + result = __FAILURE__; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_036: [message_create_IoTHubMessage_from_uamqp_message() shall iterate through each uAMQP application property and add it to IOTHUB_MESSAGE_HANDLE properties.] + uint32_t i; + for (i = 0; result == RESULT_OK && i < property_count; i++) + { + AMQP_VALUE map_key_name = NULL; + AMQP_VALUE map_key_value = NULL; + const char *key_name; + const char* key_value; + + // Codes_SRS_UAMQP_MESSAGING_09_037: [The uAMQP application property name and value shall be obtained using amqpvalue_get_map_key_value_pair.] + if ((result = amqpvalue_get_map_key_value_pair(uamqp_app_properties_ipdv, i, &map_key_name, &map_key_value)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_038: [If amqpvalue_get_map_key_value_pair fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed reading the key/value pair from the uAMQP property map (return code %d).", result); + result = __FAILURE__; + } + + // Codes_SRS_UAMQP_MESSAGING_09_039: [The uAMQP application property name shall be extracted as string using amqpvalue_get_string.] + else if ((result = amqpvalue_get_string(map_key_name, &key_name)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_040: [If amqpvalue_get_string fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed parsing the uAMQP property name (return code %d).", result); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_09_041: [The uAMQP application property value shall be extracted as string using amqpvalue_get_string.] + else if ((result = amqpvalue_get_string(map_key_value, &key_value)) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_042: [If amqpvalue_get_string fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed parsing the uAMQP property value (return code %d).", result); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_09_043: [The application property name and value shall be added to IOTHUB_MESSAGE_HANDLE properties using Map_AddOrUpdate.] + else if (Map_AddOrUpdate(iothub_message_properties_map, key_name, key_value) != MAP_OK) + { + // Codes_SRS_UAMQP_MESSAGING_09_044: [If Map_AddOrUpdate fails, message_create_IoTHubMessage_from_uamqp_message() shall fail and return immediately.] + LogError("Failed to add/update IoTHub message property map."); + result = __FAILURE__; + } + + // Codes_SRS_UAMQP_MESSAGING_09_045: [message_create_IoTHubMessage_from_uamqp_message() shall destroy the uAMQP message property name and value (obtained with amqpvalue_get_string) by calling amqpvalue_destroy().] + if (map_key_name != NULL) + { + amqpvalue_destroy(map_key_name); + } + + if (map_key_value != NULL) + { + amqpvalue_destroy(map_key_value); + } + } + } + + // Codes_SRS_UAMQP_MESSAGING_09_046: [message_create_IoTHubMessage_from_uamqp_message() shall destroy the uAMQP message property (obtained with message_get_application_properties) by calling amqpvalue_destroy().] + amqpvalue_destroy(uamqp_app_properties); + } + } + + return result; +} + +int message_create_IoTHubMessage_from_uamqp_message(MESSAGE_HANDLE uamqp_message, IOTHUB_MESSAGE_HANDLE* iothubclient_message) +{ + int result = __FAILURE__; + + IOTHUB_MESSAGE_HANDLE iothub_message = NULL; + MESSAGE_BODY_TYPE body_type; + + // Codes_SRS_UAMQP_MESSAGING_09_001: [The body type of the uAMQP message shall be retrieved using message_get_body_type().] + if (message_get_body_type(uamqp_message, &body_type) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_002: [If message_get_body_type() fails, message_create_IoTHubMessage_from_uamqp_message shall fail and return immediately.] + LogError("Failed to get the type of the uamqp message."); + result = __FAILURE__; + } + else + { + // Codes_SRS_UAMQP_MESSAGING_09_003: [If the uAMQP message body type is MESSAGE_BODY_TYPE_DATA, the body data shall be treated as binary data.] + if (body_type == MESSAGE_BODY_TYPE_DATA) + { + // Codes_SRS_UAMQP_MESSAGING_09_004: [The uAMQP message body data shall be retrieved using message_get_body_amqp_data().] + BINARY_DATA binary_data; + if (message_get_body_amqp_data_in_place(uamqp_message, 0, &binary_data) != 0) + { + // Codes_SRS_UAMQP_MESSAGING_09_005: [If message_get_body_amqp_data() fails, message_create_IoTHubMessage_from_uamqp_message shall fail and return immediately.] + LogError("Failed to get the body of the uamqp message."); + result = __FAILURE__; + } + // Codes_SRS_UAMQP_MESSAGING_09_006: [The IOTHUB_MESSAGE instance shall be created using IoTHubMessage_CreateFromByteArray(), passing the uAMQP body bytes as parameter.] + else if ((iothub_message = IoTHubMessage_CreateFromByteArray(binary_data.bytes, binary_data.length)) == NULL) + { + // Codes_SRS_UAMQP_MESSAGING_09_007: [If IoTHubMessage_CreateFromByteArray() fails, message_create_IoTHubMessage_from_uamqp_message shall fail and return immediately.] + LogError("Failed creating the IOTHUB_MESSAGE_HANDLE instance (IoTHubMessage_CreateFromByteArray failed)."); + result = __FAILURE__; + } + } + } + + if (iothub_message != NULL) + { + if (readPropertiesFromuAMQPMessage(iothub_message, uamqp_message) != RESULT_OK) + { + LogError("Failed reading properties of the uamqp message."); + IoTHubMessage_Destroy(iothub_message); + result = __FAILURE__; + } + else if (readApplicationPropertiesFromuAMQPMessage(iothub_message, uamqp_message) != RESULT_OK) + { + LogError("Failed reading application properties of the uamqp message."); + IoTHubMessage_Destroy(iothub_message); + result = __FAILURE__; + } + else + { + *iothubclient_message = iothub_message; + result = RESULT_OK; + } + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/upload_to_blob.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,4 @@ +LIBRARY iothub_client_dll +EXPORTS + IoTHubClient_UploadToBlobAsync + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULTStrings \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_client/src/version.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "iothub_client_version.h" + +const char* IoTHubClient_GetVersionString(void) +{ + /*Codes_SRS_IOTHUBCLIENT_05_001: [IoTHubClient_GetVersionString shall return a pointer to a constant string which indicates the version of IoTHubClient API.]*/ + return IOTHUB_SDK_VERSION; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_deviceconfiguration.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_DEVICECONFIGURATION_H +#define IOTHUB_DEVICECONFIGURATION_H + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include <time.h> +#include "iothub_service_client_auth.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define IOTHUB_DEVICE_CONFIGURATION_RESULT_VALUES \ + IOTHUB_DEVICE_CONFIGURATION_OK, \ + IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG, \ + IOTHUB_DEVICE_CONFIGURATION_ERROR, \ + IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR, \ + IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR, \ + IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR, \ + IOTHUB_DEVICE_CONFIGURATION_CONFIGURATION_NOT_EXIST, \ + IOTHUB_DEVICE_CONFIGURATION_CONFIGURATION_EXIST \ + +DEFINE_ENUM(IOTHUB_DEVICE_CONFIGURATION_RESULT, IOTHUB_DEVICE_CONFIGURATION_RESULT_VALUES); + +#define IOTHUB_DEVICECONFIGURATION_REQUEST_MODE_VALUES \ + IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST, \ + IOTHUB_DEVICECONFIGURATION_REQUEST_GET, \ + IOTHUB_DEVICECONFIGURATION_REQUEST_ADD, \ + IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE, \ + IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE, \ + IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT + +DEFINE_ENUM(IOTHUB_DEVICECONFIGURATION_REQUEST_MODE, IOTHUB_DEVICECONFIGURATION_REQUEST_MODE_VALUES); + +typedef struct IOTHUB_DEVICE_CONFIGURATION_CONTENT_TAG +{ + const char* deviceContent; + const char* modulesContent; +} IOTHUB_DEVICE_CONFIGURATION_CONTENT; + +typedef struct IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULTS_TAG +{ + size_t numQueries; + const char** queryNames; + double* results; +} IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT; + +typedef struct IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION_TAG +{ + size_t numQueries; + const char** queryNames; + const char** queryStrings; +} IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION; + +typedef struct IOTHUB_DEVICE_CONFIGURATION_LABEL_TAG +{ + size_t numLabels; + const char** labelNames; + const char** labelValues; +} IOTHUB_DEVICE_CONFIGURATION_LABELS; + +#define IOTHUB_DEVICE_CONFIGURATION_SCHEMA_VERSION_1 "1.0" +#define IOTHUB_DEVICE_CONFIGURATION_VERSION_1 1 +typedef struct IOTHUB_DEVICE_CONFIGURATION_TAG +{ + int version; + const char* schemaVersion; //version 1+ + const char* configurationId; //version 1+ + const char* targetCondition; //version 1+ + const char* eTag; //version 1+ + const char* createdTimeUtc; //version 1+ + const char* lastUpdatedTimeUtc; //version 1+ + int priority; //version 1+ + + IOTHUB_DEVICE_CONFIGURATION_CONTENT content; //version 1+ + IOTHUB_DEVICE_CONFIGURATION_LABELS labels; //version 1+ + + IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT systemMetricsResult; //version 1+ + IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION systemMetricsDefinition; //version 1+ + + IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT metricResult; //version 1+ + IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION metricsDefinition; //version 1+ +} IOTHUB_DEVICE_CONFIGURATION; + +#define IOTHUB_DEVICE_CONFIGURATION_ADD_VERSION_1 1 +typedef struct IOTHUB_DEVICE_CONFIGURATION_ADD_TAG +{ + int version; + const char* configurationId; //version 1+ + const char* targetCondition; //version 1+ + int priority; //version 1+ + + IOTHUB_DEVICE_CONFIGURATION_CONTENT content; //version 1+ + IOTHUB_DEVICE_CONFIGURATION_LABELS labels; //version 1+ + IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION metrics; //version 1+ +} IOTHUB_DEVICE_CONFIGURATION_ADD; + +/** @brief Handle to hide struct and use it in consequent APIs +*/ +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_TAG* IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE; + + +/** @brief Creates a IoT Hub Service Client DeviceConfiguration handle for use it in consequent APIs. +* +* @param serviceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE value that is used when +* invoking other functions for IoT Hub DeviceConfiguration and @c NULL on failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, IoTHubDeviceConfiguration_Create, IOTHUB_SERVICE_CLIENT_AUTH_HANDLE, serviceClientHandle); + +/** @brief Disposes of resources allocated by the IoT Hub IoTHubDeviceConfiguration_Create. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubDeviceConfiguration_Destroy, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle); + +/** @brief Retrieves the Configuration info for multiple configurations from IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param maxConfigurationsCount Maximum number of configurations requested +* @param configurations Output parameter, if it is not NULL will contain the requested configurations +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_GetConfigurations, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, size_t, maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE, configurationsList); + +/** @brief Retrieves the Configuration info for specified configurationId from IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param configurationId The configuration name (id) to retrieve Configuration info for. +* @param configuration Output parameter, if it is not NULL will contain the requested configuration info structure +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_GetConfiguration, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, const char*, configurationId, IOTHUB_DEVICE_CONFIGURATION*, configuration); + +/** @brief Adds the Configuration info to IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param configurationAdd IOTHUB_DEVICE_CONFIGURATION_ADD structure containing +* the new configuration Id and other optional parameters +* @param configuration Output parameter, if it is not NULL will contain the created configuration info structure +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_AddConfiguration, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, const IOTHUB_DEVICE_CONFIGURATION_ADD*, configurationAdd, IOTHUB_DEVICE_CONFIGURATION*, configuration); + +/** @brief Updates the given Configuration in IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param configuration IOTHUB_DEVICE_CONFIGURATION structure containing the new configuration info. +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_UpdateConfiguration, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, const IOTHUB_DEVICE_CONFIGURATION*, configuration); + +/** @brief Deletes the given Configuration from IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param configurationId The configuration name (id) to delete Configuration info for. +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_DeleteConfiguration, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, const char*, configurationId); + +/** @brief Deletes the given Configuration from IoT Hub. +* +* @param serviceClientDeviceConfigurationHandle The handle created by a call to the create function. +* @param deviceOrModuleId The target device or module id for the Configuration content. +* @param configurationContent The configuration content to be applied. +* +* @return IOTHUB_DEVICE_CONFIGURATION_RESULT upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_CONFIGURATION_RESULT, IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule, IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE, serviceClientDeviceConfigurationHandle, const char*, deviceOrModuleId, const IOTHUB_DEVICE_CONFIGURATION_CONTENT*, configurationContent); + + +/** +* @brief Free members of the IOTHUB_DEVICE_CONFIGURATION structure (NOT the structure itself) +* +* @param configuration The structure to have its members freed. +*/ +extern void IoTHubDeviceConfiguration_FreeConfigurationMembers(IOTHUB_DEVICE_CONFIGURATION* configuration); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_DEVICECONFIGURATION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_devicemethod.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_DEVICETMETHOD_H +#define IOTHUB_DEVICETMETHOD_H + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + +#include "iothub_service_client_auth.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#define IOTHUB_DEVICE_METHOD_RESULT_VALUES \ + IOTHUB_DEVICE_METHOD_OK, \ + IOTHUB_DEVICE_METHOD_INVALID_ARG, \ + IOTHUB_DEVICE_METHOD_ERROR, \ + IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR \ + +DEFINE_ENUM(IOTHUB_DEVICE_METHOD_RESULT, IOTHUB_DEVICE_METHOD_RESULT_VALUES); + +/** @brief Handle to hide struct and use it in consequent APIs +*/ +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_TAG* IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE; + +/** @brief Creates a IoT Hub Service Client DeviceMethod handle for use it in consequent APIs. +* +* @param serviceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE value that is used when +* invoking other functions for IoT Hub DeviceMethod and @c NULL on failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE, IoTHubDeviceMethod_Create, IOTHUB_SERVICE_CLIENT_AUTH_HANDLE, serviceClientHandle); + +/** @brief Disposes of resources allocated by the IoT Hub IoTHubDeviceMethod_Create. +* +* @param serviceClientDeviceMethodHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubDeviceMethod_Destroy, IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE, serviceClientDeviceMethodHandle); + +/** @brief Call a method on device with a given payload. +* +* @param serviceClientDeviceMethodHandle The handle created by a call to the create function. +* @param deviceId The device name (id) to call a method on. +* @param methodName The method name to call. +* @param methodPayload The message payload to send. +* @param response Output buffer for response payload. +* @param timeout Time before IoTHubDeviceMethod_InvokeModule times out. +* @param responseStatus Response status code from invocation +* @param responsePayload Output buffer for response payload. +* @param responsePayloadSize String length of responsePayload. +* +* @return An IOTHUB_DEVICE_METHOD_RESULT containing the return status. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_METHOD_RESULT, IoTHubDeviceMethod_Invoke, IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE, serviceClientDeviceMethodHandle, const char*, deviceId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + +/** @brief Call a method on device and a module with a given payload. +* +* @param serviceClientDeviceMethodHandle The handle created by a call to the create function. +* @param deviceId The device name (id) to call a method on. +* @param moduleId The module name (id) to call a method on. +* @param methodName The method name to call. +* @param methodPayload The message payload to send. +* @param timeout Time before IoTHubDeviceMethod_InvokeModule times out. +* @param responseStatus Response status code from invocation. +* @param responsePayload Output buffer for response payload. +* @param responsePayloadSize String length of responsePayload. +* +* @return An IOTHUB_DEVICE_METHOD_RESULT containing the return status. +*/ +MOCKABLE_FUNCTION(, IOTHUB_DEVICE_METHOD_RESULT, IoTHubDeviceMethod_InvokeModule, IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE, serviceClientDeviceMethodHandle, const char*, deviceId, const char*, moduleId, const char*, methodName, const char*, methodPayload, unsigned int, timeout, int*, responseStatus, unsigned char**, responsePayload, size_t*, responsePayloadSize); + + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_DEVICETMETHOD_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_devicetwin.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_DEVICETWIN_H +#define IOTHUB_DEVICETWIN_H + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/map.h" +#include <time.h> +#include "iothub_service_client_auth.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + +#define IOTHUB_DEVICE_TWIN_RESULT_VALUES \ + IOTHUB_DEVICE_TWIN_OK, \ + IOTHUB_DEVICE_TWIN_INVALID_ARG, \ + IOTHUB_DEVICE_TWIN_ERROR, \ + IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR \ + +DEFINE_ENUM(IOTHUB_DEVICE_TWIN_RESULT, IOTHUB_DEVICE_TWIN_RESULT_VALUES); + +/** @brief Handle to hide struct and use it in consequent APIs +*/ +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_TAG* IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE; + + +/** @brief Creates a IoT Hub Service Client DeviceTwin handle for use it in consequent APIs. +* +* @param serviceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE value that is used when +* invoking other functions for IoT Hub DeviceTwin and @c NULL on failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, IoTHubDeviceTwin_Create, IOTHUB_SERVICE_CLIENT_AUTH_HANDLE, serviceClientHandle); + +/** @brief Disposes of resources allocated by the IoT Hub IoTHubDeviceTwin_Create. +* +* @param serviceClientDeviceTwinHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubDeviceTwin_Destroy, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, serviceClientDeviceTwinHandle); + +/** @brief Retrieves the given device's twin info. +* +* @param serviceClientDeviceTwinHandle The handle created by a call to the create function. +* @param deviceId The device name (id) to retrieve twin info for. +* +* @return A non-NULL char* containing device twin info upon success or NULL upon failure. +*/ +MOCKABLE_FUNCTION(, char*, IoTHubDeviceTwin_GetTwin, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, serviceClientDeviceTwinHandle, const char*, deviceId); + +/** @brief Updates (partial update) the given device's twin info. +* +* @param serviceClientDeviceTwinHandle The handle created by a call to the create function. +* @param deviceId The device name (id) to update the twin info for. +* @param deviceTwinJson DeviceTwin JSon string containing the info (tags, desired properties) to update. +* All well-known read-only members are ignored. +* Properties provided with value of null are removed from twin's document. +* +* @return A non-NULL char* containing updated device twin info upon success or NULL upon failure. +*/ +MOCKABLE_FUNCTION(, char*, IoTHubDeviceTwin_UpdateTwin, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, serviceClientDeviceTwinHandle, const char*, deviceId, const char*, deviceTwinJson); + +/** @brief Retrieves the given module's twin info. +* +* @param serviceClientDeviceTwinHandle The handle created by a call to the create function. +* @param deviceId The device name (id) containing the module to retrieve the twin info for. +* @param moduleId The module name (id) to retrieve twin info for. +* +* @return A non-NULL char* containing module twin info upon success or NULL upon failure. +*/ +MOCKABLE_FUNCTION(, char*, IoTHubDeviceTwin_GetModuleTwin, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, serviceClientDeviceTwinHandle, const char*, deviceId, const char*, moduleId); + +/** @brief Updates (partial update) the given module's twin info. +* +* @param serviceClientDeviceTwinHandle The handle created by a call to the create function. +* @param deviceId The device name (id) containing the module to update. +* @param moduleId The module name (id) to update the twin info for. +* @param moduleTwinJson ModuleTwin JSon string containing the info (tags, desired properties) to update. +* All well-known read-only members are ignored. +* Properties provided with value of null are removed from twin's document. +* +* @return A non-NULL char* containing updated module twin info upon success or NULL upon failure. +*/ +MOCKABLE_FUNCTION(, char*, IoTHubDeviceTwin_UpdateModuleTwin, IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE, serviceClientDeviceTwinHandle, const char*, deviceId, const char*, moduleId, const char*, moduleTwinJson); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_DEVICETWIN_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_messaging.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_MESSAGING_H +#define IOTHUB_MESSAGING_H + +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "iothub_messaging_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + +typedef struct IOTHUB_MESSAGING_CLIENT_INSTANCE_TAG* IOTHUB_MESSAGING_CLIENT_HANDLE; + +/** @brief Creates a IoT Hub Service Client Messaging handle for use it in consequent APIs. +* +* @param serviceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_MESSAGING_CLIENT_HANDLE value that is used when +* invoking other functions for IoT Hub DeviceMethod and @c NULL on failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_CLIENT_HANDLE, IoTHubMessaging_Create, IOTHUB_SERVICE_CLIENT_AUTH_HANDLE, serviceClientHandle); + +/** @brief Disposes of resources allocated by the IoT Hub Service Client Messaging. +* +* @param messagingClientHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessaging_Destroy, IOTHUB_MESSAGING_CLIENT_HANDLE, messagingClientHandle); + +/** @brief Opens connection to IoTHub. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param openCompleteCallback The callback specified by the user for receiving +* confirmation of the connection opened. +* The user can specify a @c NULL value here to +* indicate that no callback is required. +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @return IOTHUB_MESSAGING_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_Open, IOTHUB_MESSAGING_CLIENT_HANDLE, messagingClientHandle, IOTHUB_OPEN_COMPLETE_CALLBACK, openCompleteCallback, void*, userContextCallback); + +/** @brief Closes connection to IoTHub. +* +* @param messagingClientHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessaging_Close, IOTHUB_MESSAGING_CLIENT_HANDLE, messagingClientHandle); + +/** +* @brief Asynchronous call to send the message to a specified device. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param deviceId The name (Id) of the device to send the message to. +* @param message The message to send. +* @param sendCompleteCallback The callback specified by the user for receiving +* confirmation of the delivery of the message. +* The user can specify a @c NULL value here to +* indicate that no callback is required. +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @b NOTE: The application behavior is undefined if the user calls +* the ::IoTHubMessaging_Destroy or IoTHubMessaging_Close function from within any callback. +* +* @return IOTHUB_MESSAGING_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_SendAsync, IOTHUB_MESSAGING_CLIENT_HANDLE, messagingClientHandle, const char*, deviceId, IOTHUB_MESSAGE_HANDLE, message, IOTHUB_SEND_COMPLETE_CALLBACK, sendCompleteCallback, void*, userContextCallback); + +/** +* @brief This API specifies a callback to be used when the device receives the message. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param feedbackMessageReceivedCallback The callback specified by the user to be used for receiveng +* confirmation feedback from the deice about the recevied message. +* +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @b NOTE: The application behavior is undefined if the user calls +* the ::IoTHubMessaging_Destroy or IoTHubMessaging_Close function from within any callback. +* +* @return IOTHUB_CLIENT_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_SetFeedbackMessageCallback, IOTHUB_MESSAGING_CLIENT_HANDLE, messagingClientHandle, IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK, feedbackMessageReceivedCallback, void*, userContextCallback); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_MESSAGING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_messaging_ll.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_MESSAGING_LL_H +#define IOTHUB_MESSAGING_LL_H + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/map.h" +#include "iothub_message.h" +#include "iothub_service_client_auth.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + + +#define IOTHUB_FEEDBACK_STATUS_CODE_VALUES \ + IOTHUB_FEEDBACK_STATUS_CODE_SUCCESS, \ + IOTHUB_FEEDBACK_STATUS_CODE_EXPIRED, \ + IOTHUB_FEEDBACK_STATUS_CODE_DELIVER_COUNT_EXCEEDED, \ + IOTHUB_FEEDBACK_STATUS_CODE_REJECTED, \ + IOTHUB_FEEDBACK_STATUS_CODE_UNKNOWN \ + +DEFINE_ENUM(IOTHUB_FEEDBACK_STATUS_CODE, IOTHUB_FEEDBACK_STATUS_CODE_VALUES); + +#define IOTHUB_MESSAGE_SEND_STATE_VALUES \ + IOTHUB_MESSAGE_SEND_STATE_NOT_SENT, \ + IOTHUB_MESSAGE_SEND_STATE_SEND_IN_PROGRESS, \ + IOTHUB_MESSAGE_SEND_STATE_SENT_OK, \ + IOTHUB_MESSAGE_SEND_STATE_SEND_FAILED \ + +DEFINE_ENUM(IOTHUB_MESSAGE_SEND_STATE, IOTHUB_MESSAGE_SEND_STATE_VALUES); + +#define IOTHUB_MESSAGING_RESULT_VALUES \ + IOTHUB_MESSAGING_OK, \ + IOTHUB_MESSAGING_INVALID_ARG, \ + IOTHUB_MESSAGING_ERROR, \ + IOTHUB_MESSAGING_INVALID_JSON, \ + IOTHUB_MESSAGING_DEVICE_EXIST, \ + IOTHUB_MESSAGING_CALLBACK_NOT_SET \ + +DEFINE_ENUM(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_RESULT_VALUES); + +typedef struct IOTHUB_SERVICE_FEEDBACK_RECORD_TAG +{ + char* description; + const char* deviceId; + const char* correlationId; + const char* generationId; + const char* enqueuedTimeUtc; + IOTHUB_FEEDBACK_STATUS_CODE statusCode; + const char* originalMessageId; +} IOTHUB_SERVICE_FEEDBACK_RECORD; + +typedef struct IOTHUB_SERVICE_FEEDBACK_BATCH_TAG +{ + const char* userId; + const char* lockToken; + SINGLYLINKEDLIST_HANDLE feedbackRecordList; +} IOTHUB_SERVICE_FEEDBACK_BATCH; + +typedef struct IOTHUB_MESSAGING_TAG* IOTHUB_MESSAGING_HANDLE; + +typedef void(*IOTHUB_OPEN_COMPLETE_CALLBACK)(void* context); +typedef void(*IOTHUB_SEND_COMPLETE_CALLBACK)(void* context, IOTHUB_MESSAGING_RESULT messagingResult); +typedef void(*IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK)(void* context, IOTHUB_SERVICE_FEEDBACK_BATCH* feedbackBatch); + +/** @brief Creates a IoT Hub Service Client Messaging handle for use it in consequent APIs. +* +* @param iotHubMessagingServiceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_MESSAGING_CLIENT_HANDLE value that is used when +* invoking other functions for IoT Hub DeviceMethod and @c NULL on failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_HANDLE, IoTHubMessaging_LL_Create, IOTHUB_SERVICE_CLIENT_AUTH_HANDLE, iotHubMessagingServiceClientHandle); + +/** @brief Disposes of resources allocated by the IoT Hub Service Client Messaging. +* +* @param messagingClientHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessaging_LL_Destroy, IOTHUB_MESSAGING_HANDLE, messagingHandle); + +/** @brief Opens connection to IoTHub. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param openCompleteCallback The callback specified by the user for receiving +* confirmation of the connection opened. +* The user can specify a @c NULL value here to +* indicate that no callback is required. +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @return IOTHUB_MESSAGING_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_LL_Open, IOTHUB_MESSAGING_HANDLE, messagingHandle, IOTHUB_OPEN_COMPLETE_CALLBACK, openCompleteCallback, void*, userContextCallback); + +/** @brief Closes connection to IoTHub. +* +* @param messagingClientHandle The handle created by a call to the create function. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessaging_LL_Close, IOTHUB_MESSAGING_HANDLE, messagingHandle); + +/** +* @brief Synchronous call to send the message to a specified device. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param deviceId The name (Id) of the device to send the message to. +* @param message The message to send. +* @param sendCompleteCallback The callback specified by the user for receiving +* confirmation of the delivery of the message. +* The user can specify a @c NULL value here to +* indicate that no callback is required. +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @b NOTE: The application behavior is undefined if the user calls +* the ::IoTHubMessaging_Destroy or IoTHubMessaging_Close function from within any callback. +* +* @return IOTHUB_MESSAGING_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_LL_Send, IOTHUB_MESSAGING_HANDLE, messagingHandle, const char*, deviceId, IOTHUB_MESSAGE_HANDLE, message, IOTHUB_SEND_COMPLETE_CALLBACK, sendCompleteCallback, void*, userContextCallback); + +/** +* @brief This API specifies a callback to be used when the device receives the message. +* +* @param messagingClientHandle The handle created by a call to the create function. +* @param feedbackMessageReceivedCallback The callback specified by the user to be used for receiveng +* confirmation feedback from the deice about the recevied message. +* +* @param userContextCallback User specified context that will be provided to the +* callback. This can be @c NULL. +* +* @b NOTE: The application behavior is undefined if the user calls +* the ::IoTHubMessaging_Destroy or IoTHubMessaging_Close function from within any callback. +* +* @return IOTHUB_CLIENT_OK upon success or an error code upon failure. +*/ +MOCKABLE_FUNCTION(, IOTHUB_MESSAGING_RESULT, IoTHubMessaging_LL_SetFeedbackMessageCallback, IOTHUB_MESSAGING_HANDLE, messagingHandle, IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK, feedbackMessageReceivedCallback, void*, userContextCallback); + +/** +* @brief This function is meant to be called by the user when work +* (sending/receiving) can be done by the IoTHubServiceClient. +* +* @param messagingHandle The handle created by a call to the create function. +* +* All IoTHubServiceClient interactions (in regards to network traffic +* and/or user level callbacks) are the effect of calling this +* function and they take place synchronously inside _DoWork. +*/ +MOCKABLE_FUNCTION(, void, IoTHubMessaging_LL_DoWork, IOTHUB_MESSAGING_HANDLE, messagingHandle); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_MESSAGING_LL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_registrymanager.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,413 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is under development and it is subject to change + +#ifndef IOTHUB_REGISTRYMANAGER_H +#define IOTHUB_REGISTRYMANAGER_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/map.h" +#include "iothub_service_client_auth.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define IOTHUB_REGISTRYMANAGER_RESULT_VALUES \ + IOTHUB_REGISTRYMANAGER_OK, \ + IOTHUB_REGISTRYMANAGER_INVALID_ARG, \ + IOTHUB_REGISTRYMANAGER_ERROR, \ + IOTHUB_REGISTRYMANAGER_JSON_ERROR, \ + IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR, \ + IOTHUB_REGISTRYMANAGER_HTTP_STATUS_ERROR, \ + IOTHUB_REGISTRYMANAGER_DEVICE_EXIST, \ + IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST, \ + IOTHUB_REGISTRYMANAGER_CALLBACK_NOT_SET, \ + IOTHUB_REGISTRYMANAGER_INVALID_VERSION \ + +DEFINE_ENUM(IOTHUB_REGISTRYMANAGER_RESULT, IOTHUB_REGISTRYMANAGER_RESULT_VALUES); + +#define IOTHUB_REGISTRYMANAGER_AUTH_METHOD_VALUES \ + IOTHUB_REGISTRYMANAGER_AUTH_SPK, \ + IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT, \ + IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY, \ + IOTHUB_REGISTRYMANAGER_AUTH_NONE, \ + IOTHUB_REGISTRYMANAGER_AUTH_UNKNOWN \ + + +DEFINE_ENUM(IOTHUB_REGISTRYMANAGER_AUTH_METHOD, IOTHUB_REGISTRYMANAGER_AUTH_METHOD_VALUES); + +#define IOTHUB_DEVICE_EX_VERSION_1 1 +typedef struct IOTHUB_DEVICE_EX_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + const char* generationId; //version 1+ + const char* eTag; //version 1+ + IOTHUB_DEVICE_CONNECTION_STATE connectionState; //version 1+ + const char* connectionStateUpdatedTime; //version 1+ + IOTHUB_DEVICE_STATUS status; //version 1+ + const char* statusReason; //version 1+ + const char* statusUpdatedTime; //version 1+ + const char* lastActivityTime; //version 1+ + size_t cloudToDeviceMessageCount; //version 1+ + + bool isManaged; //version 1+ + const char* configuration; //version 1+ + const char* deviceProperties; //version 1+ + const char* serviceProperties; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + + bool iotEdge_capable; //version 1+ +} IOTHUB_DEVICE_EX; + +/** +* @brief Free members of the IOTHUB_DEVICE_EX structure (NOT the structure itself) +* +* @param deviceInfo The structure to have its members freed. +*/ +extern void IoTHubRegistryManager_FreeDeviceExMembers(IOTHUB_DEVICE_EX* deviceInfo); + +#define IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_1 1 +typedef struct IOTHUB_REGISTRY_DEVICE_CREATE_EX_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + bool iotEdge_capable; //version 1+ +} IOTHUB_REGISTRY_DEVICE_CREATE_EX; + +#define IOTHUB_REGISTRY_DEVICE_UPDATE_EX_VERSION_1 1 +typedef struct IOTHUB_REGISTRY_DEVICE_UPDATE_EX_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + IOTHUB_DEVICE_STATUS status; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + bool iotEdge_capable; //version 1+ +} IOTHUB_REGISTRY_DEVICE_UPDATE_EX; + +typedef struct IOTHUB_REGISTRY_STATISTIC_TAG +{ + size_t totalDeviceCount; + size_t enabledDeviceCount; + size_t disabledDeviceCount; +} IOTHUB_REGISTRY_STATISTICS; + +#define IOTHUB_MODULE_VERSION_1 1 +typedef struct IOTHUB_MODULE_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + const char* generationId; //version 1+ + const char* eTag; //version 1+ + IOTHUB_DEVICE_CONNECTION_STATE connectionState; //version 1+ + const char* connectionStateUpdatedTime; //version 1+ + IOTHUB_DEVICE_STATUS status; //version 1+ + const char* statusReason; //version 1+ + const char* statusUpdatedTime; //version 1+ + const char* lastActivityTime; //version 1+ + size_t cloudToDeviceMessageCount; //version 1+ + + bool isManaged; //version 1+ + const char* configuration; //version 1+ + const char* deviceProperties; //version 1+ + const char* serviceProperties; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + + const char* moduleId; //version 1+ + const char* managedBy; //version 1+ +} IOTHUB_MODULE; + +/** +* @brief Free members of the IOTHUB_MODULE structure (NOT the structure itself) +* +* @param moduleInfo The structure to have its members freed. +*/ +extern void IoTHubRegistryManager_FreeModuleMembers(IOTHUB_MODULE* moduleInfo); + + +#define IOTHUB_REGISTRY_MODULE_CREATE_VERSION_1 1 +typedef struct IOTHUB_REGISTRY_MODULE_CREATE_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + const char* moduleId; //version 1+ + const char* managedBy; //version 1+ +} IOTHUB_REGISTRY_MODULE_CREATE; + +#define IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_1 1 +typedef struct IOTHUB_REGISTRY_MODULE_UPDATE_TAG +{ + int version; + const char* deviceId; //version 1+ + const char* primaryKey; //version 1+ + const char* secondaryKey; //version 1+ + IOTHUB_DEVICE_STATUS status; //version 1+ + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; //version 1+ + const char* moduleId; //version 1+ + const char* managedBy; //version 1+ +} IOTHUB_REGISTRY_MODULE_UPDATE; + +/** @brief Structure to store IoTHub authentication information +*/ +typedef struct IOTHUB_REGISTRYMANAGER_TAG +{ + char* hostname; + char* iothubName; + char* iothubSuffix; + char* sharedAccessKey; + char* keyName; + char* deviceId; +} IOTHUB_REGISTRYMANAGER; + +/** @brief Handle to hide struct and use it in consequent APIs +*/ +typedef struct IOTHUB_REGISTRYMANAGER_TAG* IOTHUB_REGISTRYMANAGER_HANDLE; + +/** +* @brief Creates a IoT Hub Registry Manager handle for use it +* in consequent APIs. +* +* @param serviceClientHandle Service client handle. +* +* @return A non-NULL @c IOTHUB_REGISTRYMANAGER_HANDLE value that is used when +* invoking other functions for IoT Hub REgistry Manager and @c NULL on failure. +*/ +extern IOTHUB_REGISTRYMANAGER_HANDLE IoTHubRegistryManager_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle); + +/** +* @brief Disposes of resources allocated by the IoT Hub Registry Manager. +* +* @param registryManagerHandle The handle created by a call to the create function. +*/ +extern void IoTHubRegistryManager_Destroy(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle); + +/** +* @brief Creates a device on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceCreate IOTHUB_REGISTRY_DEVICE_CREATE_EX structure containing +* the new device Id, primaryKey (optional) and secondaryKey (optional) +* @param device Input parameter, if it is not NULL will contain the created device info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_DEVICE_CREATE_EX* deviceCreate, IOTHUB_DEVICE_EX* device); + +/** +* @brief Gets device info for a given device. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The Id of the requested device. +* @param device Input parameter, if it is not NULL will contain the requested device info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, IOTHUB_DEVICE_EX* device); + +/** +* @brief Updates a device on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceUpdate IOTHUB_REGISTRY_DEVICE_UPDATE_EX structure containing +* the new device Id, primaryKey (optional), secondaryKey (optional), +* authentication method, and status +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_DEVICE_UPDATE_EX* deviceUpdate); + +/** +* @brief Deletes a given device. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The Id of the device to delete. +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_DeleteDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId); + +/** +* @brief Gets the registry statistic info. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param registryStatistics Input parameter, if it is not NULL will contain the requested registry info. +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetStatistics(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_STATISTICS* registryStatistics); + +/** +* @brief Creates a module on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param moduleCreate IOTHUB_REGISTRY_MODULE_CREATE structure containing +* the existing deviceID, new module Id, primaryKey (optional) and secondaryKey (optional) +* @param module Input parameter, if it is not NULL will contain the created module info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_MODULE_CREATE* moduleCreate, IOTHUB_MODULE* module); + +/** +* @brief Gets module info for a given module. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The Id of the requested device. +* @param moduleId The Id of the requested module. +* @param module Input parameter, if it is not NULL will contain the requested module info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, const char* moduleId, IOTHUB_MODULE* module); + +/** +* @brief Updates a module on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param moduleUpdate IOTHUB_REGISTRY_MODULE_UPDATE structure containing +* the new module Id, primaryKey (optional), secondaryKey (optional), +* authentication method, and status +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_MODULE_UPDATE* moduleUpdate); + +/** +* @brief Deletes a given module. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The Id of the device containing module to delete. +* @param moduleId The Id of the module to delete. +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_DeleteModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, const char* moduleId); + +/** +* @brief Gets a list of modules registered on the specified device. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The device to get a list of modules from +* @param moduleList The linked list structure to hold the returned modules +* @param module_version The version of the module structure to return +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetModuleList(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, SINGLYLINKEDLIST_HANDLE moduleList, int module_version); + + +/* DEPRECATED: THE FOLLOWING APIS ARE DEPRECATED, AND ARE ONLY BEING KEPT FOR BACK COMPAT. PLEASE USE _EX EQUIVALENT ABOVE */ +/* DEPRECATED: THE FOLLOWING APIS ARE DEPRECATED, AND ARE ONLY BEING KEPT FOR BACK COMPAT. PLEASE USE _EX EQUIVALENT ABOVE */ +/* DEPRECATED: THE FOLLOWING APIS ARE DEPRECATED, AND ARE ONLY BEING KEPT FOR BACK COMPAT. PLEASE USE _EX EQUIVALENT ABOVE */ + +/* Please use IOTHUB_DEVICE_EX instead */ +typedef struct IOTHUB_DEVICE_TAG +{ + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + const char* generationId; + const char* eTag; + IOTHUB_DEVICE_CONNECTION_STATE connectionState; + const char* connectionStateUpdatedTime; + IOTHUB_DEVICE_STATUS status; + const char* statusReason; + const char* statusUpdatedTime; + const char* lastActivityTime; + size_t cloudToDeviceMessageCount; + + bool isManaged; + const char* configuration; + const char* deviceProperties; + const char* serviceProperties; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; +} IOTHUB_DEVICE; + +/* Please use IOTHUB_REGISTRY_DEVICE_CREATE_EX instead */ +typedef struct IOTHUB_REGISTRY_DEVICE_CREATE_TAG +{ + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; +} IOTHUB_REGISTRY_DEVICE_CREATE; + +/* Please use IOTHUB_REGISTRY_DEVICE_UPDATED_EX instead */ +typedef struct IOTHUB_REGISTRY_DEVICE_UPDATE_TAG +{ + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + IOTHUB_DEVICE_STATUS status; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; +} IOTHUB_REGISTRY_DEVICE_UPDATE; + +/** DEPRECATED:: Use IoTHubRegistryManager_CreateDevice_Ex instead +* @brief Creates a device on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceCreate IOTHUB_REGISTRY_DEVICE_CREATE structure containing +* the new device Id, primaryKey (optional) and secondaryKey (optional) +* @param device Input parameter, if it is not NULL will contain the created device info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_DEVICE_CREATE* deviceCreate, IOTHUB_DEVICE* device); + +/** DEPRECATED:: Use IoTHubRegistryManager_GetDevice_Ex instead +* @brief Gets device info for a given device. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceId The Id of the requested device. +* @param device Input parameter, if it is not NULL will contain the requested device info structure +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, IOTHUB_DEVICE* device); + +/** DEPRECATED:: Use IoTHubRegistryManager_UpdateDevice_Ex instead +* @brief Updates a device on IoT Hub. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param deviceUpdate IOTHUB_REGISTRY_DEVICE_UPDATE structure containing +* the new device Id, primaryKey (optional), secondaryKey (optional), +* authentication method, and status +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_DEVICE_UPDATE* deviceUpdate); + +/* *DEPRECATED:: IoTHubRegistryManager_GetDeviceList is deprecated and may be removed from a future release. +* @brief Gets device a list of devices registered on the IoTHUb. +* +* @param registryManagerHandle The handle created by a call to the create function. +* @param numberOfDevices Number of devices requested. +* @param deviceList Input parameter, if it is not NULL will contain the requested list of devices. +* +* @return IOTHUB_REGISTRYMANAGER_RESULT_OK upon success or an error code upon failure. +*/ +extern IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDeviceList(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, size_t numberOfDevices, SINGLYLINKEDLIST_HANDLE deviceList); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_REGISTRYMANAGER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_sc_version.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_sc_version.h +* @brief Functions for managing the service client SDK version +*/ + +#ifndef IOTHUB_SC_VERSION_H +#define IOTHUB_SC_VERSION_H + +#define IOTHUB_SERVICE_CLIENT_TYPE_PREFIX "iothubserviceclient" +#define IOTHUB_SERVICE_CLIENT_BACKSLASH "/" + +#define IOTHUB_SERVICE_CLIENT_VERSION "1.1.0" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Returns a pointer to a null terminated string containing the + * current IoT Hub Service Client SDK version. + * + * @return Pointer to a null terminated string containing the + * current IoT Hub Service Client SDK version. + */ + MOCKABLE_FUNCTION(, const char*, IoTHubServiceClient_GetVersionString); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_SC_VERSION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/inc/iothub_service_client_auth.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file iothub_service_client_auth.h +* @brief APIs that allow a user (usually a device) to create and destroy +* a handle for use in Service client APIs. +* +* @details APIs that allow a user (usually a device) to create +* a handle for use in Service client APIs. +* The create API parses the given connection string and +* saves the IoTHub specific authentication info in the handle. +* The destory API deallocate all the resources allocated in the handle. +* +* User will store the handle and use it as input parameter in +* consequent APIs. When the handle is not needed anymore user +* responsbility to call destory to free all the resources. +*/ + +#ifndef IOTHUB_SERVICE_CLIENT_AUTH_H +#define IOTHUB_SERVICE_CLIENT_AUTH_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#define IOTHUB_DEVICE_STATUS_VALUES \ + IOTHUB_DEVICE_STATUS_ENABLED, \ + IOTHUB_DEVICE_STATUS_DISABLED \ + +DEFINE_ENUM(IOTHUB_DEVICE_STATUS, IOTHUB_DEVICE_STATUS_VALUES); + +#define IOTHUB_DEVICE_CONNECTION_STATE_VALUES \ + IOTHUB_DEVICE_CONNECTION_STATE_CONNECTED, \ + IOTHUB_DEVICE_CONNECTION_STATE_DISCONNECTED \ + +DEFINE_ENUM(IOTHUB_DEVICE_CONNECTION_STATE, IOTHUB_DEVICE_CONNECTION_STATE_VALUES); + +#ifdef __cplusplus +extern "C" +{ +#else +#endif + +#include "azure_c_shared_utility/macro_utils.h" + +/** @brief Structure to store IoTHub authentication information +*/ +typedef struct IOTHUB_SERVICE_CLIENT_AUTH_TAG +{ + char* hostname; + char* iothubName; + char* iothubSuffix; + char* sharedAccessKey; + char* keyName; + char* deviceId; +} IOTHUB_SERVICE_CLIENT_AUTH; + +/** @brief Handle to hide struct and use it in consequent APIs +*/ +typedef struct IOTHUB_SERVICE_CLIENT_AUTH_TAG* IOTHUB_SERVICE_CLIENT_AUTH_HANDLE; + +/** +* @brief Creates a IoT Hub service client handle for use it +* in consequent APIs. +* +* @param connectionString Pointer to a character string +* +* Sample connection string: +* <blockquote> +* <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];SharedAccessKeyName=[Shared Access Key Name goes here];SharedAccessKey=[Shared Access key goes here];</pre> +* </blockquote> +* +* @return A non-NULL @c IOTHUB_SERVICE_CLIENT_AUTH_HANDLE value that is used when +* invoking other functions for IoT Hub Service Client and @c NULL on failure. +*/ +extern IOTHUB_SERVICE_CLIENT_AUTH_HANDLE IoTHubServiceClientAuth_CreateFromConnectionString(const char* connectionString); + +/** +* @brief Disposes of resources allocated by the IoT Hub Service Client. +* +* @param serviceClientHandle The handle created by a call to the create function. +*/ +extern void IoTHubServiceClientAuth_Destroy(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle); + +#ifdef __cplusplus +} +#endif + +#endif // IOTHUB_SERVICE_CLIENT_AUTH_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_deviceconfiguration.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1795 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/connection_string_parser.h" + +#include "parson.h" +#include "iothub_deviceconfiguration.h" +#include "iothub_sc_version.h" + +DEFINE_ENUM_STRINGS(IOTHUB_DEVICE_CONFIGURATION_RESULT, IOTHUB_DEVICE_CONFIGURATION_RESULT_VALUES); + +#define UID_LENGTH 37 +#define IOTHUB_DEVICE_CONFIGURATIONS_MAX_REQUEST 20 +static const char* HTTP_HEADER_KEY_AUTHORIZATION = "Authorization"; +static const char* HTTP_HEADER_VAL_AUTHORIZATION = " "; +static const char* HTTP_HEADER_KEY_REQUEST_ID = "Request-Id"; +static const char* HTTP_HEADER_KEY_USER_AGENT = "User-Agent"; +static const char* HTTP_HEADER_VAL_USER_AGENT = IOTHUB_SERVICE_CLIENT_TYPE_PREFIX IOTHUB_SERVICE_CLIENT_BACKSLASH IOTHUB_SERVICE_CLIENT_VERSION; +static const char* HTTP_HEADER_KEY_ACCEPT = "Accept"; +static const char* HTTP_HEADER_VAL_ACCEPT = "application/json"; +static const char* HTTP_HEADER_KEY_CONTENT_TYPE = "Content-Type"; +static const char* HTTP_HEADER_VAL_CONTENT_TYPE = "application/json; charset=utf-8"; +static const char* HTTP_HEADER_KEY_IFMATCH = "If-Match"; +static const char* HTTP_HEADER_VAL_IFMATCH = "*"; + +#define CONFIGURATION_JSON_KEY_CONTENT "content" +#define CONFIGURATION_JSON_KEY_DEVICE_CONTENT "deviceContent" +#define CONFIGURATION_JSON_KEY_MODULES_CONTENT "modulesContent" +#define CONFIGURATION_JSON_KEY_SYSTEM_METRICS "systemMetrics" +#define CONFIGURATION_JSON_KEY_CUSTOM_METRICS "metrics" +#define CONFIGURATION_JSON_KEY_METRICS_RESULTS "results" +#define CONFIGURATION_JSON_KEY_METRICS_QUERIES "queries" +#define CONFIGURATION_JSON_DOT_SEPARATOR "." + +static const char* CONFIGURATION_JSON_KEY_CONFIGURATION_ID = "id"; +static const char* CONFIGURATION_JSON_KEY_SCHEMA_VERSION = "schemaVersion"; +static const char* CONFIGURATION_JSON_KEY_TARGET_CONDITION = "targetCondition"; +static const char* CONFIGURATION_JSON_KEY_CREATED_TIME = "createdTimeUtc"; +static const char* CONFIGURATION_JSON_KEY_LAST_UPDATED_TIME = "lastUpdatedTimeUtc"; +static const char* CONFIGURATION_JSON_KEY_PRIORITY = "priority"; +static const char* CONFIGURATION_JSON_KEY_ETAG = "etag"; +static const char* CONFIGURATION_JSON_KEY_LABELS = "labels"; + +static const char* CONFIGURATION_DEVICE_CONTENT_NODE_NAME = CONFIGURATION_JSON_KEY_CONTENT CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_DEVICE_CONTENT; +static const char* CONFIGURATION_MODULES_CONTENT_NODE_NAME = CONFIGURATION_JSON_KEY_CONTENT CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_MODULES_CONTENT; +static const char* CONFIGURATION_SYSTEM_METRICS_RESULTS_NODE_NAME = CONFIGURATION_JSON_KEY_SYSTEM_METRICS CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_METRICS_RESULTS; +static const char* CONFIGURATION_SYSTEM_METRICS_QUERIES_NODE_NAME = CONFIGURATION_JSON_KEY_SYSTEM_METRICS CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_METRICS_QUERIES; +static const char* CONFIGURATION_CUSTOM_METRICS_RESULTS_NODE_NAME = CONFIGURATION_JSON_KEY_CUSTOM_METRICS CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_METRICS_RESULTS; +static const char* CONFIGURATION_CUSTOM_METRICS_QUERIES_NODE_NAME = CONFIGURATION_JSON_KEY_CUSTOM_METRICS CONFIGURATION_JSON_DOT_SEPARATOR CONFIGURATION_JSON_KEY_METRICS_QUERIES; + +static const char* const URL_API_VERSION = "api-version=2018-03-01-preview"; + +static const char* const RELATIVE_PATH_FMT_DEVICECONFIGURATION = "/configurations/%s?%s"; +static const char* const RELATIVE_PATH_FMT_DEVICECONFIGURATIONS = "/configurations/?top=%d&%s"; +static const char* const RELATIVE_PATH_FMT_APPLYCONFIGURATIONCONTENT = "/devices/%s/applyConfigurationContent?%s"; + +static const char* const CONFIGURATION_DEFAULT_SCHEMA_VERSION = "1.0"; +static const char* const CONFIGURATION_DEFAULT_ETAG = "MQ=="; + +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_TAG +{ + char* hostname; + char* sharedAccessKey; + char* keyName; +} IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION; + +static const char* generateGuid(void) +{ + char* result; + + if ((result = malloc(UID_LENGTH)) != NULL) + { + result[0] = '\0'; + if (UniqueId_Generate(result, UID_LENGTH) != UNIQUEID_OK) + { + LogError("UniqueId_Generate failed"); + free((void*)result); + result = NULL; + } + } + return (const char*)result; +} + +static STRING_HANDLE createRelativePath(IOTHUB_DEVICECONFIGURATION_REQUEST_MODE iotHubDeviceConfigurationRequestMode, const char* id, size_t numberOfConfigurations) +{ + //IOTHUB_DEVICECONFIGURATION_REQUEST_GET GET {iot hub}/configurations/{configuration id} // Get single device configuration + //IOTHUB_DEVICECONFIGURATION_REQUEST_ADD PUT {iot hub}/configurations/{configuration id} // Add device configuration + //IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE PUT {iot hub}/configurations/{configuration id} // Update device configuration + //IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE DELETE {iot hub}/configurations/{configuration id} // Delete device configuration + //IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST GET {iot hub}/configurations // Get multiple configurations + //IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT POST {iot hub}/devices/{deviceOrModule id}/applyConfigurationContent // Apply device configuration to device or module + + STRING_HANDLE result; + + if (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST) + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_DEVICECONFIGURATIONS, numberOfConfigurations, URL_API_VERSION); + } + else if ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_ADD) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE)) + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_DEVICECONFIGURATION, id, URL_API_VERSION); + } + else if (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT) + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_APPLYCONFIGURATIONCONTENT, id, URL_API_VERSION); + } + else + { + result = NULL; + } + return result; +} + +static HTTP_HEADERS_HANDLE createHttpHeader(IOTHUB_DEVICECONFIGURATION_REQUEST_MODE iotHubDeviceConfigurationRequestMode) +{ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_020: [ IoTHubDeviceConfiguration_GetConfiguration shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + HTTP_HEADERS_HANDLE httpHeader; + const char* guid = NULL; + + if ((httpHeader = HTTPHeaders_Alloc()) == NULL) + { + LogError("HTTPHeaders_Alloc failed"); + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_AUTHORIZATION, HTTP_HEADER_VAL_AUTHORIZATION) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Authorization header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if ((guid = generateGuid()) == NULL) + { + LogError("GUID creation failed"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_REQUEST_ID, guid) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for RequestId header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_USER_AGENT, HTTP_HEADER_VAL_USER_AGENT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for User-Agent header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_ACCEPT, HTTP_HEADER_VAL_ACCEPT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Accept header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_CONTENT_TYPE, HTTP_HEADER_VAL_CONTENT_TYPE) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Content-Type header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE)) + { + if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_IFMATCH, HTTP_HEADER_VAL_IFMATCH) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for If-Match header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + } + free((void*)guid); + + return httpHeader; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT sendHttpRequestDeviceConfiguration(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_MODE iotHubDeviceConfigurationRequestMode, const char* id, BUFFER_HANDLE json, size_t maxConfigurationsCount, BUFFER_HANDLE responseBuffer) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + STRING_HANDLE uriResource = NULL; + STRING_HANDLE accessKey = NULL; + STRING_HANDLE keyName = NULL; + HTTPAPIEX_SAS_HANDLE httpExApiSasHandle; + HTTPAPIEX_HANDLE httpExApiHandle; + HTTP_HEADERS_HANDLE httpHeader; + + if ((uriResource = STRING_construct(serviceClientDeviceConfigurationHandle->hostname)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("STRING_construct failed for uriResource"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((accessKey = STRING_construct(serviceClientDeviceConfigurationHandle->sharedAccessKey)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("STRING_construct failed for accessKey"); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((keyName = STRING_construct(serviceClientDeviceConfigurationHandle->keyName)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("STRING_construct failed for keyName"); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_020: [ IoTHubDeviceConfiguration_GetConfiguration shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + else if ((httpHeader = createHttpHeader(iotHubDeviceConfigurationRequestMode)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("HttpHeader creation failed"); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_021: [ IoTHubDeviceConfiguration_GetConfiguration shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ]*/ + else if ((httpExApiSasHandle = HTTPAPIEX_SAS_Create(accessKey, uriResource, keyName)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_025: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_GetConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR ]*/ + LogError("HTTPAPIEX_SAS_Create failed"); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_022: [ IoTHubDeviceConfiguration_GetConfiguration shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ]*/ + else if ((httpExApiHandle = HTTPAPIEX_Create(serviceClientDeviceConfigurationHandle->hostname)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_025: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("HTTPAPIEX_Create failed"); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR; + } + else + { + HTTPAPI_REQUEST_TYPE httpApiRequestType = HTTPAPI_REQUEST_GET; + STRING_HANDLE relativePath; + unsigned int statusCode = 0; + unsigned char is_error = 0; + + if ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_ADD) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE)) + { + httpApiRequestType = HTTPAPI_REQUEST_PUT; + } + else if ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET) || (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST)) + { + httpApiRequestType = HTTPAPI_REQUEST_GET; + } + else if (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE) + { + httpApiRequestType = HTTPAPI_REQUEST_DELETE; + } + else if (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT) + { + httpApiRequestType = HTTPAPI_REQUEST_POST; + } + else + { + is_error = 1; + } + + if (is_error) + { + LogError("Invalid request type"); + result = IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_019: [ IoTHubDeviceConfiguration_GetConfiguration shall create HTTP GET request URL using the given configurationId using the following format: url/configurations/[configurationId] ]*/ + if ((relativePath = createRelativePath(iotHubDeviceConfigurationRequestMode, id, maxConfigurationsCount)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("Failure creating relative path"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ IoTHubDeviceConfiguration_GetConfiguration shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ]*/ + else if (HTTPAPIEX_SAS_ExecuteRequest(httpExApiSasHandle, httpExApiHandle, httpApiRequestType, STRING_c_str(relativePath), httpHeader, json, &statusCode, NULL, responseBuffer) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_025: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_GetConfiguration shall fail and return NULL ]*/ + LogError("HTTPAPIEX_SAS_ExecuteRequest failed"); + STRING_delete(relativePath); + result = IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR; + } + else + { + STRING_delete(relativePath); + if ((((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_ADD) || + (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET) || + (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST) || + (iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE)) && (statusCode == 200)) || + ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE) && (statusCode == 204)) || + ((iotHubDeviceConfigurationRequestMode == IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT) && ((statusCode == 200) || (statusCode == 204))) + ) + { + /*CodesSRS_IOTHUBDEVICECONFIGURATION_38_030: [ Otherwise IoTHubDeviceConfiguration_GetConfiguration shall save the received deviceConfiguration to the out parameter and return with it ]*/ + result = IOTHUB_DEVICE_CONFIGURATION_OK; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_026: [ IoTHubDeviceConfiguration_GetConfiguration shall verify the received HTTP status code and if it is not equal to 200 then return NULL ]*/ + LogError("Http Failure status code %d.", statusCode); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + HTTPAPIEX_Destroy(httpExApiHandle); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + } + return result; +} + +static JSON_Value* createConfigurationContentPayload(const IOTHUB_DEVICE_CONFIGURATION_CONTENT* configurationContent) +{ + JSON_Value* result = NULL; + JSON_Object* root_object = NULL; + + if (configurationContent == NULL) + { + LogError("configuration cannot be null"); + result = NULL; + } + else if (((configurationContent->deviceContent == NULL) && (configurationContent->modulesContent == NULL)) || + ((configurationContent->deviceContent != NULL && strlen(configurationContent->deviceContent) == 0) && (configurationContent->modulesContent != NULL && strlen(configurationContent->modulesContent) == 0))) + { + LogError("both deviceContent and modulesContent cannot be NULL or empty"); + result = NULL; + } + if ((result = json_value_init_object()) == NULL) + { + LogError("json_value_init_object failed"); + result = NULL; + } + else if ((root_object = json_value_get_object(result)) == NULL) + { + LogError("json_value_get_object failed"); + result = NULL; + } + else if ((configurationContent->deviceContent != NULL) && (strlen(configurationContent->deviceContent) > 0) && (json_object_set_value(root_object, CONFIGURATION_JSON_KEY_DEVICE_CONTENT, json_parse_string(configurationContent->deviceContent))) != JSONSuccess) + { + LogError("json_object_set_string failed for deviceContent"); + result = NULL; + } + else if ((configurationContent->modulesContent != NULL) && (strlen(configurationContent->modulesContent) > 0) && (json_object_set_value(root_object, CONFIGURATION_JSON_KEY_MODULES_CONTENT, json_parse_string(configurationContent->modulesContent))) != JSONSuccess) + { + LogError("json_object_set_string failed for modulesContent"); + result = NULL; + } + + return result; +} + +static JSON_Value* createConfigurationMetricsQueriesPayload(const IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION* configurationMetricsDefinition) +{ + JSON_Value* result = NULL; + JSON_Value* new_value = NULL; + JSON_Object* root_object = NULL; + JSON_Object* new_value_object = NULL; + + if (configurationMetricsDefinition == NULL) + { + LogError("configuration cannot be null"); + result = NULL; + } + else if ((result = json_value_init_object()) == NULL || (new_value = json_value_init_object()) == NULL) + { + LogError("json_value_init_object failed"); + result = NULL; + } + else if ((root_object = json_value_get_object(result)) == NULL || (new_value_object = json_value_get_object(new_value)) == NULL) + { + LogError("json_value_get_object failed"); + result = NULL; + } + else if (json_object_set_value(root_object, CONFIGURATION_JSON_KEY_METRICS_QUERIES, new_value) != JSONSuccess) + { + LogError("json_object_set_value failed for deviceContent"); + result = NULL; + } + else + { + for (size_t i = 0; i < configurationMetricsDefinition->numQueries; i++) + { + if (json_object_set_string(new_value_object, (const char*)configurationMetricsDefinition->queryNames[i], configurationMetricsDefinition->queryStrings[i]) != JSONSuccess) + { + LogError("json_object_set_string failed"); + result = NULL; + break; + } + } + } + + return result; +} + +static JSON_Value* createConfigurationLabelsPayload(const IOTHUB_DEVICE_CONFIGURATION_LABELS* configurationLabels) +{ + JSON_Value* result = NULL; + JSON_Object* root_object = NULL; + + if (configurationLabels == NULL) + { + LogError("configurationLabels cannot be null"); + result = NULL; + } + else if (configurationLabels->numLabels == 0) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_010: [ IoTHubDeviceConfiguration_AddConfiguration shall return NULL for labels, when device configuration label list is empty ] */ + result = NULL; + } + else if ((result = json_value_init_object()) == NULL) + { + LogError("json_value_init_object failed"); + result = NULL; + } + else if ((root_object = json_value_get_object(result)) == NULL) + { + LogError("json_value_get_object failed"); + result = NULL; + } + else + { + for (size_t i = 0; i < configurationLabels->numLabels; i++) + { + if (json_object_set_string(root_object, (const char*)configurationLabels->labelNames[i], configurationLabels->labelValues[i]) != JSONSuccess) + { + LogError("json_object_set_value failed"); + result = NULL; + break; + } + } + } + + return result; +} + +static BUFFER_HANDLE createConfigurationPayloadJson(const IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + BUFFER_HANDLE result; + + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + JSON_Value* configurationContentJson; + JSON_Value* configurationLabelsJson; + JSON_Value* metricsQueriesJson; + JSON_Value* systemMetricsQueriesJson; + + if (configuration == NULL) + { + LogError("configuration cannot be null"); + result = NULL; + } + else if (configuration->configurationId == NULL) + { + LogError("Configuration id cannot be NULL"); + result = NULL; + } + else if (((configuration->content).deviceContent == NULL) && (((configuration->content).modulesContent) == NULL)) + { + LogError("deviceContent and modulesContent both cannot be NULL"); + result = NULL; + } + else if ((root_value = json_value_init_object()) == NULL) + { + LogError("json_value_init_object failed"); + result = NULL; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + LogError("json_value_get_object failed"); + result = NULL; + } + else if ((json_object_set_string(root_object, CONFIGURATION_JSON_KEY_CONFIGURATION_ID, configuration->configurationId)) != JSONSuccess) + { + LogError("json_object_set_string failed for configurationId"); + result = NULL; + } + else if ((json_object_set_string(root_object, CONFIGURATION_JSON_KEY_SCHEMA_VERSION, configuration->schemaVersion)) != JSONSuccess) + { + LogError("json_object_set_string failed for schemaVersion"); + result = NULL; + } + else if ((json_object_set_string(root_object, CONFIGURATION_JSON_KEY_TARGET_CONDITION, configuration->targetCondition)) != JSONSuccess) + { + LogError("json_object_set_string failed for targetCondition"); + result = NULL; + } + else if ((json_object_set_number(root_object, CONFIGURATION_JSON_KEY_PRIORITY, configuration->priority)) != JSONSuccess) + { + LogError("json_object_set_string failed for priority"); + result = NULL; + } + else if ((json_object_set_string(root_object, CONFIGURATION_JSON_KEY_ETAG, configuration->eTag)) != JSONSuccess) + { + LogError("json_object_set_string failed for eTag"); + result = NULL; + } + else if (((configuration->createdTimeUtc != NULL) && json_object_set_string(root_object, CONFIGURATION_JSON_KEY_CREATED_TIME, configuration->createdTimeUtc)) != JSONSuccess) + { + LogError("json_object_set_string failed for createdTimeUtc"); + result = NULL; + } + else if (((configuration->lastUpdatedTimeUtc != NULL) && json_object_set_string(root_object, CONFIGURATION_JSON_KEY_CREATED_TIME, configuration->lastUpdatedTimeUtc)) != JSONSuccess) + { + LogError("json_object_set_string failed for lastUpdatedTimeUtc"); + result = NULL; + } + else if (((configurationLabelsJson = createConfigurationLabelsPayload(&configuration->labels)) != NULL) && ((json_object_set_value(root_object, CONFIGURATION_JSON_KEY_LABELS, configurationLabelsJson)) != JSONSuccess)) + { + LogError("json_object_set_string failed for configurationLabelsJson"); + result = NULL; + } + else if ((((configurationContentJson = createConfigurationContentPayload(&configuration->content)) != NULL) && ((json_object_set_value(root_object, CONFIGURATION_JSON_KEY_CONTENT, configurationContentJson)) != JSONSuccess))) + { + LogError("json_object_set_string failed for configurationContentJson"); + result = NULL; + } + else if (((metricsQueriesJson = createConfigurationMetricsQueriesPayload(&configuration->metricsDefinition)) != NULL) && ((json_object_set_value(root_object, CONFIGURATION_JSON_KEY_CUSTOM_METRICS, metricsQueriesJson)) != JSONSuccess)) + { + LogError("json_object_set_string failed for metricsQueriesJson"); + result = NULL; + } + else if (((systemMetricsQueriesJson = createConfigurationMetricsQueriesPayload(&configuration->systemMetricsDefinition)) != NULL) && ((json_object_set_value(root_object, CONFIGURATION_JSON_KEY_SYSTEM_METRICS, systemMetricsQueriesJson)) != JSONSuccess)) + { + LogError("json_object_set_string failed for systemMetricsQueriesJson"); + result = NULL; + } + else + { + char* serialized_string; + if ((serialized_string = json_serialize_to_string(root_value)) == NULL) + { + LogError("json_serialize_to_string failed"); + result = NULL; + } + else + { + if ((result = BUFFER_create((const unsigned char*)serialized_string, strlen(serialized_string))) == NULL) + { + LogError("Buffer_Create failed"); + result = NULL; + } + json_free_serialized_string(serialized_string); + } + } + + json_object_clear(root_object); + + if (root_value != NULL) + json_value_free(root_value); + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT parseDeviceConfigurationLabelsJsonObject(const JSON_Object* labelsJson, IOTHUB_DEVICE_CONFIGURATION_LABELS* labels) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + STRING_HANDLE tempLabelsName = NULL; + STRING_HANDLE tempLabelsValue = NULL; + + if (labels == NULL) + { + LogError("labels cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else if (labelsJson == NULL) + { + labels->numLabels = 0; + result = IOTHUB_DEVICE_CONFIGURATION_OK; + } + else + { + result = IOTHUB_DEVICE_CONFIGURATION_OK; + + size_t labelsCount = json_object_get_count(labelsJson); + + labels->numLabels = labelsCount; + if (labelsCount > 0) + { + if ((labels->labelNames = malloc(sizeof(const char*) * labelsCount)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT labelNames"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else if ((labels->labelValues = malloc(sizeof(const char*) * labelsCount)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT results"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else + { + for (size_t i = 0; i < labelsCount; i++) + { + if ((tempLabelsName = STRING_construct(json_object_get_name(labelsJson, i))) == NULL) + { + LogError("STRING_construct failed for tempLabelsName"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (!json_object_has_value(labelsJson, STRING_c_str(tempLabelsName))) + { + LogError("missing result for label %s", tempLabelsName); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(labels->labelNames[i]), STRING_c_str(tempLabelsName)) != 0) + { + LogError("mallocAndStrcpy_s failed for results tempLabelsName"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((tempLabelsValue = STRING_construct(json_value_get_string(json_object_get_value_at(labelsJson, i)))) == NULL) + { + LogError("STRING_construct failed for tempMetricQueryString"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(labels->labelValues[i]), STRING_c_str(tempLabelsValue)) != 0) + { + LogError("mallocAndStrcpy_s failed for tempMetricQueryString"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + } + } + + STRING_delete(tempLabelsName); + STRING_delete(tempLabelsValue); + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT parseDeviceConfigurationMetricsJsonObject(const JSON_Object* metricResults, const JSON_Object* metricQueries, IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT* results, IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION* queries) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + size_t metricResultsCount = json_object_get_count(metricResults); + size_t metricQueriesCount = json_object_get_count(metricQueries); + const char* tempMetricQueryName = NULL; + const char* tempMetricQueryString = NULL; + + if (metricResults == NULL || metricQueries == NULL || results == NULL || queries == NULL) + { + LogError("metricResults, metricQueries, results or queries cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + result = IOTHUB_DEVICE_CONFIGURATION_OK; + + results->numQueries = metricResultsCount; + if (metricResultsCount > 0) + { + if ((results->queryNames = calloc(metricResultsCount, sizeof(const char*))) == NULL) + { + LogError("Calloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((results->results = calloc(metricResultsCount, sizeof(double))) == NULL) + { + LogError("Calloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT results"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + for (size_t i = 0; i < metricResultsCount; i++) + { + if ((tempMetricQueryName = json_object_get_name(metricResults, i)) == NULL) + { + LogError("STRING_construct failed for tempMetricQueryName"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(results->queryNames[i]), tempMetricQueryName) != 0) + { + LogError("mallocAndStrcpy_s failed for queries tempMetricQueryName"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + results->results[i] = json_object_dotget_number(metricResults, tempMetricQueryName); + } + } + } + } + + queries->numQueries = metricQueriesCount; + if (metricQueriesCount > 0) + { + if ((queries->queryNames = calloc(metricQueriesCount, sizeof(const char*))) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else if ((queries->queryStrings = calloc(metricQueriesCount, sizeof(const char*))) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryStrings"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + for (size_t i = 0; i < metricQueriesCount; i++) + { + if ((tempMetricQueryName = json_object_get_name(metricQueries, i)) == NULL) + { + LogError("STRING_construct failed for tempMetricQueryName"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(queries->queryNames[i]), tempMetricQueryName) != 0) + { + LogError("mallocAndStrcpy_s failed for queries tempMetricQueryName"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((tempMetricQueryString = json_value_get_string(json_object_get_value_at(metricQueries, i))) == NULL) + { + LogError("STRING_construct failed for tempMetricQueryString"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(queries->queryStrings[i]), tempMetricQueryString) != 0) + { + LogError("mallocAndStrcpy_s failed for tempMetricQueryString"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + } + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT parseDeviceConfigurationJsonObject(JSON_Object* root_object, IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + const char* configurationId = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_CONFIGURATION_ID); + const char* schemaVersion = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_SCHEMA_VERSION); + const char* deviceContent = json_serialize_to_string(json_object_dotget_value(root_object, CONFIGURATION_DEVICE_CONTENT_NODE_NAME)); + const char* modulesContent = json_serialize_to_string(json_object_dotget_value(root_object, CONFIGURATION_MODULES_CONTENT_NODE_NAME)); + const char* targetCondition = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_TARGET_CONDITION); + const char* createdTime = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_CREATED_TIME); + const char* lastUpdatedTime = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_LAST_UPDATED_TIME); + const char* priority = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_PRIORITY); + const char* eTag = json_object_get_string(root_object, CONFIGURATION_JSON_KEY_ETAG); + + JSON_Object* systemMetricsResults = json_object_dotget_object(root_object, CONFIGURATION_SYSTEM_METRICS_RESULTS_NODE_NAME); + JSON_Object* systemMetricsQueries = json_object_dotget_object(root_object, CONFIGURATION_SYSTEM_METRICS_QUERIES_NODE_NAME); + JSON_Object* customMetricsResults = json_object_dotget_object(root_object, CONFIGURATION_CUSTOM_METRICS_RESULTS_NODE_NAME); + JSON_Object* customMetricsQueries = json_object_dotget_object(root_object, CONFIGURATION_CUSTOM_METRICS_QUERIES_NODE_NAME); + + JSON_Object* labels = json_object_dotget_object(root_object, CONFIGURATION_JSON_KEY_LABELS); + + if ((configurationId != NULL) && (mallocAndStrcpy_s((char**)&(configuration->configurationId), configurationId) != 0)) + { + LogError("mallocAndStrcpy_s failed for configurationId"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((schemaVersion != NULL) && (mallocAndStrcpy_s((char**)&(configuration->schemaVersion), schemaVersion) != 0)) + { + LogError("mallocAndStrcpy_s failed for schemaVersion"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((deviceContent != NULL) && (mallocAndStrcpy_s((char**)&configuration->content.deviceContent, deviceContent) != 0)) + { + LogError("mallocAndStrcpy_s failed for content.deviceContent"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((modulesContent != NULL) && (mallocAndStrcpy_s((char**)&configuration->content.modulesContent, modulesContent) != 0)) + { + LogError("mallocAndStrcpy_s failed for content.modulesContent"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((eTag != NULL) && (mallocAndStrcpy_s((char**)&configuration->eTag, eTag) != 0)) + { + LogError("mallocAndStrcpy_s failed for eTag"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((targetCondition != NULL) && (mallocAndStrcpy_s((char**)&configuration->targetCondition, targetCondition) != 0)) + { + LogError("mallocAndStrcpy_s failed for targetCondition"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((createdTime != NULL) && (mallocAndStrcpy_s((char**)&configuration->createdTimeUtc, createdTime) != 0)) + { + LogError("mallocAndStrcpy_s failed for createdTimeUtc"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((lastUpdatedTime != NULL) && (mallocAndStrcpy_s((char**)&configuration->lastUpdatedTimeUtc, lastUpdatedTime) != 0)) + { + LogError("mallocAndStrcpy_s failed for lastUpdatedTimeUtc"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + if (priority != NULL) + { + configuration->priority = (int)json_object_get_number(root_object, priority); + } + + if ((result = parseDeviceConfigurationMetricsJsonObject(systemMetricsResults, systemMetricsQueries, &(configuration->systemMetricsResult), &(configuration->systemMetricsDefinition))) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("parseDeviceConfigurationMetricsJsonObject failed for systemMetrics"); + } + else if ((result = parseDeviceConfigurationMetricsJsonObject(customMetricsResults, customMetricsQueries, &(configuration->metricResult), &(configuration->metricsDefinition))) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("parseDeviceConfigurationMetricsJsonObject failed for systemMetrics"); + } + else if ((result = parseDeviceConfigurationLabelsJsonObject(labels, &(configuration->labels))) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("parseDeviceConfigurationLabelsJsonObject failed for systemMetrics"); + } + } + + return result; +} + +void IoTHubDeviceConfiguration_FreeConfigurationMembers(IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + free((void *)configuration->configurationId); + free((char *)configuration->schemaVersion); + free((char *)configuration->targetCondition); + free((char *)configuration->eTag); + free((char *)configuration->createdTimeUtc); + free((char *)configuration->lastUpdatedTimeUtc); + + if (configuration->content.deviceContent != NULL) free((char *)configuration->content.deviceContent); + if (configuration->content.modulesContent != NULL) free((char *)configuration->content.modulesContent); + + if (configuration->labels.numLabels > 0) + { + for (size_t i = 0; i < configuration->labels.numLabels; i++) + { + free((void *)(configuration->labels.labelNames[i])); + free((void *)(configuration->labels.labelValues[i])); + } + + free((void *)configuration->labels.labelNames); + free((void *)configuration->labels.labelValues); + } + + if (configuration->metricsDefinition.numQueries > 0) + { + for (size_t i = 0; i < configuration->metricsDefinition.numQueries; i++) + { + free((void *)(configuration->metricsDefinition.queryNames[i])); + free((void *)(configuration->metricsDefinition.queryStrings[i])); + } + + free((void *)configuration->metricsDefinition.queryNames); + free((void *)configuration->metricsDefinition.queryStrings); + } + + + if (configuration->metricResult.numQueries > 0) + { + for (size_t i = 0; i < configuration->metricResult.numQueries; i++) + { + free((void *)(configuration->metricResult.queryNames[i])); + } + + free((void *)configuration->metricResult.queryNames); + free((void *)configuration->metricResult.results); + } + + + if (configuration->systemMetricsDefinition.numQueries > 0) + { + for (size_t i = 0; i < configuration->systemMetricsDefinition.numQueries; i++) + { + free((void *)(configuration->systemMetricsDefinition.queryNames[i])); + free((void *)(configuration->systemMetricsDefinition.queryStrings[i])); + } + + free((void *)configuration->systemMetricsDefinition.queryNames); + free((void *)configuration->systemMetricsDefinition.queryStrings); + } + + if (configuration->systemMetricsResult.numQueries > 0) + { + for (size_t i = 0; i < configuration->systemMetricsResult.numQueries; i++) + { + free((void *)(configuration->systemMetricsResult.queryNames[i])); + } + + free((void *)configuration->systemMetricsResult.queryNames); + free((void *)configuration->systemMetricsResult.results); + } + + memset(configuration, 0, sizeof(*configuration)); +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT parseDeviceConfigurationJson(BUFFER_HANDLE jsonBuffer, IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If the configuration out parameter is not NULL IoTHubDeviceConfiguration_AddConfiguration shall save the received configuration to the out parameter and return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_033: [ IoTHubDeviceConfiguration_GetConfiguration shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to configuration for configuration properties ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_034: [ If any of the property field above missing from the JSON the property value will not be populated ] */ + if (jsonBuffer == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("jsonBuffer cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (configuration == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("configuration cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + const char* bufferStr = NULL; + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + + if ((bufferStr = (const char*)BUFFER_u_char(jsonBuffer)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("BUFFER_u_char failed"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_036: [ If the received JSON is empty, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_CONFIGURATION_NOT_EXIST ] */ + else if (strlen(bufferStr) == 0) + { + LogError("Returned JSON cannot be empty"); + result = IOTHUB_DEVICE_CONFIGURATION_CONFIGURATION_NOT_EXIST; + } + else if ((root_value = json_parse_string(bufferStr)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + LogError("json_parse_string failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + LogError("json_value_get_object failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else + { + result = parseDeviceConfigurationJsonObject(root_object, configuration); + } + + json_object_clear(root_object); + json_value_free(root_value); + } + return result; +} + +static void free_deviceConfiguration_handle(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION* deviceConfiguration) +{ + free(deviceConfiguration->hostname); + free(deviceConfiguration->sharedAccessKey); + free(deviceConfiguration->keyName); + free(deviceConfiguration); +} + +IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE IoTHubDeviceConfiguration_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_001: [ If the serviceClientHandle input parameter is NULL IoTHubDeviceConfiguration_Create shall return NULL ]*/ + if (serviceClientHandle == NULL) + { + LogError("IotHubDeviceConfiguration_Create: serviceClientHandle is null"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_002: [ If any member of the serviceClientHandle input parameter is NULL IoTHubDeviceConfiguration_Create shall return NULL ]*/ + IOTHUB_SERVICE_CLIENT_AUTH* serviceClientAuth = (IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle; + + if (serviceClientAuth->hostname == NULL) + { + LogError("authInfo->hostName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubName == NULL) + { + LogError("authInfo->iothubName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubSuffix == NULL) + { + LogError("authInfo->iothubSuffix input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->keyName == NULL) + { + LogError("authInfo->keyName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->sharedAccessKey == NULL) + { + LogError("authInfo->sharedAccessKey input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_003: [ IoTHubDeviceMethod_Create shall allocate memory for a new IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE instance ]*/ + result = malloc(sizeof(IOTHUB_DEVICE_CONFIGURATION)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_002: [ If the allocation failed, IoTHubDeviceConfiguration_Create shall return NULL ]*/ + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION"); + } + else + { + memset(result, 0, sizeof(IOTHUB_DEVICE_CONFIGURATION)); + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_005: [ If the allocation successful, IoTHubDeviceConfiguration_Create shall create a IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE from the given IOTHUB_SERVICE_CLIENT_AUTH_HANDLE and return with it ]*/ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_006: [ IoTHubDeviceConfiguration_Create shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s. ]*/ + if (mallocAndStrcpy_s(&result->hostname, serviceClientAuth->hostname) != 0) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_007: [ If the mallocAndStrcpy_s fails, IoTHubDeviceConfiguration_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for hostName"); + free_deviceConfiguration_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_012: [ IoTHubDeviceConfiguration_Create shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, serviceClientAuth->sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_013: [ If the mallocAndStrcpy_s fails, IoTHubDeviceConfiguration_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_deviceConfiguration_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_014: [ IoTHubDeviceConfiguration_Create shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->keyName, serviceClientAuth->keyName) != 0) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_015: [ If the mallocAndStrcpy_s fails, IoTHubDeviceConfiguration_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for keyName"); + free_deviceConfiguration_handle(result); + result = NULL; + } + } + } + } + + return result; +} + +void IoTHubDeviceConfiguration_Destroy(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle) +{ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_016: [ If the serviceClientDeviceConfigurationHandle input parameter is NULL IoTHubDeviceConfiguration_Destroy shall return ]*/ + if (serviceClientDeviceConfigurationHandle != NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_17: [ If the serviceClientDeviceConfigurationHandle input parameter is not NULL IoTHubDeviceConfiguration_Destroy shall free the memory of it and return ]*/ + free_deviceConfiguration_handle((IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION*)serviceClientDeviceConfigurationHandle); + } +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT cloneNameValueArrays_labels(IOTHUB_DEVICE_CONFIGURATION_LABELS *source, IOTHUB_DEVICE_CONFIGURATION_LABELS *target) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result = IOTHUB_DEVICE_CONFIGURATION_OK; + + target->numLabels = source->numLabels; + if (target->numLabels > 0) + { + if ((target->labelNames = malloc(sizeof(const char*) * target->numLabels)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION labelNames"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else if ((target->labelValues = malloc(sizeof(const char*) * target->numLabels)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION labelValues"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + for (size_t i = 0; i < target->numLabels; i++) + { + if (mallocAndStrcpy_s((char**)&(target->labelNames[i]), source->labelNames[i]) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION labelNames"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(target->labelValues[i]), source->labelValues[i]) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION labelValues"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT cloneNameValueArrays_definitions(IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION *source, IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION *target) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result = IOTHUB_DEVICE_CONFIGURATION_OK; + + target->numQueries = source->numQueries; + if (target->numQueries > 0) + { + if ((target->queryNames = malloc(sizeof(const char*) * target->numQueries)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else if ((target->queryStrings = malloc(sizeof(const char*) * target->numQueries)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryStrings"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + for (size_t i = 0; i < target->numQueries; i++) + { + if (mallocAndStrcpy_s((char**)&(target->queryNames[i]), source->queryNames[i]) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (mallocAndStrcpy_s((char**)&(target->queryStrings[i]), source->queryStrings[i]) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION queryStrings"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT cloneNameValueArrays_results(IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT *source, IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT *target) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result = IOTHUB_DEVICE_CONFIGURATION_OK; + + target->numQueries = source->numQueries; + if (target->numQueries > 0) + { + if ((target->queryNames = malloc(sizeof(const char*) * target->numQueries)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else if ((target->results = malloc(sizeof(double) * target->numQueries)) == NULL) + { + LogError("Malloc failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT results"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else + { + for (size_t i = 0; i < target->numQueries; i++) + { + if (mallocAndStrcpy_s((char**)&(target->queryNames[i]), source->queryNames[i]) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT queryNames"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + target->results[i] = source->results[i]; + } + } + } + } + + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT clone_deviceConfiguration(IOTHUB_DEVICE_CONFIGURATION* source, IOTHUB_DEVICE_CONFIGURATION* target) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result = IOTHUB_DEVICE_CONFIGURATION_OK; + + if ((source != NULL) && (target != NULL)) + { + target->version = source->version; + target->priority = source->priority; + target->content.deviceContent = NULL; + target->content.modulesContent = NULL; + + if (source->configurationId != NULL && mallocAndStrcpy_s((char**)&(target->configurationId), source->configurationId) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION configurationId"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->schemaVersion != NULL && mallocAndStrcpy_s((char**)&(target->schemaVersion), source->schemaVersion) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION schemaVersion"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->targetCondition != NULL && mallocAndStrcpy_s((char**)&(target->targetCondition), source->targetCondition) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION targetCondition"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->eTag != NULL && mallocAndStrcpy_s((char**)&(target->eTag), source->eTag) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION eTag"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->createdTimeUtc != NULL && mallocAndStrcpy_s((char**)&(target->createdTimeUtc), source->createdTimeUtc) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION createdTimeUtc"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->lastUpdatedTimeUtc != NULL && mallocAndStrcpy_s((char**)&(target->lastUpdatedTimeUtc), source->lastUpdatedTimeUtc) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION lastUpdatedTimeUtc"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->content.deviceContent != NULL && mallocAndStrcpy_s((char**)&(target->content.deviceContent), source->content.deviceContent) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_CONTENT deviceContent"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (source->content.modulesContent != NULL && mallocAndStrcpy_s((char**)&(target->content.modulesContent), source->content.modulesContent) != 0) + { + LogError("mallocAndStrcpy_s failed for IOTHUB_DEVICE_CONFIGURATION_CONTENT modulesContent"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + if (cloneNameValueArrays_results(&(source->metricResult), &(target->metricResult)) != IOTHUB_DEVICE_CONFIGURATION_OK || cloneNameValueArrays_results(&(source->systemMetricsResult), &(target->systemMetricsResult)) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("Cloning failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_RESULT structures"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + + if (cloneNameValueArrays_definitions(&(source->metricsDefinition), &(target->metricsDefinition)) != IOTHUB_DEVICE_CONFIGURATION_OK || cloneNameValueArrays_definitions(&(source->systemMetricsDefinition), &(target->systemMetricsDefinition)) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("Cloning failed for IOTHUB_DEVICE_CONFIGURATION_METRICS_DEFINITION structures"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + + if (cloneNameValueArrays_labels(&(source->labels), &(target->labels)) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("Cloning failed for IOTHUB_DEVICE_CONFIGURATION_LABELS structures"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + } + } + return result; +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT addDeviceConfigurationToLinkedList(IOTHUB_DEVICE_CONFIGURATION* iothubDeviceConfiguration, SINGLYLINKEDLIST_HANDLE deviceConfigurationList) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + IOTHUB_DEVICE_CONFIGURATION* deviceConfiguration = NULL; + + if ((deviceConfiguration = (IOTHUB_DEVICE_CONFIGURATION*)calloc(1, sizeof(IOTHUB_DEVICE_CONFIGURATION))) == NULL) + { + LogError("Malloc failed for deviceConfiguration"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else + { + if (clone_deviceConfiguration(iothubDeviceConfiguration, deviceConfiguration) != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("clone_deviceConfiguration failed for deviceConfiguration"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((singlylinkedlist_add(deviceConfigurationList, deviceConfiguration)) == NULL) + { + LogError("singlylinkedlist_add deviceConfiguration failed"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + result = IOTHUB_DEVICE_CONFIGURATION_OK; + } + + if (result != IOTHUB_DEVICE_CONFIGURATION_OK) + { + IoTHubDeviceConfiguration_FreeConfigurationMembers(deviceConfiguration); + free(deviceConfiguration); + } + } + + return result; +} + +static void initializeDeviceConfigurationMembers(IOTHUB_DEVICE_CONFIGURATION * configuration) +{ + if (NULL != configuration) + { + memset(configuration, 0, sizeof(IOTHUB_DEVICE_CONFIGURATION)); + } +} + +static IOTHUB_DEVICE_CONFIGURATION_RESULT parseDeviceConfigurationListJson(BUFFER_HANDLE jsonBuffer, SINGLYLINKEDLIST_HANDLE configurationList) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + const char* bufferStr = NULL; + JSON_Value* root_value = NULL; + JSON_Array* device_configuration_array = NULL; + JSON_Status jsonStatus = JSONFailure; + + if (jsonBuffer == NULL) + { + LogError("jsonBuffer cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if (configurationList == NULL) + { + LogError("configurationList cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else + { + if ((bufferStr = (const char*)BUFFER_u_char(jsonBuffer)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_072: [** If populating the deviceList parameter fails IoTHubDeviceConfiguration_GetConfigurations shall return IOTHUB_DEVICE_CONFIGURATION_ERROR **] */ + LogError("BUFFER_u_char failed"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + else if ((root_value = json_parse_string(bufferStr)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_070: [** If any of the parson API fails, IoTHubDeviceConfiguration_GetConfigurations shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR **] */ + LogError("json_parse_string failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else if ((device_configuration_array = json_value_get_array(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_070: [** If any of the parson API fails, IoTHubDeviceConfiguration_GetConfigurations shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR **] */ + LogError("json_value_get_object failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else + { + result = IOTHUB_DEVICE_CONFIGURATION_OK; + + size_t array_count = json_array_get_count(device_configuration_array); + for (size_t i = 0; i < array_count; i++) + { + JSON_Object* device_configuration_object = NULL; + IOTHUB_DEVICE_CONFIGURATION iotHubDeviceConfiguration; + + if ((device_configuration_object = json_array_get_object(device_configuration_array, i)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_070: [** If any of the parson API fails, IoTHubDeviceConfiguration_GetConfigurations shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR **] */ + LogError("json_array_get_object failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + else + { + initializeDeviceConfigurationMembers(&iotHubDeviceConfiguration); + + result = parseDeviceConfigurationJsonObject(device_configuration_object, &iotHubDeviceConfiguration); + if (result != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("parseDeviceConfigurationJsonObject failed"); + } + else + { + result = addDeviceConfigurationToLinkedList(&iotHubDeviceConfiguration, configurationList); + if (result != IOTHUB_DEVICE_CONFIGURATION_OK) + { + LogError("addDeviceConfigurationToLinkedList failed"); + } + } + } + + json_object_clear(device_configuration_object); + IoTHubDeviceConfiguration_FreeConfigurationMembers(&iotHubDeviceConfiguration); + + if (result != IOTHUB_DEVICE_CONFIGURATION_OK) + { + break; + } + } + } + } + if (device_configuration_array != NULL) + { + if ((jsonStatus = json_array_clear(device_configuration_array)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_070: [** If any of the parson API fails, IoTHubDeviceConfiguration_GetConfigurations shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR **] */ + LogError("json_array_clear failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + } + + if (root_value != NULL) + { + json_value_free(root_value); + } + + if (result != IOTHUB_DEVICE_CONFIGURATION_OK) + { + if (configurationList != NULL) + { + LIST_ITEM_HANDLE itemHandle = singlylinkedlist_get_head_item(configurationList); + while (itemHandle != NULL) + { + IOTHUB_DEVICE_CONFIGURATION* curr_item = (IOTHUB_DEVICE_CONFIGURATION *)singlylinkedlist_item_get_value(itemHandle); + IoTHubDeviceConfiguration_FreeConfigurationMembers(curr_item); + free(curr_item); + + LIST_ITEM_HANDLE lastHandle = itemHandle; + itemHandle = singlylinkedlist_get_next_item(itemHandle); + singlylinkedlist_remove(configurationList, lastHandle); + } + } + } + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, size_t maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_060: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ] */ + if ((serviceClientDeviceConfigurationHandle == NULL) || (configurations == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_061: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall verify if the numberOfDevices input parameter is between 1 and 20 and if it is not then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ] */ + else if ((maxConfigurationsCount == 0) || (maxConfigurationsCount > IOTHUB_DEVICE_CONFIGURATIONS_MAX_REQUEST)) + { + LogError("numberOfDevices has to be between 1 and %d", IOTHUB_DEVICE_CONFIGURATIONS_MAX_REQUEST); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + BUFFER_HANDLE responseBuffer = NULL; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_109: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall allocate memory for response buffer by calling BUFFER_new ] */ + if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_110: [ If the BUFFER_new fails, IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall do clean up and return NULL ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_062: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall create HTTP GET request for numberOfDevices using the follwoing format: url/devices/?top=[numberOfDevices]&api-version ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_063: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_064: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_065: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_066: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_067: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_068: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceList ] */ + else if ((result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_GET_LIST, NULL, NULL, maxConfigurationsCount, responseBuffer)) == IOTHUB_DEVICE_CONFIGURATION_ERROR) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_115: [ If any of the HTTPAPI call fails IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall fail and return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("Failure sending HTTP request for get configuration list"); + } + else if (result == IOTHUB_DEVICE_CONFIGURATION_OK) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_069: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall use the parson APIs to parse the response JSON ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_070: [ If any of the parson API fails, IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_071: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall populate the deviceList parameter with structures of type "IOTHUB_DEVICE_CONFIGURATION" ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_072: [ If populating the deviceList parameter fails IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_073: [ If populating the deviceList parameter successful IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + result = parseDeviceConfigurationListJson(responseBuffer, configurations); + } + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_111: [ IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfigurations(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const int maxConfigurationsCount, SINGLYLINKEDLIST_HANDLE configurations) shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + } + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_GetConfiguration(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const char* configurationId, IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_18: [ IoTHubDeviceConfiguration_GetConfiguration shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ]*/ + if ((serviceClientDeviceConfigurationHandle == NULL) || (configurationId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + initializeDeviceConfigurationMembers(configuration); + + BUFFER_HANDLE responseBuffer; + + if ((responseBuffer = BUFFER_new()) == NULL) + { + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_026: [ IoTHubDeviceConfiguration_GetConfiguration shall create HTTP GET request URL using the given configurationId using the following format: url/devices/[configurationId]?[apiVersion] ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_027: [ IoTHubDeviceConfiguration_GetConfiguration shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_028: [ IoTHubDeviceConfiguration_GetConfiguration shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_029: [ IoTHubDeviceConfiguration_GetConfiguration shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_030: [ IoTHubDeviceConfiguration_GetConfiguration shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_GET, configurationId, NULL, (size_t)0, responseBuffer)) == IOTHUB_DEVICE_CONFIGURATION_ERROR) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_031: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_GetConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("Failure sending HTTP request for get device configuration"); + } + else if (result == IOTHUB_DEVICE_CONFIGURATION_OK) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_033: [ IoTHubDeviceConfiguration_GetConfiguration shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to configuration for the configuration properties] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_034: [ If any of the property field above missing from the JSON the property value will not be populated ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_035: [ If the JSON parsing failed, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_036: [ If the received JSON is empty, IoTHubDeviceConfiguration_GetConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_DEVICE_NOT_EXIST ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_037: [ If the configuration out parameter if not NULL IoTHubDeviceConfiguration_GetConfiguration shall save the received configuration to the out parameter and return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + result = parseDeviceConfigurationJson(responseBuffer, configuration); + } + + BUFFER_delete(responseBuffer); + } + + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_AddConfiguration(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const IOTHUB_DEVICE_CONFIGURATION_ADD* configurationCreate, IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_007: [ IoTHubDeviceConfiguration_AddConfiguration shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ] */ + if ((serviceClientDeviceConfigurationHandle == NULL) || (configurationCreate == NULL) || (configuration == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + initializeDeviceConfigurationMembers(configuration); + + if (configurationCreate->configurationId == NULL) + { + LogError("configurationId cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_095: [ IoTHubDeviceConfiguration_AddConfiguration shall allocate memory for device info structure by calling malloc ] */ + IOTHUB_DEVICE_CONFIGURATION* tempConfigurationInfo; + if ((tempConfigurationInfo = malloc(sizeof(IOTHUB_DEVICE_CONFIGURATION))) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_096 : [ If the malloc fails, IoTHubDeviceConfiguration_AddConfiguration shall do clean up and return IOTHUB_DEVICE_CONFIGURATION_ERROR. ] */ + LogError("Malloc failed for tempconfiguration"); + result = IOTHUB_DEVICE_CONFIGURATION_OUT_OF_MEMORY_ERROR; + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_011: [ IoTHubDeviceConfiguration_AddConfiguration shall set the "configurationId" value to the configurationCreate->configurationId ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_011: [ IoTHubDeviceConfiguration_AddConfiguration shall set the "targetCondition" value to the configurationCreate->targetCondition ] */ + memset(tempConfigurationInfo, 0, sizeof(*tempConfigurationInfo)); + tempConfigurationInfo->configurationId = configurationCreate->configurationId; + tempConfigurationInfo->targetCondition = configurationCreate->targetCondition; + tempConfigurationInfo->content = configurationCreate->content; + tempConfigurationInfo->labels = configurationCreate->labels; + tempConfigurationInfo->metricsDefinition = configurationCreate->metrics; + tempConfigurationInfo->priority = configurationCreate->priority; + + tempConfigurationInfo->schemaVersion = CONFIGURATION_DEFAULT_SCHEMA_VERSION; + tempConfigurationInfo->eTag = CONFIGURATION_DEFAULT_ETAG; + + BUFFER_HANDLE configurationJsonBuffer = NULL; + BUFFER_HANDLE responseBuffer = NULL; + + if (((tempConfigurationInfo->content).deviceContent == NULL || strlen((tempConfigurationInfo->content).deviceContent) == 0) && + ((tempConfigurationInfo->content).modulesContent == NULL || strlen((tempConfigurationInfo->content).modulesContent) == 0)) + { + LogError("both deviceContent and modulesContent cannot be null"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_010: [ IoTHubDeviceConfiguration_AddConfiguration shall create a flat "key1:value2,key2:value2..." JSON representation from the given deviceOrModuleCreateInfo parameter using parson APIs ] */ + else if ((configurationJsonBuffer = createConfigurationPayloadJson(tempConfigurationInfo)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_013: [ IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR if the JSON creation failed ] */ + LogError("Json creation failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_097: [ IoTHubDeviceConfiguration_AddConfiguration shall allocate memory for response buffer by calling BUFFER_new ] */ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_098 : [ If the BUFFER_new fails, IoTHubDeviceConfiguration_AddConfiguration shall do clean up and return IOTHUB_DEVICE_CONFIGURATION_ERROR. ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_014: [ IoTHubDeviceConfiguration_AddConfiguration shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_015: [ IoTHubDeviceConfiguration_AddConfiguration shall create an HTTP PUT request using the following HTTP headers: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_016: [ IoTHubDeviceConfiguration_AddConfiguration shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_017: [ IoTHubDeviceConfiguration_AddConfiguration shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_018: [ IoTHubDeviceConfiguration_AddConfiguration shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_ADD, tempConfigurationInfo->configurationId, configurationJsonBuffer, (size_t)0, responseBuffer)) == IOTHUB_DEVICE_CONFIGURATION_ERROR) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_019: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_AddConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_099: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_AddConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("Failure sending HTTP request for create device"); + } + else if (result == IOTHUB_DEVICE_CONFIGURATION_OK) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_022: [ IoTHubDeviceConfiguration_AddConfiguration shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to configuration ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_023: [ If the JSON parsing failed, IoTHubDeviceConfiguration_AddConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_024: [ If the configuration out parameter is not NULL IoTHubDeviceConfiguration_AddConfiguration shall save the received configuration to the out parameter and return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + result = parseDeviceConfigurationJson(responseBuffer, configuration); + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_020: [ IoTHubDeviceConfiguration_AddConfiguration shall verify the received HTTP status code and if it is 409 then return IOTHUB_DEVICE_CONFIGURATION_DEVICE_EXIST ] */ + } + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_100: [ IoTHubDeviceConfiguration_AddConfiguration shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + if (configurationJsonBuffer != NULL) + { + BUFFER_delete(configurationJsonBuffer); + } + } + free(tempConfigurationInfo); + } + } + + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_UpdateConfiguration(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const IOTHUB_DEVICE_CONFIGURATION* configuration) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_007: [ IoTHubDeviceConfiguration_UpdateConfiguration shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ] */ + if ((serviceClientDeviceConfigurationHandle == NULL) || (configuration == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + if (configuration->configurationId == NULL) + { + LogError("configurationId cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + BUFFER_HANDLE configurationJsonBuffer = NULL; + BUFFER_HANDLE responseBuffer = NULL; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_010: [ IoTHubDeviceConfiguration_UpdateConfiguration shall create a flat "key1:value2,key2:value2..." JSON representation from the given configuration parameter using the parson APIs ] */ + if ((configurationJsonBuffer = createConfigurationPayloadJson(configuration)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_013: [ IoTHubDeviceConfiguration_UpdateConfiguration shall return IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR if the JSON creation failed ] */ + LogError("Json creation failed"); + result = IOTHUB_DEVICE_CONFIGURATION_JSON_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_097: [ IoTHubDeviceConfiguration_UpdateConfiguration shall allocate memory for response buffer by calling BUFFER_new ] */ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_098 : [ If the BUFFER_new fails, IoTHubDeviceConfiguration_UpdateConfiguration shall do clean up and return IOTHUB_DEVICE_CONFIGURATION_ERROR. ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_DEVICE_CONFIGURATION_ERROR; + } + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_014: [ IoTHubDeviceConfiguration_UpdateConfiguration shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_015: [ IoTHubDeviceConfiguration_UpdateConfiguration shall create an HTTP PUT request using the following HTTP headers: authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_016: [ IoTHubDeviceConfiguration_UpdateConfiguration shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_017: [ IoTHubDeviceConfiguration_UpdateConfiguration shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_018: [ IoTHubDeviceConfiguration_UpdateConfiguration shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_UPDATE, configuration->configurationId, configurationJsonBuffer, (size_t)0, responseBuffer)) == IOTHUB_DEVICE_CONFIGURATION_ERROR) + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_019: [ If any of the HTTPAPI call fails IoTHubDeviceConfiguration_UpdateConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_099: [ If any of the call fails during the HTTP creation IoTHubDeviceConfiguration_UpdateConfiguration shall fail and return IOTHUB_DEVICE_CONFIGURATION_ERROR ] */ + LogError("Failure sending HTTP request for update device configuration"); + } + else + { + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_020: [ IoTHubDeviceConfiguration_UpdateConfiguration shall verify the received HTTP status code and if it is 409 then return IOTHUB_DEVICE_CONFIGURATION_DEVICE_EXIST ] */ + } + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_100: [ IoTHubDeviceConfiguration_UpdateConfiguration shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + if (configurationJsonBuffer != NULL) + { + BUFFER_delete(configurationJsonBuffer); + } + } + } + + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_DeleteConfiguration(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const char* configurationId) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_052: [ IoTHubDeviceConfiguration_DeleteConfiguration shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ]*/ + if ((serviceClientDeviceConfigurationHandle == NULL) || (configurationId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + /*SRS_IOTHUBDEVICECONFIGURATION_38_053: [ IoTHubDeviceConfiguration_DeleteConfiguration shall create HTTP DELETE request URL using the given configurationId using the following format : url/configurations/[configurationId]?api-version ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_054: [ IoTHubDeviceConfiguration_DeleteConfiguration shall add the following headers to the created HTTP GET request : authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_055: [ IoTHubDeviceConfiguration_DeleteConfiguration shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_056: [ IoTHubDeviceConfiguration_DeleteConfiguration shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_057: [ IoTHubDeviceConfiguration_DeleteConfiguration shall execute the HTTP DELETE request by calling HTTPAPIEX_ExecuteRequest ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_058: [ IoTHubDeviceConfiguration_DeleteConfiguration shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_DEVICE_CONFIGURATION_HTTP_STATUS_ERROR ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_059: [ IoTHubDeviceConfiguration_DeleteConfiguration shall verify the received HTTP status code and if it is less or equal than 300 then return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_DELETE, configurationId, NULL, (size_t)0, NULL); + } + + return result; +} + +IOTHUB_DEVICE_CONFIGURATION_RESULT IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule(IOTHUB_SERVICE_CLIENT_DEVICE_CONFIGURATION_HANDLE serviceClientDeviceConfigurationHandle, const char* deviceOrModuleId, const IOTHUB_DEVICE_CONFIGURATION_CONTENT* configurationContent) +{ + IOTHUB_DEVICE_CONFIGURATION_RESULT result; + + /*Codes_SRS_IOTHUBDEVICECONFIGURATION_38_052: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG ]*/ + if ((serviceClientDeviceConfigurationHandle == NULL) || (deviceOrModuleId == NULL) || (configurationContent == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_CONFIGURATION_INVALID_ARG; + } + else + { + BUFFER_HANDLE configurationJsonBuffer; + + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + JSON_Value* configurationContentJson; + + if ((configurationContent->deviceContent == NULL) && ((configurationContent->modulesContent) == NULL)) + { + LogError("deviceContent and modulesContent both cannot be NULL"); + configurationJsonBuffer = NULL; + } + else if ((root_value = json_value_init_object()) == NULL) + { + LogError("json_value_init_object failed"); + configurationJsonBuffer = NULL; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + LogError("json_value_get_object failed"); + configurationJsonBuffer = NULL; + } + else if ((((configurationContentJson = createConfigurationContentPayload(configurationContent)) != NULL) && ((json_object_set_value(root_object, CONFIGURATION_JSON_KEY_CONTENT, configurationContentJson)) != JSONSuccess))) + { + LogError("json_object_set_string failed for configurationContentJson"); + configurationJsonBuffer = NULL; + } + else + { + char* serialized_string; + if ((serialized_string = json_serialize_to_string(root_value)) == NULL) + { + LogError("json_serialize_to_string failed"); + configurationJsonBuffer = NULL; + } + else + { + if ((configurationJsonBuffer = BUFFER_create((const unsigned char*)serialized_string, strlen(serialized_string))) == NULL) + { + LogError("Buffer_Create failed"); + } + json_free_serialized_string(serialized_string); + } + } + + json_object_clear(root_object); + + if (root_value != NULL) + json_value_free(root_value); + + /*SRS_IOTHUBDEVICECONFIGURATION_38_053: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall create HTTP POST request URL using the given deviceOrModuleId using the following format : url/devices/[deviceOrModuleId]?api-version ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_054: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall add the following headers to the created HTTP POST request : authorization=sasToken,Request-Id=<generatedGuid>,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_055: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_056: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_057: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall execute the HTTP POST request by calling HTTPAPIEX_ExecuteRequest ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_058: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_DEVICE_CONFIGURATION_HTTP_STATUS_ERROR ] */ + /*SRS_IOTHUBDEVICECONFIGURATION_38_059: [ IoTHubDeviceConfiguration_ApplyConfigurationContentToDeviceOrModule shall verify the received HTTP status code and if it is equal to 200 or 204 then return IOTHUB_DEVICE_CONFIGURATION_OK ] */ + result = sendHttpRequestDeviceConfiguration(serviceClientDeviceConfigurationHandle, IOTHUB_DEVICECONFIGURATION_REQUEST_APPLY_CONFIGURATION_CONTENT, deviceOrModuleId, configurationJsonBuffer, (size_t)0, NULL); + } + + return result; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_devicemethod.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,549 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/connection_string_parser.h" + +#include "parson.h" +#include "iothub_devicemethod.h" +#include "iothub_sc_version.h" + +DEFINE_ENUM_STRINGS(IOTHUB_DEVICE_METHOD_RESULT, IOTHUB_DEVICE_METHOD_RESULT_VALUES); + +#define IOTHUB_DEVICE_METHOD_REQUEST_MODE_VALUES \ + IOTHUB_DEVICEMETHOD_REQUEST_INVOKE + +DEFINE_ENUM(IOTHUB_DEVICEMETHOD_REQUEST_MODE, IOTHUB_DEVICE_METHOD_REQUEST_MODE_VALUES); + + +#define HTTP_HEADER_KEY_AUTHORIZATION "Authorization" +#define HTTP_HEADER_VAL_AUTHORIZATION " " +#define HTTP_HEADER_KEY_REQUEST_ID "Request-Id" +#define HTTP_HEADER_KEY_USER_AGENT "User-Agent" +#define HTTP_HEADER_VAL_USER_AGENT IOTHUB_SERVICE_CLIENT_TYPE_PREFIX IOTHUB_SERVICE_CLIENT_BACKSLASH IOTHUB_SERVICE_CLIENT_VERSION +#define HTTP_HEADER_KEY_ACCEPT "Accept" +#define HTTP_HEADER_VAL_ACCEPT "application/json" +#define HTTP_HEADER_KEY_CONTENT_TYPE "Content-Type" +#define HTTP_HEADER_VAL_CONTENT_TYPE "application/json; charset=utf-8" +#define UID_LENGTH 37 + +static const char* const URL_API_VERSION = "?api-version=2017-11-08-preview"; +static const char* const RELATIVE_PATH_FMT_DEVICEMETHOD = "/twins/%s/methods%s"; +static const char* const RELATIVE_PATH_FMT_DEVICEMETHOD_MODULE = "/twins/%s/modules/%s/methods%s"; +static const char* const RELATIVE_PATH_FMT_DEVIECMETHOD_PAYLOAD = "{\"methodName\":\"%s\",\"timeout\":%d,\"payload\":%s}"; + +/** @brief Structure to store IoTHub authentication information +*/ +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_TAG +{ + char* hostname; + char* sharedAccessKey; + char* keyName; +} IOTHUB_SERVICE_CLIENT_DEVICE_METHOD; + +static IOTHUB_DEVICE_METHOD_RESULT parseResponseJson(BUFFER_HANDLE responseJson, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_DEVICE_METHOD_RESULT result; + JSON_Value* root_value; + JSON_Object* json_object; + JSON_Value* statusJsonValue; + JSON_Value* payloadJsonValue; + char* payload; + STRING_HANDLE jsonStringHandle; + const char* jsonStr; + unsigned char* bufferStr; + + if ((bufferStr = BUFFER_u_char(responseJson)) == NULL) + { + LogError("BUFFER_u_char failed"); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((jsonStringHandle = STRING_from_byte_array(bufferStr, BUFFER_length(responseJson))) == NULL) + { + LogError("STRING_construct_n failed"); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((jsonStr = STRING_c_str(jsonStringHandle)) == NULL) + { + LogError("STRING_c_str failed"); + STRING_delete(jsonStringHandle); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((root_value = json_parse_string(jsonStr)) == NULL) + { + LogError("json_parse_string failed"); + STRING_delete(jsonStringHandle); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((json_object = json_value_get_object(root_value)) == NULL) + { + LogError("json_value_get_object failed"); + STRING_delete(jsonStringHandle); + json_value_free(root_value); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((statusJsonValue = json_object_get_value(json_object, "status")) == NULL) + { + LogError("json_object_get_value failed for status"); + STRING_delete(jsonStringHandle); + json_value_free(root_value); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((payloadJsonValue = json_object_get_value(json_object, "payload")) == NULL) + { + LogError("json_object_get_value failed for payload"); + STRING_delete(jsonStringHandle); + json_value_free(root_value); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((payload = json_serialize_to_string(payloadJsonValue)) == NULL) + { + LogError("json_serialize_to_string failed for payload"); + STRING_delete(jsonStringHandle); + json_value_free(root_value); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else + { + *responseStatus = (int)json_value_get_number(statusJsonValue); + *responsePayload = (unsigned char *)payload; + *responsePayloadSize = strlen(payload); + + STRING_delete(jsonStringHandle); + json_value_free(root_value); + result = IOTHUB_DEVICE_METHOD_OK; + } + + return result; +} + +static BUFFER_HANDLE createMethodPayloadJson(const char* methodName, unsigned int timeout, const char* payload) +{ + STRING_HANDLE stringHandle; + const char* stringHandle_c_str; + BUFFER_HANDLE result; + + if ((stringHandle = STRING_construct_sprintf(RELATIVE_PATH_FMT_DEVIECMETHOD_PAYLOAD, methodName, timeout, payload)) == NULL) + { + LogError("STRING_construct_sprintf failed"); + result = NULL; + } + else if ((stringHandle_c_str = STRING_c_str(stringHandle)) == NULL) + { + LogError("STRING_c_str failed"); + STRING_delete(stringHandle); + result = NULL; + } + else + { + result = BUFFER_create((const unsigned char*)stringHandle_c_str, strlen(stringHandle_c_str)); + STRING_delete(stringHandle); + } + return result; +} + +static const char* generateGuid(void) +{ + char* result; + + if ((result = malloc(UID_LENGTH)) != NULL) + { + result[0] = '\0'; + if (UniqueId_Generate(result, UID_LENGTH) != UNIQUEID_OK) + { + LogError("UniqueId_Generate failed"); + free((void*)result); + result = NULL; + } + } + return (const char*)result; +} + +static STRING_HANDLE createRelativePath(IOTHUB_DEVICEMETHOD_REQUEST_MODE iotHubDeviceMethodRequestMode, const char* deviceId, const char* moduleId) +{ + STRING_HANDLE result; + + if (iotHubDeviceMethodRequestMode == IOTHUB_DEVICEMETHOD_REQUEST_INVOKE) + { + if (moduleId != NULL) + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_DEVICEMETHOD_MODULE, deviceId, moduleId, URL_API_VERSION); + } + else + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_DEVICEMETHOD, deviceId, URL_API_VERSION); + } + } + else + { + result = NULL; + } + return result; +} + +static HTTP_HEADERS_HANDLE createHttpHeader() +{ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_020: [ IoTHubDeviceMethod_GetTwin shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + HTTP_HEADERS_HANDLE httpHeader; + const char* guid; + + if ((httpHeader = HTTPHeaders_Alloc()) == NULL) + { + LogError("HTTPHeaders_Alloc failed"); + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_AUTHORIZATION, HTTP_HEADER_VAL_AUTHORIZATION) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Authorization header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if ((guid = generateGuid()) == NULL) + { + LogError("GUID creation failed"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_REQUEST_ID, guid) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for RequestId header"); + free((void*)guid); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_USER_AGENT, HTTP_HEADER_VAL_USER_AGENT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for User-Agent header"); + free((void*)guid); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_CONTENT_TYPE, HTTP_HEADER_VAL_CONTENT_TYPE) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Content-Type header"); + free((void*)guid); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else + { + free((void*)guid); + } + + return httpHeader; +} + +static IOTHUB_DEVICE_METHOD_RESULT sendHttpRequestDeviceMethod(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle, IOTHUB_DEVICEMETHOD_REQUEST_MODE iotHubDeviceMethodRequestMode, const char* deviceId, const char* moduleId, BUFFER_HANDLE deviceJsonBuffer, BUFFER_HANDLE responseBuffer) +{ + IOTHUB_DEVICE_METHOD_RESULT result; + + STRING_HANDLE uriResource; + STRING_HANDLE accessKey; + STRING_HANDLE keyName; + HTTPAPIEX_SAS_HANDLE httpExApiSasHandle; + HTTPAPIEX_HANDLE httpExApiHandle; + HTTP_HEADERS_HANDLE httpHeader; + + if ((uriResource = STRING_construct(serviceClientDeviceMethodHandle->hostname)) == NULL) + { + LogError("STRING_construct failed for uriResource"); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((accessKey = STRING_construct(serviceClientDeviceMethodHandle->sharedAccessKey)) == NULL) + { + LogError("STRING_construct failed for accessKey"); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((keyName = STRING_construct(serviceClientDeviceMethodHandle->keyName)) == NULL) + { + LogError("STRING_construct failed for keyName"); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((httpHeader = createHttpHeader()) == NULL) + { + LogError("HttpHeader creation failed"); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if ((httpExApiSasHandle = HTTPAPIEX_SAS_Create(accessKey, uriResource, keyName)) == NULL) + { + LogError("HTTPAPIEX_SAS_Create failed"); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; + } + else if ((httpExApiHandle = HTTPAPIEX_Create(serviceClientDeviceMethodHandle->hostname)) == NULL) + { + LogError("HTTPAPIEX_Create failed"); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; + } + else + { + HTTPAPI_REQUEST_TYPE httpApiRequestType = HTTPAPI_REQUEST_GET; + STRING_HANDLE relativePath; + unsigned int statusCode = 0; + unsigned char is_error = 0; + + if (iotHubDeviceMethodRequestMode == IOTHUB_DEVICEMETHOD_REQUEST_INVOKE) + { + httpApiRequestType = HTTPAPI_REQUEST_POST; + } + else + { + is_error = 1; + } + + if (is_error) + { + LogError("Invalid request type"); + result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; + } + else + { + if ((relativePath = createRelativePath(iotHubDeviceMethodRequestMode, deviceId, moduleId)) == NULL) + { + LogError("Failure creating relative path"); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else if (HTTPAPIEX_SAS_ExecuteRequest(httpExApiSasHandle, httpExApiHandle, httpApiRequestType, STRING_c_str(relativePath), httpHeader, deviceJsonBuffer, &statusCode, NULL, responseBuffer) != HTTPAPIEX_OK) + { + LogError("HTTPAPIEX_SAS_ExecuteRequest failed"); + STRING_delete(relativePath); + result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; + } + else + { + STRING_delete(relativePath); + if (statusCode == 200) + { + result = IOTHUB_DEVICE_METHOD_OK; + } + else + { + LogError("Http Failure status code %d.", statusCode); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + } + } + HTTPAPIEX_Destroy(httpExApiHandle); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + } + return result; +} + +IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE IoTHubDeviceMethod_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE result; + + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_001: [ If the serviceClientHandle input parameter is NULL IoTHubDeviceMethod_Create shall return NULL ]*/ + if (serviceClientHandle == NULL) + { + LogError("serviceClientHandle input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_002: [ If any member of the serviceClientHandle input parameter is NULL IoTHubDeviceMethod_Create shall return NULL ]*/ + IOTHUB_SERVICE_CLIENT_AUTH* serviceClientAuth = (IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle; + + if (serviceClientAuth->hostname == NULL) + { + LogError("authInfo->hostName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubName == NULL) + { + LogError("authInfo->iothubName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubSuffix == NULL) + { + LogError("authInfo->iothubSuffix input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->keyName == NULL) + { + LogError("authInfo->keyName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->sharedAccessKey == NULL) + { + LogError("authInfo->sharedAccessKey input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_003: [ IoTHubDeviceMethod_Create shall allocate memory for a new IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE instance ]*/ + result = malloc(sizeof(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_004: [ If the allocation failed, IoTHubDeviceMethod_Create shall return NULL ]*/ + LogError("Malloc failed for IOTHUB_SERVICE_CLIENT_DEVICE_METHOD"); + } + else + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_005: [ If the allocation successful, IoTHubDeviceMethod_Create shall create a IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE from the given IOTHUB_SERVICE_CLIENT_AUTH_HANDLE and return with it ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_006: [ IoTHubDeviceMethod_Create shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s. ]*/ + if (mallocAndStrcpy_s(&result->hostname, serviceClientAuth->hostname) != 0) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_007: [ If the mallocAndStrcpy_s fails, IoTHubDeviceMethod_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for hostName"); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_012: [ IoTHubDeviceMethod_Create shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, serviceClientAuth->sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_013: [ If the mallocAndStrcpy_s fails, IoTHubDeviceMethod_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free(result->hostname); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_014: [ IoTHubDeviceMethod_Create shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->keyName, serviceClientAuth->keyName) != 0) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_015: [ If the mallocAndStrcpy_s fails, IoTHubDeviceMethod_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for keyName"); + free(result->hostname); + free(result->sharedAccessKey); + free(result); + result = NULL; + } + } + } + } + return result; +} + +void IoTHubDeviceMethod_Destroy(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle) +{ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_016: [ If the serviceClientDeviceMethodHandle input parameter is NULL IoTHubDeviceMethod_Destroy shall return ]*/ + if (serviceClientDeviceMethodHandle != NULL) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_017: [ If the serviceClientDeviceMethodHandle input parameter is not NULL IoTHubDeviceMethod_Destroy shall free the memory of it and return ]*/ + IOTHUB_SERVICE_CLIENT_DEVICE_METHOD* serviceClientDeviceMethod = (IOTHUB_SERVICE_CLIENT_DEVICE_METHOD*)serviceClientDeviceMethodHandle; + + free(serviceClientDeviceMethod->hostname); + free(serviceClientDeviceMethod->sharedAccessKey); + free(serviceClientDeviceMethod->keyName); + free(serviceClientDeviceMethod); + } +} + +static IOTHUB_DEVICE_METHOD_RESULT IoTHubDeviceMethod_DeviceOrModuleInvoke(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_DEVICE_METHOD_RESULT result; + + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_031: [ IoTHubDeviceMethod_Invoke(Module) shall verify the input parameters and if any of them (except the timeout) are NULL then return IOTHUB_DEVICE_METHOD_INVALID_ARG ]*/ + if ((serviceClientDeviceMethodHandle == NULL) || (deviceId == NULL) || (methodName == NULL) || (methodPayload == NULL) || (responseStatus == NULL) || (responsePayload == NULL) || (responsePayloadSize == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_DEVICE_METHOD_INVALID_ARG; + } + else + { + BUFFER_HANDLE httpPayloadBuffer; + BUFFER_HANDLE responseBuffer; + + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_032: [ IoTHubDeviceMethod_Invoke(Module) shall create a BUFFER_HANDLE from methodName, timeout and methodPayload by calling BUFFER_create ]*/ + if ((httpPayloadBuffer = createMethodPayloadJson(methodName, timeout, methodPayload)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_033: [ If the creation fails, IoTHubDeviceMethod_Invoke(Module) shall return IOTHUB_DEVICE_METHOD_ERROR ]*/ + LogError("BUFFER creation failed for httpPayloadBuffer"); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_034: [ IoTHubDeviceMethod_Invoke(Module) shall allocate memory for response buffer by calling BUFFER_new ]*/ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_035: [ If the allocation failed, IoTHubDeviceMethod_Invoke(Module) shall return IOTHUB_DEVICE_METHOD_ERROR ]*/ + LogError("BUFFER_new failed for responseBuffer"); + BUFFER_delete(httpPayloadBuffer); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_039: [ IoTHubDeviceMethod_Invoke(Module) shall create an HTTP POST request using methodPayloadBuffer ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_040: [ IoTHubDeviceMethod_Invoke(Module) shall create an HTTP POST request using the following HTTP headers: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_041: [ IoTHubDeviceMethod_Invoke(Module) shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_042: [ IoTHubDeviceMethod_Invoke(Module) shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_043: [ IoTHubDeviceMethod_Invoke(Module) shall execute the HTTP POST request by calling HTTPAPIEX_ExecuteRequest ]*/ + else if (sendHttpRequestDeviceMethod(serviceClientDeviceMethodHandle, IOTHUB_DEVICEMETHOD_REQUEST_INVOKE, deviceId, moduleId, httpPayloadBuffer, responseBuffer) != IOTHUB_DEVICE_METHOD_OK) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_044: [ If any of the call fails during the HTTP creation IoTHubDeviceMethod_Invoke(Module) shall fail and return IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_045: [ If any of the HTTPAPI call fails IoTHubDeviceMethod_Invoke(Module) shall fail and return IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR ]*/ + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_046: [ IoTHubDeviceMethod_Invoke(Module) shall verify the received HTTP status code and if it is not equal to 200 then return IOTHUB_DEVICE_METHOD_ERROR ]*/ + LogError("Failure sending HTTP request for device method invoke"); + BUFFER_delete(responseBuffer); + BUFFER_delete(httpPayloadBuffer); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_049: [ Otherwise IoTHubDeviceMethod_Invoke(Module) shall save the received status and payload to the corresponding out parameter and return with IOTHUB_DEVICE_METHOD_OK ]*/ + else if ((parseResponseJson(responseBuffer, responseStatus, responsePayload, responsePayloadSize)) != IOTHUB_DEVICE_METHOD_OK) + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_047: [ If parsing the response fails IoTHubDeviceMethod_Invoke(Module) shall return IOTHUB_DEVICE_METHOD_ERROR ]*/ + LogError("Failure parsing response"); + BUFFER_delete(responseBuffer); + BUFFER_delete(httpPayloadBuffer); + result = IOTHUB_DEVICE_METHOD_ERROR; + } + else + { + /*Codes_SRS_IOTHUBDEVICEMETHOD_12_049: [ Otherwise IoTHubDeviceMethod_Invoke(Module) shall save the received status and payload to the corresponding out parameter and return with IOTHUB_DEVICE_METHOD_OK ]*/ + result = IOTHUB_DEVICE_METHOD_OK; + + BUFFER_delete(responseBuffer); + BUFFER_delete(httpPayloadBuffer); + } + } + return result; + +} + +IOTHUB_DEVICE_METHOD_RESULT IoTHubDeviceMethod_Invoke(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle, const char* deviceId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + return IoTHubDeviceMethod_DeviceOrModuleInvoke(serviceClientDeviceMethodHandle, deviceId, NULL, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); +} + +IOTHUB_DEVICE_METHOD_RESULT IoTHubDeviceMethod_InvokeModule(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_DEVICE_METHOD_RESULT result; + + if (moduleId == NULL) + { + /*SRS_IOTHUBDEVICEMETHOD_31_050: [ IoTHubDeviceMethod_InvokeModule shall return IOTHUB_DEVICE_METHOD_INVALID_ARG if moduleId is NULL. ]*/ + LogError("moduleId input parameter cannot be NULL"); + result = IOTHUB_DEVICE_METHOD_INVALID_ARG; + } + else + { + result = IoTHubDeviceMethod_DeviceOrModuleInvoke(serviceClientDeviceMethodHandle, deviceId, moduleId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_devicetwin.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,585 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/uniqueid.h" +#include "azure_c_shared_utility/connection_string_parser.h" + +#include "parson.h" +#include "iothub_devicetwin.h" +#include "iothub_sc_version.h" + +#define IOTHUB_TWIN_REQUEST_MODE_VALUES \ + IOTHUB_TWIN_REQUEST_GET, \ + IOTHUB_TWIN_REQUEST_UPDATE, \ + IOTHUB_TWIN_REQUEST_REPLACE_TAGS, \ + IOTHUB_TWIN_REQUEST_REPLACE_DESIRED, \ + IOTHUB_TWIN_REQUEST_UPDATE_DESIRED + +DEFINE_ENUM(IOTHUB_TWIN_REQUEST_MODE, IOTHUB_TWIN_REQUEST_MODE_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_DEVICE_TWIN_RESULT, IOTHUB_DEVICE_TWIN_RESULT_VALUES); + +#define HTTP_HEADER_KEY_AUTHORIZATION "Authorization" +#define HTTP_HEADER_VAL_AUTHORIZATION " " +#define HTTP_HEADER_KEY_REQUEST_ID "Request-Id" +#define HTTP_HEADER_KEY_USER_AGENT "User-Agent" +#define HTTP_HEADER_VAL_USER_AGENT IOTHUB_SERVICE_CLIENT_TYPE_PREFIX IOTHUB_SERVICE_CLIENT_BACKSLASH IOTHUB_SERVICE_CLIENT_VERSION +#define HTTP_HEADER_KEY_ACCEPT "Accept" +#define HTTP_HEADER_VAL_ACCEPT "application/json" +#define HTTP_HEADER_KEY_CONTENT_TYPE "Content-Type" +#define HTTP_HEADER_VAL_CONTENT_TYPE "application/json; charset=utf-8" +#define HTTP_HEADER_KEY_IFMATCH "If-Match" +#define HTTP_HEADER_VAL_IFMATCH "*" +#define UID_LENGTH 37 + +static const char* URL_API_VERSION = "?api-version=2017-11-08-preview"; + +static const char* RELATIVE_PATH_FMT_TWIN = "/twins/%s%s"; +static const char* RELATIVE_PATH_FMT_TWIN_MODULE = "/twins/%s/modules/%s%s"; + + +/** @brief Structure to store IoTHub authentication information +*/ +typedef struct IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_TAG +{ + char* hostname; + char* sharedAccessKey; + char* keyName; +} IOTHUB_SERVICE_CLIENT_DEVICE_TWIN; + +static const char* generateGuid(void) +{ + char* result; + + if ((result = malloc(UID_LENGTH)) != NULL) + { + result[0] = '\0'; + if (UniqueId_Generate(result, UID_LENGTH) != UNIQUEID_OK) + { + free((void*)result); + result = NULL; + } + } + return (const char*)result; +} + +static STRING_HANDLE createRelativePath(IOTHUB_TWIN_REQUEST_MODE iotHubTwinRequestMode, const char* deviceId, const char* moduleId) +{ + //IOTHUB_TWIN_REQUEST_GET GET {iot hub}/twins/{device id} // Get device twin + //IOTHUB_TWIN_REQUEST_UPDATE PATCH {iot hub}/twins/{device id} // Partally update device twin + //IOTHUB_TWIN_REQUEST_REPLACE_TAGS PUT {iot hub}/twins/{device id}/tags // Replace update tags + //IOTHUB_TWIN_REQUEST_REPLACE_DESIRED PUT {iot hub}/twins/{device id}/properties/desired // Replace update desired properties + //IOTHUB_TWIN_REQUEST_UPDATE_DESIRED PATCH {iot hub}/twins/{device id}/properties/desired // Partially update desired properties + + STRING_HANDLE result; + + if ((iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_GET) || (iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_UPDATE)) + { + if (moduleId == NULL) + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_TWIN, deviceId, URL_API_VERSION); + } + else + { + result = STRING_construct_sprintf(RELATIVE_PATH_FMT_TWIN_MODULE, deviceId, moduleId, URL_API_VERSION); + } + } + else + { + result = NULL; + } + return result; +} + +static HTTP_HEADERS_HANDLE createHttpHeader(IOTHUB_TWIN_REQUEST_MODE iotHubTwinRequestMode) +{ + /*Codes_SRS_IOTHUBDEVICETWIN_12_020: [ IoTHubDeviceTwin_GetTwin shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + HTTP_HEADERS_HANDLE httpHeader; + const char* guid = NULL; + + if ((httpHeader = HTTPHeaders_Alloc()) == NULL) + { + LogError("HTTPHeaders_Alloc failed"); + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_AUTHORIZATION, HTTP_HEADER_VAL_AUTHORIZATION) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Authorization header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if ((guid = generateGuid()) == NULL) + { + LogError("GUID creation failed"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_REQUEST_ID, guid) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for RequestId header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_USER_AGENT, HTTP_HEADER_VAL_USER_AGENT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for User-Agent header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_CONTENT_TYPE, HTTP_HEADER_VAL_CONTENT_TYPE) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Content-Type header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (iotHubTwinRequestMode != IOTHUB_TWIN_REQUEST_GET) + { + if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_IFMATCH, HTTP_HEADER_VAL_IFMATCH) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for If-Match header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + } + free((void*)guid); + + return httpHeader; +} + +static IOTHUB_DEVICE_TWIN_RESULT sendHttpRequestTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, IOTHUB_TWIN_REQUEST_MODE iotHubTwinRequestMode, const char* deviceName, const char* moduleId, BUFFER_HANDLE deviceJsonBuffer, BUFFER_HANDLE responseBuffer) +{ + IOTHUB_DEVICE_TWIN_RESULT result; + + STRING_HANDLE uriResource = NULL; + STRING_HANDLE accessKey = NULL; + STRING_HANDLE keyName = NULL; + HTTPAPIEX_SAS_HANDLE httpExApiSasHandle; + HTTPAPIEX_HANDLE httpExApiHandle; + HTTP_HEADERS_HANDLE httpHeader; + + if ((uriResource = STRING_construct(serviceClientDeviceTwinHandle->hostname)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("STRING_construct failed for uriResource"); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + else if ((accessKey = STRING_construct(serviceClientDeviceTwinHandle->sharedAccessKey)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("STRING_construct failed for accessKey"); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + else if ((keyName = STRING_construct(serviceClientDeviceTwinHandle->keyName)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("STRING_construct failed for keyName"); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_020: [ IoTHubDeviceTwin_GetTwin shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + else if ((httpHeader = createHttpHeader(iotHubTwinRequestMode)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("HttpHeader creation failed"); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_021: [ IoTHubDeviceTwin_GetTwin shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ]*/ + else if ((httpExApiSasHandle = HTTPAPIEX_SAS_Create(accessKey, uriResource, keyName)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_025: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_GetTwin shall fail and return IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR ]*/ + LogError("HTTPAPIEX_SAS_Create failed"); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_022: [ IoTHubDeviceTwin_GetTwin shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ]*/ + else if ((httpExApiHandle = HTTPAPIEX_Create(serviceClientDeviceTwinHandle->hostname)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_025: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("HTTPAPIEX_Create failed"); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + result = IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR; + } + else + { + HTTPAPI_REQUEST_TYPE httpApiRequestType = HTTPAPI_REQUEST_GET; + STRING_HANDLE relativePath; + unsigned int statusCode = 0; + unsigned char is_error = 0; + + //IOTHUB_TWIN_REQUEST_GET GET {iot hub}/twins/{device id} // Get device twin + //IOTHUB_TWIN_REQUEST_UPDATE PATCH {iot hub}/twins/{device id} // Partally update device twin + //IOTHUB_TWIN_REQUEST_REPLACE_TAGS PUT {iot hub}/twins/{device id}/tags // Replace update tags + //IOTHUB_TWIN_REQUEST_REPLACE_DESIRED PUT {iot hub}/twins/{device id}/properties/desired // Replace update desired properties + //IOTHUB_TWIN_REQUEST_UPDATE_DESIRED PATCH {iot hub}/twins/{device id}/properties/desired // Partially update desired properties + + if ((iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_REPLACE_TAGS) || (iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_REPLACE_DESIRED)) + { + httpApiRequestType = HTTPAPI_REQUEST_PUT; + } + else if ((iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_UPDATE) || (iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_UPDATE_DESIRED)) + { + httpApiRequestType = HTTPAPI_REQUEST_PATCH; + } + else if (iotHubTwinRequestMode == IOTHUB_TWIN_REQUEST_GET) + { + httpApiRequestType = HTTPAPI_REQUEST_GET; + } + else + { + is_error = 1; + } + + if (is_error) + { + LogError("Invalid request type"); + result = IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR; + } + else + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_019: [ IoTHubDeviceTwin_GetTwin shall create HTTP GET request URL using the given deviceId using the following format: url/twins/[deviceId] ]*/ + if ((relativePath = createRelativePath(iotHubTwinRequestMode, deviceName, moduleId)) == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("Failure creating relative path"); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_023: [ IoTHubDeviceTwin_GetTwin shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ]*/ + else if (HTTPAPIEX_SAS_ExecuteRequest(httpExApiSasHandle, httpExApiHandle, httpApiRequestType, STRING_c_str(relativePath), httpHeader, deviceJsonBuffer, &statusCode, NULL, responseBuffer) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_025: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("HTTPAPIEX_SAS_ExecuteRequest failed"); + STRING_delete(relativePath); + result = IOTHUB_DEVICE_TWIN_HTTPAPI_ERROR; + } + else + { + STRING_delete(relativePath); + if (statusCode == 200) + { + /*CodesSRS_IOTHUBDEVICETWIN_12_030: [ Otherwise IoTHubDeviceTwin_GetTwin shall save the received deviceTwin to the out parameter and return with it ]*/ + result = IOTHUB_DEVICE_TWIN_OK; + } + else + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_026: [ IoTHubDeviceTwin_GetTwin shall verify the received HTTP status code and if it is not equal to 200 then return NULL ]*/ + LogError("Http Failure status code %d.", statusCode); + result = IOTHUB_DEVICE_TWIN_ERROR; + } + } + } + HTTPAPIEX_Destroy(httpExApiHandle); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + HTTPHeaders_Free(httpHeader); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + } + return result; +} + +static void free_devicetwin_handle(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN* deviceTwin) +{ + free(deviceTwin->hostname); + free(deviceTwin->sharedAccessKey); + free(deviceTwin->keyName); + free(deviceTwin); +} + +IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE IoTHubDeviceTwin_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE result; + + /*Codes_SRS_IOTHUBDEVICETWIN_12_001: [ If the serviceClientHandle input parameter is NULL IoTHubDeviceTwin_Create shall return NULL ]*/ + if (serviceClientHandle == NULL) + { + LogError("serviceClientHandle input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_002: [ If any member of the serviceClientHandle input parameter is NULL IoTHubDeviceTwin_Create shall return NULL ]*/ + IOTHUB_SERVICE_CLIENT_AUTH* serviceClientAuth = (IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle; + + if (serviceClientAuth->hostname == NULL) + { + LogError("authInfo->hostName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubName == NULL) + { + LogError("authInfo->iothubName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubSuffix == NULL) + { + LogError("authInfo->iothubSuffix input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->keyName == NULL) + { + LogError("authInfo->keyName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->sharedAccessKey == NULL) + { + LogError("authInfo->sharedAccessKey input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_003: [ IoTHubDeviceTwin_Create shall allocate memory for a new IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE instance ]*/ + result = malloc(sizeof(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_004: [ If the allocation failed, IoTHubDeviceTwin_Create shall return NULL ]*/ + LogError("Malloc failed for IOTHUB_SERVICE_CLIENT_DEVICE_TWIN"); + } + else + { + memset(result, 0, sizeof(*result)); + + /*Codes_SRS_IOTHUBDEVICETWIN_12_005: [ If the allocation successful, IoTHubDeviceTwin_Create shall create a IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE from the given IOTHUB_SERVICE_CLIENT_AUTH_HANDLE and return with it ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_006: [ IoTHubDeviceTwin_Create shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s. ]*/ + if (mallocAndStrcpy_s(&result->hostname, serviceClientAuth->hostname) != 0) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_007: [ If the mallocAndStrcpy_s fails, IoTHubDeviceTwin_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for hostName"); + free_devicetwin_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_012: [ IoTHubDeviceTwin_Create shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, serviceClientAuth->sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_013: [ If the mallocAndStrcpy_s fails, IoTHubDeviceTwin_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_devicetwin_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_014: [ IoTHubDeviceTwin_Create shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. ]*/ + else if (mallocAndStrcpy_s(&result->keyName, serviceClientAuth->keyName) != 0) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_015: [ If the mallocAndStrcpy_s fails, IoTHubDeviceTwin_Create shall do clean up and return NULL. ]*/ + LogError("mallocAndStrcpy_s failed for keyName"); + free_devicetwin_handle(result); + result = NULL; + } + } + } + } + return result; +} + +void IoTHubDeviceTwin_Destroy(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle) +{ + /*Codes_SRS_IOTHUBDEVICETWIN_12_016: [ If the serviceClientDeviceTwinHandle input parameter is NULL IoTHubDeviceTwin_Destroy shall return ]*/ + if (serviceClientDeviceTwinHandle != NULL) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_017: [ If the serviceClientDeviceTwinHandle input parameter is not NULL IoTHubDeviceTwin_Destroy shall free the memory of it and return ]*/ + free_devicetwin_handle((IOTHUB_SERVICE_CLIENT_DEVICE_TWIN*)serviceClientDeviceTwinHandle); + } +} + +static int malloc_and_copy_uchar(char** strDestination, BUFFER_HANDLE strSource) +{ + int result; + if ((strDestination == NULL) || (strSource == NULL)) + { + /* If strDestination or strSource is a NULL pointer[...] function return line number where error is spotted */ + LogError("invalid parameter strDestination or strSource"); + result = __FAILURE__; + } + else + { + size_t buffer_size = BUFFER_length(strSource); + char *temp = malloc(buffer_size + 1); + if (temp == NULL) + { + LogError("failed to malloc"); + result = __FAILURE__; + } + else + { + *strDestination = memcpy(temp, BUFFER_u_char(strSource), buffer_size); + temp[buffer_size] = '\0'; + result = 0; + } + } + return result; +} + +char* IoTHubDeviceTwin_GetDeviceOrModuleTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId, const char* moduleId) +{ + char* result; + + /*Codes_SRS_IOTHUBDEVICETWIN_12_018: [ IoTHubDeviceTwin_GetTwin shall verify the input parameters and if any of them are NULL then return IOTHUB_DEVICE_TWIN_INVALID_ARG ]*/ + if ((serviceClientDeviceTwinHandle == NULL) || (deviceId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = NULL; + } + else + { + BUFFER_HANDLE responseBuffer; + + if ((responseBuffer = BUFFER_new()) == NULL) + { + LogError("BUFFER_new failed for responseBuffer"); + result = NULL; + } + /*Codes_SRS_IOTHUBDEVICETWIN_12_019: [ IoTHubDeviceTwin_GetTwin shall create HTTP GET request URL using the given deviceId using the following format: url/twins/[deviceId] ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_020: [ IoTHubDeviceTwin_GetTwin shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_021: [ IoTHubDeviceTwin_GetTwin shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_022: [ IoTHubDeviceTwin_GetTwin shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_023: [ IoTHubDeviceTwin_GetTwin shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ]*/ + else if (sendHttpRequestTwin(serviceClientDeviceTwinHandle, IOTHUB_TWIN_REQUEST_GET, deviceId, moduleId, NULL, responseBuffer) != IOTHUB_DEVICE_TWIN_OK) + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_024: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + /*Codes_SRS_IOTHUBDEVICETWIN_12_025: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_GetTwin shall fail and return NULL ]*/ + LogError("Failure sending HTTP request for create device"); + BUFFER_delete(responseBuffer); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBDEVICETWIN_12_030: [ Otherwise IoTHubDeviceTwin_GetTwin shall save the received `deviceTwin` to the out parameter and return with it ]*/ + if (malloc_and_copy_uchar(&result, responseBuffer) != 0) + { + LogError("failed to copy response"); + result = NULL; + } + BUFFER_delete(responseBuffer); + } + } + return result; + +} + +char* IoTHubDeviceTwin_GetTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId) +{ + return IoTHubDeviceTwin_GetDeviceOrModuleTwin(serviceClientDeviceTwinHandle, deviceId, NULL); +} + +char* IoTHubDeviceTwin_GetModuleTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId, const char* moduleId) +{ + char* result; + if ((serviceClientDeviceTwinHandle == NULL) || (deviceId == NULL) || (moduleId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = NULL; + } + else + { + result = IoTHubDeviceTwin_GetDeviceOrModuleTwin(serviceClientDeviceTwinHandle, deviceId, moduleId); + } + + return result; +} + +char* IoTHubDeviceTwin_UpdateDeviceOrModuleTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId, const char* moduleId, const char* deviceTwinJson) +{ + char* result; + + /*CodesSRS_IOTHUBDEVICETWIN_12_031: [ IoTHubDeviceTwin_UpdateTwin shall verify the input parameters and if any of them are NULL then return NULL ]*/ + if ((serviceClientDeviceTwinHandle == NULL) || (deviceId == NULL) || (deviceTwinJson == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = NULL; + } + else + { + BUFFER_HANDLE updateJson; + BUFFER_HANDLE responseBuffer; + + /*CodesSRS_IOTHUBDEVICETWIN_12_032: [ IoTHubDeviceTwin_UpdateTwin shall create a BUFFER_HANDLE from deviceTwinJson by calling BUFFER_create ]*/ + if ((updateJson = BUFFER_create((const unsigned char*)deviceTwinJson, strlen(deviceTwinJson))) == NULL) + { + /*CodesSRS_IOTHUBDEVICETWIN_12_033: [ If the creation fails, IoTHubDeviceTwin_UpdateTwin shall return NULL ]*/ + LogError("BUFFER_create failed for deviceTwinJson"); + result = NULL; + } + /*CodesSRS_IOTHUBDEVICETWIN_12_034: [ IoTHubDeviceTwin_UpdateTwin shall allocate memory for response buffer by calling BUFFER_new ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_035: [ If the allocation failed, IoTHubDeviceTwin_UpdateTwin shall return NULL ]*/ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*CodesSRS_IOTHUBDEVICETWIN_12_034: [ If the creation fails, IoTHubDeviceTwin_UpdateTwin shall return NULL ]*/ + LogError("BUFFER_new failed for responseBuffer"); + BUFFER_delete(updateJson); + result = NULL; + } + /*CodesSRS_IOTHUBDEVICETWIN_12_039: [ IoTHubDeviceTwin_UpdateTwin shall create an HTTP PATCH request using deviceTwinJson ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_040: [ IoTHubDeviceTwin_UpdateTwin shall create an HTTP PATCH request using the createdfollowing HTTP headers: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_041: [ IoTHubDeviceTwin_UpdateTwin shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_042: [ IoTHubDeviceTwin_UpdateTwin shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_043: [ IoTHubDeviceTwin_UpdateTwin shall execute the HTTP PATCH request by calling HTTPAPIEX_ExecuteRequest ]*/ + else if (sendHttpRequestTwin(serviceClientDeviceTwinHandle, IOTHUB_TWIN_REQUEST_UPDATE, deviceId, moduleId, updateJson, responseBuffer) != IOTHUB_DEVICE_TWIN_OK) + { + /*CodesSRS_IOTHUBDEVICETWIN_12_044: [ If any of the call fails during the HTTP creation IoTHubDeviceTwin_UpdateTwin shall fail and return NULL ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_045: [ If any of the HTTPAPI call fails IoTHubDeviceTwin_UpdateTwin shall fail and return NULL ]*/ + /*CodesSRS_IOTHUBDEVICETWIN_12_046: [ IoTHubDeviceTwin_UpdateTwin shall verify the received HTTP status code and if it is not equal to 200 then return NULL ]*/ + LogError("Failure sending HTTP request for create device"); + BUFFER_delete(responseBuffer); + BUFFER_delete(updateJson); + result = NULL; + } + else + { + /*CodesSRS_IOTHUBDEVICETWIN_12_047: [ Otherwise IoTHubDeviceTwin_UpdateTwin shall save the received updated device twin to the out parameter and return with it ]*/ + if (malloc_and_copy_uchar(&result, responseBuffer) != 0) + { + LogError("failed to copy response"); + result = NULL; + } + BUFFER_delete(responseBuffer); + BUFFER_delete(updateJson); + } + } + return result; + +} + +char* IoTHubDeviceTwin_UpdateTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId, const char* deviceTwinJson) +{ + return IoTHubDeviceTwin_UpdateDeviceOrModuleTwin(serviceClientDeviceTwinHandle, deviceId, NULL, deviceTwinJson); +} + +char* IoTHubDeviceTwin_UpdateModuleTwin(IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE serviceClientDeviceTwinHandle, const char* deviceId, const char* moduleId, const char* moduleTwinJson) +{ + char* result; + if ((serviceClientDeviceTwinHandle == NULL) || (deviceId == NULL) || (moduleId == NULL) || (moduleTwinJson == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = NULL; + } + else + { + result = IoTHubDeviceTwin_UpdateDeviceOrModuleTwin(serviceClientDeviceTwinHandle, deviceId, moduleId, moduleTwinJson); + } + + return result; +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_messaging.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,328 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/sastoken.h" + +#include "azure_uamqp_c/connection.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/sasl_mechanism.h" +#include "azure_uamqp_c/saslclientio.h" +#include "azure_uamqp_c/sasl_plain.h" +#include "azure_uamqp_c/cbs.h" +#include <signal.h> +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/lock.h" + +#include "parson.h" + +#include "iothub_messaging_ll.h" +#include "iothub_messaging.h" + +typedef struct IOTHUB_MESSAGING_CLIENT_INSTANCE_TAG +{ + IOTHUB_MESSAGING_HANDLE IoTHubMessagingHandle; + THREAD_HANDLE ThreadHandle; + LOCK_HANDLE LockHandle; + sig_atomic_t StopThread; +} IOTHUB_MESSAGING_CLIENT_INSTANCE; + +static int ScheduleWork_Thread(void* threadArgument) +{ + IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)threadArgument; + + while (1) + { + if (Lock(iotHubMessagingClientInstance->LockHandle) == LOCK_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_041: [ The thread shall exit when all IoTHubServiceClients using the thread have had IoTHubMessaging_Destroy called. ]*/ + if (iotHubMessagingClientInstance->StopThread) + { + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + break; /*gets out of the thread*/ + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_042: [ The thread created by IoTHubMessaging_SendAsync shall call IoTHubMessaging_LL_DoWork every 1 ms. ]*/ + /*Codes_SRS_IOTHUBMESSAGING_12_043: [ All calls to IoTHubMessaging_LL_DoWork shall be protected by the lock created in IoTHubMessaging_Create. ]*/ + IoTHubMessaging_LL_DoWork(iotHubMessagingClientInstance->IoTHubMessagingHandle); + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + } + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_044: [ If acquiring the lock fails, `IoTHubMessaging_LL_DoWork` shall not be called. ]*/ + LogError("Lock failed, shall retry"); + } + (void)ThreadAPI_Sleep(1); + } + + ThreadAPI_Exit(0); + return 0; +} + +static IOTHUB_MESSAGING_RESULT StartWorkerThreadIfNeeded(IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance) +{ + IOTHUB_MESSAGING_RESULT result; + if (iotHubMessagingClientInstance->ThreadHandle == NULL) + { + iotHubMessagingClientInstance->StopThread = 0; + if (ThreadAPI_Create(&iotHubMessagingClientInstance->ThreadHandle, ScheduleWork_Thread, iotHubMessagingClientInstance) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + result = IOTHUB_MESSAGING_OK; + } + } + else + { + result = IOTHUB_MESSAGING_OK; + } + return result; +} + +IOTHUB_MESSAGING_CLIENT_HANDLE IoTHubMessaging_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_MESSAGING_CLIENT_INSTANCE* result; + + /*Codes_SRS_IOTHUBMESSAGING_12_001: [ IoTHubMessaging_Create shall verify the serviceClientHandle input parameter and if it is NULL then return NULL. ]*/ + if (serviceClientHandle == NULL) + { + LogError("serviceClientHandle input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_002: [ IoTHubMessaging_Create shall allocate a new IoTHubMessagingClient instance. ]*/ + if ((result = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)malloc(sizeof(IOTHUB_MESSAGING_CLIENT_INSTANCE))) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_003: [If allocating memory for the new IoTHubMessagingClient instance fails, then IoTHubMessaging_Create shall return NULL. ]*/ + LogError("malloc failed for IoTHubMessagingClient"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_004: [IoTHubMessaging_Create shall create a lock object to be used later for serializing IoTHubMessagingClient calls. ]*/ + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_005: [If creating the lock fails, then IoTHubMessaging_Create shall return NULL. ]*/ + /*Codes_SRS_IOTHUBMESSAGING_12_008: [If IoTHubMessaging_Create fails, all resources allocated by it shall be freed. ]*/ + LogError("Lock_Init failed"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_006: [IoTHubMessaging_Create shall instantiate a new IoTHubMessaging_LL instance by calling IoTHubMessaging_LL_Create and passing the serviceClientHandle argument. ]*/ + result->IoTHubMessagingHandle = IoTHubMessaging_LL_Create(serviceClientHandle); + if (result->IoTHubMessagingHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_007: [ If IoTHubMessaging_LL_Create fails, then IoTHubMessaging_Create shall return NULL. ]*/ + /*Codes_SRS_IOTHUBMESSAGING_12_008: [If IoTHubMessaging_Create fails, all resources allocated by it shall be freed. ]*/ + LogError("IoTHubMessaging_LL_Create failed"); + Lock_Deinit(result->LockHandle); + free(result); + result = NULL; + } + else + { + result->StopThread = 0; + result->ThreadHandle = NULL; + } + } + } + } + return (IOTHUB_MESSAGING_CLIENT_HANDLE)result; +} + +void IoTHubMessaging_Destroy(IOTHUB_MESSAGING_CLIENT_HANDLE messagingClientHandle) +{ + /*Codes_SRS_IOTHUBMESSAGING_12_009: [ IoTHubMessaging_Destroy shall do nothing if parameter messagingClientHandle is NULL. ]*/ + if (messagingClientHandle == NULL) + { + LogError("messagingClientHandle input parameter is NULL"); + } + else + { + IOTHUB_MESSAGING_CLIENT_INSTANCE* messagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)messagingClientHandle; + + /*Codes_SRS_IOTHUBMESSAGING_12_011: [ IoTHubMessaging_Destroy shall destroy IoTHubMessagingHandle by call IoTHubMessaging_LL_Destroy. ]*/ + IoTHubMessaging_LL_Destroy(messagingClientInstance->IoTHubMessagingHandle); + + /*Codes_SRS_IOTHUBMESSAGING_12_014: [ If the lock was allocated in IoTHubMessaging_Create, it shall be also freed. ]*/ + Lock_Deinit(messagingClientInstance->LockHandle); + + free(messagingClientInstance); + } +} + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_Open(IOTHUB_MESSAGING_CLIENT_HANDLE messagingClientHandle, IOTHUB_OPEN_COMPLETE_CALLBACK openCompleteCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + if (messagingClientHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_015: [ If messagingClientHandle is NULL, IoTHubMessaging_Open shall return IOTHUB_MESSAGING_INVALID_ARG. ]*/ + LogError("NULL messagingClientHandle"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)messagingClientHandle; + + /*Codes_SRS_IOTHUBMESSAGING_12_016: [ IoTHubMessaging_Open shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + if (Lock(iotHubMessagingClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_017: [ If acquiring the lock fails, IoTHubMessaging_Open shall return IOTHUB_MESSAGING_ERROR. ]*/ + LogError("Could not acquire lock"); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_018: [ IoTHubMessaging_Open shall call IoTHubMessaging_LL_Open, while passing the IOTHUB_MESSAGING_HANDLE handle created by IoTHubMessaging_Create and the parameters openCompleteCallback and userContextCallback. ]*/ + /*Codes_SRS_IOTHUBMESSAGING_12_019: [ When IoTHubMessaging_LL_Open is called, IoTHubMessaging_Open shall return the result of IoTHubMessaging_LL_Open. ]*/ + result = IoTHubMessaging_LL_Open(messagingClientHandle->IoTHubMessagingHandle, openCompleteCallback, userContextCallback); + + /*Codes_SRS_IOTHUBMESSAGING_12_016: [ IoTHubMessaging_Open shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + } + } + + return result; +} + +void IoTHubMessaging_Close(IOTHUB_MESSAGING_CLIENT_HANDLE messagingClientHandle) +{ + if (messagingClientHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_021: [ If messagingClientHandle is NULL, IoTHubMessaging_Close shall return IOTHUB_MESSAGING_INVALID_ARG. ]*/ + LogError("NULL messagingClientHandle"); + } + else + { + IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)messagingClientHandle; + + /*Codes_SRS_IOTHUBMESSAGING_12_022: [ IoTHubMessaging_Close shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + if (Lock(iotHubMessagingClientInstance->LockHandle) != LOCK_OK) + { + LogError("Could not acquire lock"); + iotHubMessagingClientInstance->StopThread = 1; /*setting it even when Lock fails*/ + } + else + { + iotHubMessagingClientInstance->StopThread = 1; + + /*Codes_SRS_IOTHUBMESSAGING_12_022: [ IoTHubMessaging_Close shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + } + + if (iotHubMessagingClientInstance->ThreadHandle != NULL) + { + int res; + /*Codes_SRS_IOTHUBMESSAGING_12_013: [ The thread created as part of executing IoTHubMessaging_SendAsync shall be joined. ]*/ + if (ThreadAPI_Join(iotHubMessagingClientInstance->ThreadHandle, &res) != THREADAPI_OK) + { + LogError("ThreadAPI_Join failed"); + } + } + + /*Codes_SRS_IOTHUBMESSAGING_12_024: [ IoTHubMessaging_Close shall call IoTHubMessaging_LL_Close, while passing the IOTHUB_MESSAGING_HANDLE handle created by IoTHubMessaging_Create ]*/ + IoTHubMessaging_LL_Close(messagingClientHandle->IoTHubMessagingHandle); + } +} + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_SetFeedbackMessageCallback(IOTHUB_MESSAGING_CLIENT_HANDLE messagingClientHandle, IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK feedbackMessageReceivedCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + if (messagingClientHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_027: [ If messagingClientHandle is NULL, IoTHubMessaging_SetFeedbackMessageCallback shall return IOTHUB_MESSAGING_INVALID_ARG. ]*/ + LogError("NULL messagingClientHandle"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)messagingClientHandle; + + /*Codes_SRS_IOTHUBMESSAGING_12_028: [ IoTHubMessaging_SetFeedbackMessageCallback shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + if (Lock(iotHubMessagingClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_029: [ If acquiring the lock fails, IoTHubMessaging_SetFeedbackMessageCallback shall return IOTHUB_MESSAGING_ERROR. ]*/ + LogError("Could not acquire lock"); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ IoTHubMessaging_SetFeedbackMessageCallback shall call IoTHubMessaging_LL_SetFeedbackMessageCallback, while passing the IOTHUB_MESSAGING_HANDLE handle created by IoTHubMessaging_Create, feedbackMessageReceivedCallback and userContextCallback ]*/ + /*Codes_SRS_IOTHUBMESSAGING_12_031: [ When IoTHubMessaging_LL_SetFeedbackMessageCallback is called, IoTHubMessaging_SetFeedbackMessageCallback shall return the result of IoTHubMessaging_LL_SetFeedbackMessageCallback. ]*/ + result = IoTHubMessaging_LL_SetFeedbackMessageCallback(messagingClientHandle->IoTHubMessagingHandle, feedbackMessageReceivedCallback, userContextCallback); + + /*Codes_SRS_IOTHUBMESSAGING_12_032: [ IoTHubMessaging_SetFeedbackMessageCallback shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + } + } + + return result; +} + + + + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_SendAsync(IOTHUB_MESSAGING_CLIENT_HANDLE messagingClientHandle, const char* deviceId, IOTHUB_MESSAGE_HANDLE message, IOTHUB_SEND_COMPLETE_CALLBACK sendCompleteCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + if (messagingClientHandle == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_033: [ If messagingClientHandle is NULL, IoTHubMessaging_SendAsync shall return IOTHUB_MESSAGING_INVALID_ARG. ]*/ + LogError("NULL iothubClientHandle"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + IOTHUB_MESSAGING_CLIENT_INSTANCE* iotHubMessagingClientInstance = (IOTHUB_MESSAGING_CLIENT_INSTANCE*)messagingClientHandle; + + /*Codes_SRS_IOTHUBMESSAGING_12_034: [ IoTHubMessaging_SendAsync shall be made thread-safe by using the lock created in IoTHubMessaging_Create. ]*/ + if (Lock(iotHubMessagingClientInstance->LockHandle) != LOCK_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_035: [ If acquiring the lock fails, IoTHubMessaging_SendAsync shall return IOTHUB_MESSAGING_ERROR. ]*/ + LogError("Could not acquire lock"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_036: [ IoTHubClient_SendEventAsync shall start the worker thread if it was not previously started. ]*/ + if ((result = StartWorkerThreadIfNeeded(iotHubMessagingClientInstance)) != IOTHUB_MESSAGING_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_037: [ If starting the thread fails, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_ERROR. ]*/ + LogError("Could not start worker thread"); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_038: [ IoTHubMessaging_SendAsync shall call IoTHubMessaging_LL_Send, while passing the IOTHUB_MESSAGING_HANDLE handle created by IoTHubClient_Create and the parameters deviceId, message, sendCompleteCallback and userContextCallback.*/ + /*Codes_SRS_IOTHUBMESSAGING_12_039: [ When IoTHubMessaging_LL_Send is called, IoTHubMessaging_SendAsync shall return the result of IoTHubMessaging_LL_Send. ]*/ + result = IoTHubMessaging_LL_Send(iotHubMessagingClientInstance->IoTHubMessagingHandle, deviceId, message, sendCompleteCallback, userContextCallback); + } + + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create. ]*/ + (void)Unlock(iotHubMessagingClientInstance->LockHandle); + } + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_messaging_ll.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1562 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/sastoken.h" + +#include "azure_uamqp_c/connection.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/sasl_mechanism.h" +#include "azure_uamqp_c/saslclientio.h" +#include "azure_uamqp_c/sasl_plain.h" +#include "azure_uamqp_c/cbs.h" + +#include "parson.h" + +#include "iothub_messaging_ll.h" +#include "iothub_sc_version.h" + +DEFINE_ENUM_STRINGS(IOTHUB_FEEDBACK_STATUS_CODE, IOTHUB_FEEDBACK_STATUS_CODE_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_MESSAGE_SEND_STATE, IOTHUB_MESSAGE_SEND_STATE_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_RESULT_VALUES); + +typedef struct CALLBACK_DATA_TAG +{ + IOTHUB_OPEN_COMPLETE_CALLBACK openCompleteCompleteCallback; + IOTHUB_SEND_COMPLETE_CALLBACK sendCompleteCallback; + IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK feedbackMessageCallback; + void* openUserContext; + void* sendUserContext; + void* feedbackUserContext; +} CALLBACK_DATA; + +typedef struct IOTHUB_MESSAGING_TAG +{ + int isOpened; + char* hostname; + char* iothubName; + char* iothubSuffix; + char* sharedAccessKey; + char* keyName; + + MESSAGE_SENDER_HANDLE message_sender; + MESSAGE_RECEIVER_HANDLE message_receiver; + CONNECTION_HANDLE connection; + SESSION_HANDLE session; + LINK_HANDLE sender_link; + LINK_HANDLE receiver_link; + SASL_MECHANISM_HANDLE sasl_mechanism_handle; + SASL_PLAIN_CONFIG sasl_plain_config; + XIO_HANDLE tls_io; + XIO_HANDLE sasl_io; + + MESSAGE_SENDER_STATE message_sender_state; + MESSAGE_RECEIVER_STATE message_receiver_state; + + CALLBACK_DATA* callback_data; +} IOTHUB_MESSAGING; + + +static const char* const FEEDBACK_RECORD_KEY_DEVICE_ID = "deviceId"; +static const char* const FEEDBACK_RECORD_KEY_DEVICE_GENERATION_ID = "deviceGenerationId"; +static const char* const FEEDBACK_RECORD_KEY_DESCRIPTION = "description"; +static const char* const FEEDBACK_RECORD_KEY_ENQUED_TIME_UTC = "enqueuedTimeUtc"; +static const char* const FEEDBACK_RECORD_KEY_ORIGINAL_MESSAGE_ID = "originalMessageId"; +static const char* const AMQP_ADDRESS_PATH_FMT = "/devices/%s/messages/deviceBound"; +static const char* const AMQP_ADDRESS_PATH_MODULE_FMT = "/devices/%s/modules/%s/messages/deviceBound"; + +static int setMessageId(IOTHUB_MESSAGE_HANDLE iothub_message_handle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* messageId; + + if ((messageId = IoTHubMessage_GetMessageId(iothub_message_handle)) != NULL) + { + AMQP_VALUE uamqp_message_id; + if ((uamqp_message_id = amqpvalue_create_string(messageId)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create an AMQP_VALUE for the messageId property value."); + result = __FAILURE__; + } + else + { + int api_call_result; + + /* Codes_SRS_IOTHUBMESSAGING_12_083: [ The message-id AMQP_VALUE shall be set on the uAMQP message using properties_set_message_id ] */ + if ((api_call_result = properties_set_message_id(uamqp_message_properties, uamqp_message_id)) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogInfo("Failed to set value of uAMQP message 'message-id' property (%d).", api_call_result); + result = __FAILURE__; + } + else + { + result = 0; + } + amqpvalue_destroy(uamqp_message_id); + } + } + else + { + result = 0; + } + + return result; +} + +static int setCorrelationId(IOTHUB_MESSAGE_HANDLE iothub_message_handle, PROPERTIES_HANDLE uamqp_message_properties) +{ + int result; + const char* correlationId; + + if ((correlationId = IoTHubMessage_GetCorrelationId(iothub_message_handle)) != NULL) + { + AMQP_VALUE uamqp_correlation_id; + if ((uamqp_correlation_id = amqpvalue_create_string(correlationId)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create an AMQP_VALUE for the messageId property value."); + result = __FAILURE__; + } + else + { + int api_call_result; + + /*Codes_SRS_IOTHUBMESSAGING_12_086: [ The correlation-id AMQP_VALUE shall be set on the uAMQP message using properties_set_correlation_id ] */ + if ((api_call_result = properties_set_correlation_id(uamqp_message_properties, uamqp_correlation_id)) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogInfo("Failed to set value of uAMQP message 'message-id' property (%d).", api_call_result); + result = __FAILURE__; + } + else + { + result = 0; + } + amqpvalue_destroy(uamqp_correlation_id); + } + } + else + { + result = 0; + } + + return result; +} + +static int addPropertiesToAMQPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, MESSAGE_HANDLE uamqp_message, AMQP_VALUE to_amqp_value) +{ + int result; + PROPERTIES_HANDLE uamqp_message_properties = NULL; /* This initialization is forced by Valgrind */ + int api_call_result; + + /*Codes_SRS_IOTHUBMESSAGING_12_079: [ The uAMQP message properties shall be retrieved using message_get_properties ] */ + if ((api_call_result = message_get_properties(uamqp_message, &uamqp_message_properties)) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to get properties map from uAMQP message (error code %d).", api_call_result); + result = __FAILURE__; + } + /*Codes_SRS_IOTHUBMESSAGING_12_080: [ If UAMQP message properties were not present then a new properties container shall be created using properties_create ] */ + else if (uamqp_message_properties == NULL && + (uamqp_message_properties = properties_create()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create properties map for uAMQP message (error code %d).", api_call_result); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_081: [ Message-id from the IOTHUB_MESSAGE shall be read using IoTHubMessage_GetMessageId ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_082: [ As message-id is optional field, if it is not set on the IOTHUB_MESSAGE, message_create_from_iothub_message shall ignore it and continue normally ] */ + if (setMessageId(iothub_message_handle, uamqp_message_properties) != 0) + { + LogError("Failed to set uampq messageId."); + result = __FAILURE__; + } + /*Codes_SRS_IOTHUBMESSAGING_12_084: [ Correlation-id from the IOTHUB_MESSAGE shall be read using IoTHubMessage_GetCorrelationId ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_085: [ As correlation-id is optional field, if it is not set on the IOTHUB_MESSAGE, message_create_from_iothub_message() shall ignore it and continue normally ] */ + else if (setCorrelationId(iothub_message_handle, uamqp_message_properties) != 0) + { + LogError("Failed to set uampq correlationId."); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_87: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message TO property to the given message properties by calling properties_set_to ] */ + if ((properties_set_to(uamqp_message_properties, to_amqp_value)) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create properties for message - properties_set_to failed"); + result = __FAILURE__; + } + /*Codes_SRS_IOTHUBMESSAGING_12_038: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message properties to the given message properties by calling message_set_properties ] */ + else if ((api_call_result = message_set_properties(uamqp_message, uamqp_message_properties)) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to set properties map on uAMQP message (error code %d).", api_call_result); + result = __FAILURE__; + } + else + { + result = 0; + } + } + properties_destroy(uamqp_message_properties); + } + return result; +} + +static int addApplicationPropertiesToAMQPMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, MESSAGE_HANDLE uamqp_message) +{ + int result; + MAP_HANDLE properties_map; + const char* const* propertyKeys; + const char* const* propertyValues; + size_t propertyCount = 0; + + /*Codes_SRS_IOTHUBMESSAGING_12_088: [ The IOTHUB_MESSAGE_HANDLE properties shall be obtained by calling IoTHubMessage_Properties ] */ + properties_map = IoTHubMessage_Properties(iothub_message_handle); + + /*Codes_SRS_IOTHUBMESSAGING_12_089: [ The actual keys and values, as well as the number of properties shall be obtained by calling Map_GetInternals on the handle obtained from IoTHubMessage_Properties ] */ + if (Map_GetInternals(properties_map, &propertyKeys, &propertyValues, &propertyCount) != MAP_OK) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to get the internals of the property map."); + result = __FAILURE__; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_090: [ If the number of properties is greater than 0, message_create_from_iothub_message() shall iterate through all the properties and add them to the uAMQP message ] */ + if (propertyCount != 0) + { + AMQP_VALUE uamqp_map; + + /*Codes_SRS_IOTHUBMESSAGING_12_091: [ A uAMQP property map shall be created by calling amqpvalue_create_map ] */ + if ((uamqp_map = amqpvalue_create_map()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create uAMQP map for the properties."); + result = __FAILURE__; + } + else + { + size_t i = 0; + for (i = 0; i < propertyCount; i++) + { + AMQP_VALUE map_key_value = NULL; + AMQP_VALUE map_value_value = NULL; + + /*Codes_SRS_IOTHUBMESSAGING_12_092: [ An AMQP_VALUE instance shall be created using amqpvalue_create_string() to hold each uAMQP property name ] */ + if ((map_key_value = amqpvalue_create_string(propertyKeys[i])) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create uAMQP property key name."); + break; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_093: [ An AMQP_VALUE instance shall be created using amqpvalue_create_string() to hold each uAMQP property value ] */ + if ((map_value_value = amqpvalue_create_string(propertyValues[i])) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to create uAMQP property key value."); + break; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_094: [ The property name and value (AMQP_VALUE instances) shall be added to the uAMQP property map by calling amqpvalue_map_set_value ] */ + if (amqpvalue_set_map_value(uamqp_map, map_key_value, map_value_value) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to set key/value into the the uAMQP property map."); + break; + } + amqpvalue_destroy(map_value_value); + } + /*Codes_SRS_IOTHUBMESSAGING_12_095: [ After adding the property name and value to the uAMQP property map, both AMQP_VALUE instances shall be destroyed using amqpvalue_destroy ] */ + amqpvalue_destroy(map_key_value); + } + } + + if (i == propertyCount) + { + /*Codes_SRS_IOTHUBMESSAGING_12_096: [ If no errors occurred processing the properties, the uAMQP properties map shall be set on the uAMQP message by calling message_set_application_properties ] */ + if (message_set_application_properties(uamqp_message, uamqp_map) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed to transfer the message properties to the uAMQP message."); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + LogError("Failed to set application property into the the uAMQP property map."); + result = __FAILURE__; + } + amqpvalue_destroy(uamqp_map); + } + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_097: [ If the number of properties is 0, no application properties shall be set on the uAMQP message and message_create_from_iothub_message() shall return with success ] */ + result = 0; + } + } + return result; +} + +static int getMessageContentAndSize(IOTHUB_MESSAGE_HANDLE message, unsigned const char** messageContent, size_t* messageContentSize) +{ + int result; + unsigned const char* contentByteArr; + const char* contentStr; + size_t contentSize; + + IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message); + + switch (contentType) + { + case IOTHUBMESSAGE_BYTEARRAY: + if (IoTHubMessage_GetByteArray(message, &contentByteArr, &contentSize) != IOTHUB_MESSAGE_OK) + { + LogError("Failed getting the BYTE array representation of the IOTHUB_MESSAGE_HANDLE instance."); + result = __FAILURE__; + } + else + { + *messageContent = contentByteArr; + *messageContentSize = contentSize; + result = 0; + } + break; + case IOTHUBMESSAGE_STRING: + if ((contentStr = IoTHubMessage_GetString(message)) == NULL) + { + LogError("Failed getting the STRING representation of the IOTHUB_MESSAGE_HANDLE instance."); + result = __FAILURE__; + } + else + { + contentSize = strlen(contentStr); + *messageContent = (unsigned const char*)contentStr; + *messageContentSize = contentSize; + result = 0; + } + break; + default: + LogError("Cannot parse IOTHUB_MESSAGE_HANDLE with content type IOTHUBMESSAGE_UNKNOWN."); + result = __FAILURE__; + break; + } + return result; +} + +static char* createSasToken(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + char* result; + + char* buffer = NULL; + if (messagingHandle->sharedAccessKey == NULL) + { + LogError("createSasPlainConfig failed - sharedAccessKey cannot be NULL"); + result = NULL; + } + else if (messagingHandle->hostname == NULL) + { + LogError("createSasPlainConfig failed - hostname cannot be NULL"); + result = NULL; + } + else if (messagingHandle->keyName == NULL) + { + LogError("createSasPlainConfig failed - keyName cannot be NULL"); + result = NULL; + } + else + { + STRING_HANDLE hostName = NULL; + STRING_HANDLE sharedAccessKey = NULL; + STRING_HANDLE keyName = NULL; + + if ((hostName = STRING_construct(messagingHandle->hostname)) == NULL) + { + LogError("STRING_construct failed for hostName"); + result = NULL; + } + else if ((sharedAccessKey = STRING_construct(messagingHandle->sharedAccessKey)) == NULL) + { + LogError("STRING_construct failed for sharedAccessKey"); + result = NULL; + } + else if ((keyName = STRING_construct(messagingHandle->keyName)) == NULL) + { + LogError("STRING_construct failed for keyName"); + result = NULL; + } + else + { + time_t currentTime = time(NULL); + size_t expiry_time = (size_t)(currentTime + (365 * 24 * 60 * 60)); + const char* c_buffer = NULL; + + STRING_HANDLE sasHandle = SASToken_Create(sharedAccessKey, hostName, keyName, expiry_time); + if (sasHandle == NULL) + { + LogError("SASToken_Create failed"); + result = NULL; + } + else if ((c_buffer = (const char*)STRING_c_str(sasHandle)) == NULL) + { + LogError("STRING_c_str returned NULL"); + result = NULL; + } + else if (mallocAndStrcpy_s(&buffer, c_buffer) != 0) + { + LogError("mallocAndStrcpy_s failed for sharedAccessToken"); + result = NULL; + } + else + { + result = buffer; + } + STRING_delete(sasHandle); + } + STRING_delete(keyName); + STRING_delete(sharedAccessKey); + STRING_delete(hostName); + } + return result; +} + +static char* createAuthCid(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + char* result; + + char* buffer = NULL; + if (messagingHandle->iothubName == NULL) + { + LogError("createSasPlainConfig failed - iothubName cannot be NULL"); + result = NULL; + } + else + { + const char* AMQP_SEND_AUTHCID_FMT = "iothubowner@sas.root.%s"; + size_t authCidLen = strlen(AMQP_SEND_AUTHCID_FMT) + strlen(messagingHandle->iothubName); + + if ((buffer = (char*)malloc(authCidLen + 1)) == NULL) + { + LogError("Malloc failed for authCid."); + result = NULL; + } + else if ((snprintf(buffer, authCidLen + 1, AMQP_SEND_AUTHCID_FMT, messagingHandle->iothubName)) < 0) + { + LogError("sprintf_s failed for authCid."); + free(buffer); + result = NULL; + } + else + { + result = buffer; + } + } + return result; +} + +static char* createReceiveTargetAddress(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + char* result; + + char* buffer = NULL; + if (messagingHandle->hostname == NULL) + { + LogError("createSendTargetAddress failed - hostname cannot be NULL"); + result = NULL; + } + else + { + const char* AMQP_SEND_TARGET_ADDRESS_FMT = "amqps://%s/messages/servicebound/feedback"; + size_t addressLen = strlen(AMQP_SEND_TARGET_ADDRESS_FMT) + strlen(messagingHandle->hostname); + + if ((buffer = (char*)malloc(addressLen + 1)) == NULL) + { + LogError("Malloc failed for receiveTargetAddress"); + result = NULL; + } + else if ((snprintf(buffer, addressLen + 1, AMQP_SEND_TARGET_ADDRESS_FMT, messagingHandle->hostname)) < 0) + { + LogError("sprintf_s failed for receiveTargetAddress."); + free((char*)buffer); + result = NULL; + } + else + { + result = buffer; + } + } + return result; +} + +static char* createSendTargetAddress(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + char* result; + + char* buffer = NULL; + if (messagingHandle->hostname == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_077: [ If the messagingHandle->hostname input parameter is NULL IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_INVALID_ARG ] */ + LogError("createSendTargetAddress failed - hostname cannot be NULL"); + result = NULL; + } + else + { + const char* AMQP_SEND_TARGET_ADDRESS_FMT = "amqps://%s/messages/deviceBound"; + size_t addressLen = strlen(AMQP_SEND_TARGET_ADDRESS_FMT) + strlen(messagingHandle->hostname); + + if ((buffer = (char*)malloc(addressLen + 1)) == NULL) + { + LogError("Malloc failed for sendTargetAddress"); + result = NULL; + } + else if ((snprintf(buffer, addressLen + 1, AMQP_SEND_TARGET_ADDRESS_FMT, messagingHandle->hostname)) < 0) + { + LogError("sprintf_s failed for sendTargetAddress."); + free((char*)buffer); + result = NULL; + } + else + { + result = buffer; + } + } + return result; +} + +static char* createDeviceDestinationString(const char* deviceId, const char* moduleId) +{ + char* result; + + if (deviceId == NULL) + { + LogError("createDeviceDestinationString failed - deviceId cannot be NULL"); + result = NULL; + } + else + { + size_t deviceDestLen = strlen(AMQP_ADDRESS_PATH_MODULE_FMT) + strlen(deviceId) + (moduleId == NULL ? 0 : strlen(moduleId)) + 1; + + char* buffer = (char*)malloc(deviceDestLen); + if (buffer == NULL) + { + LogError("Could not create device destination string."); + result = NULL; + } + else + { + if ((moduleId == NULL) && (snprintf(buffer, deviceDestLen, AMQP_ADDRESS_PATH_FMT, deviceId)) < 0) + { + LogError("sprintf_s failed for deviceDestinationString."); + free((char*)buffer); + result = NULL; + } + else if ((moduleId != NULL) && (snprintf(buffer, deviceDestLen, AMQP_ADDRESS_PATH_MODULE_FMT, deviceId, moduleId)) < 0) + { + LogError("sprintf_s failed for deviceDestinationString for module."); + free((char*)buffer); + result = NULL; + } + else + { + result = buffer; + } + } + } + return result; +} + +static void IoTHubMessaging_LL_SenderStateChanged(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state) +{ + (void)previous_state; + if (context != NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_049: [ IoTHubMessaging_LL_SenderStateChanged shall save the new_state to local variable ] */ + IOTHUB_MESSAGING* messagingData = (IOTHUB_MESSAGING*)context; + messagingData->message_sender_state = new_state; + + if ((messagingData->message_sender_state == MESSAGE_SENDER_STATE_OPEN) && (messagingData->message_receiver_state == MESSAGE_RECEIVER_STATE_OPEN)) + { + /*Codes_SRS_IOTHUBMESSAGING_12_050: [ If both sender and receiver state is open IoTHubMessaging_LL_SenderStateChanged shall set the isOpened local variable to true ] */ + messagingData->isOpened = true; + if (messagingData->callback_data->openCompleteCompleteCallback != NULL) + { + (messagingData->callback_data->openCompleteCompleteCallback)(messagingData->callback_data->openUserContext); + } + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_051: [ If neither sender_state nor receiver_state is open IoTHubMessaging_LL_SenderStateChanged shall set the local isOpened variable to false ] */ + messagingData->isOpened = false; + } + } +} + +static void IoTHubMessaging_LL_ReceiverStateChanged(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state) +{ + (void)previous_state; + if (context != NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_052: [ IoTHubMessaging_LL_ReceiverStateChanged shall save the new_state to local variable ] */ + IOTHUB_MESSAGING* messagingData = (IOTHUB_MESSAGING*)context; + messagingData->message_receiver_state = new_state; + + if ((messagingData->message_sender_state == MESSAGE_SENDER_STATE_OPEN) && (messagingData->message_receiver_state == MESSAGE_RECEIVER_STATE_OPEN)) + { + /*Codes_SRS_IOTHUBMESSAGING_12_053: [ If both sender and receiver state is open IoTHubMessaging_LL_ReceiverStateChanged shall set the isOpened local variable to true ] */ + messagingData->isOpened = true; + if (messagingData->callback_data->openCompleteCompleteCallback != NULL) + { + (messagingData->callback_data->openCompleteCompleteCallback)(messagingData->callback_data->openUserContext); + } + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_054: [ If neither sender_state nor receiver_state is open IoTHubMessaging_LL_ReceiverStateChanged shall set the local isOpened variable to false ] */ + messagingData->isOpened = false; + } + } +} + +static void IoTHubMessaging_LL_SendMessageComplete(void* context, IOTHUB_MESSAGING_RESULT send_result) +{ + /*Codes_SRS_IOTHUBMESSAGING_12_056: [ If context is NULL IoTHubMessaging_LL_SendMessageComplete shall return ] */ + if (context != NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_055: [ If context is not NULL and IoTHubMessaging_LL_SendMessageComplete shall call user callback with user context and messaging result ] */ + IOTHUB_MESSAGING* messagingData = (IOTHUB_MESSAGING*)context; + if (messagingData->callback_data->sendCompleteCallback != NULL) + { + (messagingData->callback_data->sendCompleteCallback)(messagingData->callback_data->sendUserContext, send_result); + } + } +} + +static AMQP_VALUE IoTHubMessaging_LL_FeedbackMessageReceived(const void* context, MESSAGE_HANDLE message) +{ + AMQP_VALUE result; + + /*Codes_SRS_IOTHUBMESSAGING_12_057: [ If context is NULL IoTHubMessaging_LL_FeedbackMessageReceived shall do nothing and return delivery_accepted ] */ + if (context == NULL) + { + result = messaging_delivery_accepted(); + } + else + { + IOTHUB_MESSAGING* messagingData = (IOTHUB_MESSAGING*)context; + + BINARY_DATA binary_data; + JSON_Value* root_value = NULL; + JSON_Object* feedback_object = NULL; + JSON_Array* feedback_array = NULL; + + /*Codes_SRS_IOTHUBMESSAGING_12_058: [ If context is not NULL IoTHubMessaging_LL_FeedbackMessageReceived shall get the content string of the message by calling message_get_body_amqp_data ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_059: [ IoTHubMessaging_LL_FeedbackMessageReceived shall parse the response JSON to IOTHUB_SERVICE_FEEDBACK_BATCH struct ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_060: [ IoTHubMessaging_LL_FeedbackMessageReceived shall use the following parson APIs to parse the response string: json_parse_string, json_value_get_object, json_object_get_string, json_object_dotget_string ] */ + if (message_get_body_amqp_data_in_place(message, 0, &binary_data) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("Cannot get message data"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed reading message body"); + } + else if ((root_value = json_parse_string((const char*)binary_data.bytes)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("json_parse_string failed"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed parsing json root"); + } + else if ((feedback_array = json_value_get_array(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("json_parse_string failed"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed parsing json array"); + } + else if (json_array_get_count(feedback_array) == 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("json_array_get_count failed"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "json_array_get_count failed"); + } + else + { + IOTHUB_SERVICE_FEEDBACK_BATCH* feedbackBatch; + + if ((feedbackBatch = (IOTHUB_SERVICE_FEEDBACK_BATCH*)malloc(sizeof(IOTHUB_SERVICE_FEEDBACK_BATCH))) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("json_parse_string failed"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed to allocate memory for feedback batch"); + } + else + { + size_t array_count = 0; + if ((array_count = json_array_get_count(feedback_array)) <= 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("json_array_get_count failed"); + free(feedbackBatch); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "json_array_get_count failed"); + } + else if ((feedbackBatch->feedbackRecordList = singlylinkedlist_create()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_061: [ If any of the parson API fails, IoTHubMessaging_LL_FeedbackMessageReceived shall return IOTHUB_MESSAGING_INVALID_JSON ] */ + LogError("singlylinkedlist_create failed"); + free(feedbackBatch); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "singlylinkedlist_create failed"); + } + else + { + bool isLoopFailed = false; + for (size_t i = 0; i < array_count; i++) + { + if ((feedback_object = json_array_get_object(feedback_array, i)) == NULL) + { + isLoopFailed = true; + break; + } + else + { + IOTHUB_SERVICE_FEEDBACK_RECORD* feedbackRecord; + if ((feedbackRecord = (IOTHUB_SERVICE_FEEDBACK_RECORD*)malloc(sizeof(IOTHUB_SERVICE_FEEDBACK_RECORD))) == NULL) + { + isLoopFailed = true; + break; + } + else + { + feedbackRecord->deviceId = (char*)json_object_get_string(feedback_object, FEEDBACK_RECORD_KEY_DEVICE_ID); + feedbackRecord->generationId = (char*)json_object_get_string(feedback_object, FEEDBACK_RECORD_KEY_DEVICE_GENERATION_ID); + feedbackRecord->description = (char*)json_object_get_string(feedback_object, FEEDBACK_RECORD_KEY_DESCRIPTION); + feedbackRecord->enqueuedTimeUtc = (char*)json_object_get_string(feedback_object, FEEDBACK_RECORD_KEY_ENQUED_TIME_UTC); + feedbackRecord->originalMessageId = (char*)json_object_get_string(feedback_object, FEEDBACK_RECORD_KEY_ORIGINAL_MESSAGE_ID); + feedbackRecord->correlationId = ""; + + if (feedbackRecord->description == NULL) + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_UNKNOWN; + } + else + { + size_t j; + for (j = 0; feedbackRecord->description[j]; j++) + { + feedbackRecord->description[j] = (char)tolower(feedbackRecord->description[j]); + } + + if (strcmp(feedbackRecord->description, "success") == 0) + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_SUCCESS; + } + else if (strcmp(feedbackRecord->description, "expired") == 0) + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_EXPIRED; + } + else if (strcmp(feedbackRecord->description, "deliverycountexceeded") == 0) + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_DELIVER_COUNT_EXCEEDED; + } + else if (strcmp(feedbackRecord->description, "rejected") == 0) + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_REJECTED; + } + else + { + feedbackRecord->statusCode = IOTHUB_FEEDBACK_STATUS_CODE_UNKNOWN; + } + } + singlylinkedlist_add(feedbackBatch->feedbackRecordList, feedbackRecord); + } + } + } + feedbackBatch->lockToken = ""; + feedbackBatch->userId = ""; + + if (isLoopFailed) + { + LogError("Failed to read feedback records"); + result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed to read feedback records"); + } + else + { + if (messagingData->callback_data->feedbackMessageCallback != NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_062: [ If context is not NULL IoTHubMessaging_LL_FeedbackMessageReceived shall call IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK with the received IOTHUB_SERVICE_FEEDBACK_BATCH ] */ + (messagingData->callback_data->feedbackMessageCallback)(messagingData->callback_data->feedbackUserContext, feedbackBatch); + } + result = messaging_delivery_accepted(); + } + + /*Codes_SRS_IOTHUBMESSAGING_12_078: [** IoTHubMessaging_LL_FeedbackMessageReceived shall do clean up before exits ] */ + LIST_ITEM_HANDLE feedbackRecord = singlylinkedlist_get_head_item(feedbackBatch->feedbackRecordList); + while (feedbackRecord != NULL) + { + IOTHUB_SERVICE_FEEDBACK_RECORD* feedback = (IOTHUB_SERVICE_FEEDBACK_RECORD*)singlylinkedlist_item_get_value(feedbackRecord); + feedbackRecord = singlylinkedlist_get_next_item(feedbackRecord); + free(feedback); + } + singlylinkedlist_destroy(feedbackBatch->feedbackRecordList); + free(feedbackBatch); + } + } + } + json_array_clear(feedback_array); + json_value_free(root_value); + } + return result; +} + +IOTHUB_MESSAGING_HANDLE IoTHubMessaging_LL_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_MESSAGING_HANDLE result; + CALLBACK_DATA* callback_data; + + /*Codes_SRS_IOTHUBMESSAGING_12_001: [ If the serviceClientHandle input parameter is NULL IoTHubMessaging_LL_Create shall return NULL ] */ + if (serviceClientHandle == NULL) + { + LogError("serviceClientHandle input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_064: [ If any member of the serviceClientHandle input parameter is NULL IoTHubMessaging_LL_Create shall return NULL ] */ + IOTHUB_SERVICE_CLIENT_AUTH* serviceClientAuth = (IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle; + + if (serviceClientAuth->hostname == NULL) + { + LogError("authInfo->hostName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubName == NULL) + { + LogError("authInfo->iothubName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubSuffix == NULL) + { + LogError("authInfo->iothubSuffix input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->keyName == NULL) + { + LogError("authInfo->keyName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->sharedAccessKey == NULL) + { + LogError("authInfo->sharedAccessKey input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_002: [ IoTHubMessaging_LL_Create shall allocate memory for a new messaging instance ] */ + if ((result = (IOTHUB_MESSAGING*)malloc(sizeof(IOTHUB_MESSAGING))) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_003: [ If the allocation failed, IoTHubMessaging_LL_Create shall return NULL ] */ + LogError("Malloc failed for IOTHUB_REGISTRYMANAGER"); + } + /*Codes_SRS_IOTHUBMESSAGING_12_004: [ If the allocation and creation is successful, IoTHubMessaging_LL_Create shall return with the messaging instance, a non-NULL value ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_065: [ IoTHubMessaging_LL_Create shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s ] */ + else if (mallocAndStrcpy_s(&result->hostname, serviceClientAuth->hostname) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_066: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("mallocAndStrcpy_s failed for hostName"); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGING_12_067: [ IoTHubMessaging_LL_Create shall allocate memory and copy iothubName to result->iothubName by calling mallocAndStrcpy_s ] */ + else if (mallocAndStrcpy_s(&result->iothubName, serviceClientAuth->iothubName) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_068: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("mallocAndStrcpy_s failed for iothubName"); + free(result->hostname); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGING_12_069: [ IoTHubMessaging_LL_Create shall allocate memory and copy iothubSuffix to result->iothubSuffix by calling mallocAndStrcpy_s ] */ + else if (mallocAndStrcpy_s(&result->iothubSuffix, serviceClientAuth->iothubSuffix) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_070: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("mallocAndStrcpy_s failed for iothubSuffix"); + free(result->hostname); + free(result->iothubName); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGING_12_071: [ IoTHubMessaging_LL_Create shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s ] */ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, serviceClientAuth->sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_072: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free(result->hostname); + free(result->iothubName); + free(result->iothubSuffix); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGING_12_073: [ IoTHubMessaging_LL_Create shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s ] */ + else if (mallocAndStrcpy_s(&result->keyName, serviceClientAuth->keyName) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_074: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("mallocAndStrcpy_s failed for keyName"); + free(result->hostname); + free(result->iothubName); + free(result->iothubSuffix); + free(result->sharedAccessKey); + free(result); + result = NULL; + } + /*Codes_SRS_IOTHUBMESSAGING_12_075: [ IoTHubMessaging_LL_Create shall set messaging isOpened flag to false ] */ + else if ((callback_data = (CALLBACK_DATA*)malloc(sizeof(CALLBACK_DATA))) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_074: [ If the mallocAndStrcpy_s fails, IoTHubMessaging_LL_Create shall do clean up and return NULL ] */ + LogError("Malloc failed for callback_data"); + free(result->hostname); + free(result->iothubName); + free(result->iothubSuffix); + free(result->sharedAccessKey); + free(result->keyName); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_076: [ If create successfull IoTHubMessaging_LL_Create shall save the callback data return the valid messaging handle ] */ + callback_data->openCompleteCompleteCallback = NULL; + callback_data->sendCompleteCallback = NULL; + callback_data->feedbackMessageCallback = NULL; + callback_data->openUserContext = NULL; + callback_data->sendUserContext = NULL; + callback_data->feedbackUserContext = NULL; + + result->callback_data = callback_data; + result->isOpened = false; + } + } + } + return result; +} + +void IoTHubMessaging_LL_Destroy(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + /*Codes_SRS_IOTHUBMESSAGING_12_005: [ If the messagingHandle input parameter is NULL IoTHubMessaging_LL_Destroy shall return ] */ + if (messagingHandle != NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_006: [ If the messagingHandle input parameter is not NULL IoTHubMessaging_LL_Destroy shall free all resources (memory) allocated by IoTHubMessaging_LL_Create ] */ + IOTHUB_MESSAGING* messHandle = (IOTHUB_MESSAGING*)messagingHandle; + + free(messHandle->callback_data); + free(messHandle->hostname); + free(messHandle->iothubName); + free(messHandle->iothubSuffix); + free(messHandle->sharedAccessKey); + free(messHandle->keyName); + free(messHandle); + } +} + +static int attachServiceClientTypeToLink(LINK_HANDLE link) +{ + fields attach_properties; + AMQP_VALUE serviceClientTypeKeyName; + AMQP_VALUE serviceClientTypeValue; + int result; + + if ((attach_properties = amqpvalue_create_map()) == NULL) + { + LogError("Failed to create the map for service client type."); + result = __FAILURE__; + } + else + { + if ((serviceClientTypeKeyName = amqpvalue_create_symbol("com.microsoft:client-version")) == NULL) + { + LogError("Failed to create the key name for the service client type."); + result = __FAILURE__; + } + else + { + if ((serviceClientTypeValue = amqpvalue_create_string(IOTHUB_SERVICE_CLIENT_TYPE_PREFIX IOTHUB_SERVICE_CLIENT_BACKSLASH IOTHUB_SERVICE_CLIENT_VERSION)) == NULL) + { + LogError("Failed to create the key value for the service client type."); + result = __FAILURE__; + } + else + { + if ((result = amqpvalue_set_map_value(attach_properties, serviceClientTypeKeyName, serviceClientTypeValue)) != 0) + { + LogError("Failed to set the property map for the service client type. Error code is: %d", result); + } + else if ((result = link_set_attach_properties(link, attach_properties)) != 0) + { + LogError("Unable to attach the service client type to the link properties. Error code is: %d", result); + } + else + { + result = 0; + } + + amqpvalue_destroy(serviceClientTypeValue); + } + + amqpvalue_destroy(serviceClientTypeKeyName); + } + + amqpvalue_destroy(attach_properties); + } + return result; +} + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_LL_Open(IOTHUB_MESSAGING_HANDLE messagingHandle, IOTHUB_OPEN_COMPLETE_CALLBACK openCompleteCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + char* send_target_address = NULL; + char* receive_target_address = NULL; + + TLSIO_CONFIG tls_io_config; + SASLCLIENTIO_CONFIG sasl_io_config; + + AMQP_VALUE sendSource = NULL; + AMQP_VALUE sendTarget = NULL; + + AMQP_VALUE receiveSource = NULL; + AMQP_VALUE receiveTarget = NULL; + + /*Codes_SRS_IOTHUBMESSAGING_12_007: [ If the messagingHandle input parameter is NULL IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_INVALID_ARG ] */ + if (messagingHandle == NULL) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + messagingHandle->message_sender = NULL; + messagingHandle->connection = NULL; + messagingHandle->session = NULL; + messagingHandle->sender_link = NULL; + messagingHandle->sasl_plain_config.authzid = NULL; + messagingHandle->sasl_mechanism_handle = NULL; + messagingHandle->tls_io = NULL; + messagingHandle->sasl_io = NULL; + + /*Codes_SRS_IOTHUBMESSAGING_12_008: [ If messaging is already opened IoTHubMessaging_LL_Open return shall IOTHUB_MESSAGING_OK ] */ + if (messagingHandle->isOpened != 0) + { + LogError("Messaging is already opened"); + result = IOTHUB_MESSAGING_OK; + } + /*Codes_SRS_IOTHUBMESSAGING_12_022: [ IoTHubMessaging_LL_Open shall create uAMQP messaging target for sender by calling the messaging_create_target ] */ + else if ((send_target_address = createSendTargetAddress(messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_077: [ If the messagingHandle->hostname input parameter is NULL IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create sendTargetAddress"); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_027: [ IoTHubMessaging_LL_Open shall create uAMQP messaging source for receiver by calling the messaging_create_source ] */ + else if ((receive_target_address = createReceiveTargetAddress(messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create receiveTargetAddress"); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_010: [ IoTHubMessaging_LL_Open shall create uAMQP PLAIN SASL mechanism by calling saslmechanism_create with the sasl plain interface ] */ + else if ((messagingHandle->sasl_plain_config.authcid = createAuthCid(messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create authCid"); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_010: [ IoTHubMessaging_LL_Open shall create uAMQP PLAIN SASL mechanism by calling saslmechanism_create with the sasl plain interface ] */ + else if ((messagingHandle->sasl_plain_config.passwd = createSasToken(messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create sasToken"); + free((char*)messagingHandle->sasl_plain_config.authcid); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + const SASL_MECHANISM_INTERFACE_DESCRIPTION* sasl_mechanism_interface; + + /*Codes_SRS_IOTHUBMESSAGING_12_010: [ IoTHubMessaging_LL_Open shall create uAMQP PLAIN SASL mechanism by calling saslmechanism_create with the sasl plain interface ] */ + if ((sasl_mechanism_interface = saslplain_get_interface()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not get SASL plain mechanism interface."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_010: [ IoTHubMessaging_LL_Open shall create uAMQP PLAIN SASL mechanism by calling saslmechanism_create with the sasl plain interface ] */ + else if ((messagingHandle->sasl_mechanism_handle = saslmechanism_create(sasl_mechanism_interface, &messagingHandle->sasl_plain_config)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create SASL plain mechanism."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + tls_io_config.hostname = messagingHandle->hostname; + tls_io_config.port = 5671; + tls_io_config.underlying_io_interface = NULL; + tls_io_config.underlying_io_parameters = NULL; + + const IO_INTERFACE_DESCRIPTION* tlsio_interface; + + /*Codes_SRS_IOTHUBMESSAGING_12_011: [ IoTHubMessaging_LL_Open shall create uAMQP TLSIO by calling the xio_create ] */ + if ((tlsio_interface = platform_get_default_tlsio()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not get default TLS IO interface."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_011: [ IoTHubMessaging_LL_Open shall create uAMQP TLSIO by calling the xio_create ] */ + else if ((messagingHandle->tls_io = xio_create(tlsio_interface, &tls_io_config)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create TLS IO."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + messagingHandle->callback_data->openCompleteCompleteCallback = openCompleteCallback; + messagingHandle->callback_data->openUserContext = userContextCallback; + + sasl_io_config.sasl_mechanism = messagingHandle->sasl_mechanism_handle; + sasl_io_config.underlying_io = messagingHandle->tls_io; + + const IO_INTERFACE_DESCRIPTION* saslclientio_interface; + + /*Codes_SRS_IOTHUBMESSAGING_12_012: [ IoTHubMessaging_LL_Open shall create uAMQP SASL IO by calling the xio_create with the previously created SASL mechanism and TLSIO] */ + if ((saslclientio_interface = saslclientio_get_interface_description()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create get SASL IO interface description."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_012: [ IoTHubMessaging_LL_Open shall create uAMQP SASL IO by calling the xio_create with the previously created SASL mechanism and TLSIO] */ + else if ((messagingHandle->sasl_io = xio_create(saslclientio_interface, &sasl_io_config)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create SASL IO."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_013: [ IoTHubMessaging_LL_Open shall create uAMQP connection by calling the connection_create with the previously created SASL IO ] */ + else if ((messagingHandle->connection = connection_create(messagingHandle->sasl_io, messagingHandle->hostname, "some", NULL, NULL)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create connection."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_014: [ IoTHubMessaging_LL_Open shall create uAMQP session by calling the session_create ] */ + else if ((messagingHandle->session = session_create(messagingHandle->connection, NULL, NULL)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create session."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_015: [ IoTHubMessaging_LL_Open shall set the AMQP incoming window to UINT32 maximum value by calling session_set_incoming_window ] */ + else if (session_set_incoming_window(messagingHandle->session, 2147483647) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set incoming window."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_016: [ IoTHubMessaging_LL_Open shall set the AMQP outgoing window to UINT32 maximum value by calling session_set_outgoing_window ] */ + else if (session_set_outgoing_window(messagingHandle->session, 255 * 1024) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set outgoing window."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_021: [ IoTHubMessaging_LL_Open shall create uAMQP messaging source for sender by calling the messaging_create_source ] */ + else if ((sendSource = messaging_create_source("ingress")) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create source for link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_022: [ IoTHubMessaging_LL_Open shall create uAMQP messaging target for sender by calling the messaging_create_target ] */ + else if ((sendTarget = messaging_create_target(send_target_address)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create target for link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_018: [ IoTHubMessaging_LL_Open shall create uAMQP sender link by calling the link_create ] */ + else if ((messagingHandle->sender_link = link_create(messagingHandle->session, "sender-link", role_sender, sendSource, sendTarget)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_06_001: [ IoTHubMessaging_LL_Open shall add the version property to the sender link by calling the link_set_attach_properties ] */ + else if (attachServiceClientTypeToLink(messagingHandle->sender_link) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set the sender attach properties."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_019: [ IoTHubMessaging_LL_Open shall set the AMQP sender link settle mode to sender_settle_mode_unsettled by calling link_set_snd_settle_mode ] */ + else if (link_set_snd_settle_mode(messagingHandle->sender_link, sender_settle_mode_unsettled) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set the sender settle mode."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_023: [ IoTHubMessaging_LL_Open shall create uAMQP message sender by calling the messagesender_create with the created sender link and the local IoTHubMessaging_LL_SenderStateChanged callback ] */ + else if ((messagingHandle->message_sender = messagesender_create(messagingHandle->sender_link, IoTHubMessaging_LL_SenderStateChanged, messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create message sender."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_023: [ IoTHubMessaging_LL_Open shall create uAMQP message sender by calling the messagesender_create with the created sender link and the local IoTHubMessaging_LL_SenderStateChanged callback ] */ + else if (messagesender_open(messagingHandle->message_sender) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not open the message sender."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_027: [ IoTHubMessaging_LL_Open shall create uAMQP messaging source for receiver by calling the messaging_create_source ] */ + else if ((receiveSource = messaging_create_source(receive_target_address)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create source for link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_028: [ IoTHubMessaging_LL_Open shall create uAMQP messaging target for receiver by calling the messaging_create_target ] */ + else if ((receiveTarget = messaging_create_target("receiver_001")) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create target for link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_024: [ IoTHubMessaging_LL_Open shall create uAMQP receiver link by calling the link_create ] */ + else if ((messagingHandle->receiver_link = link_create(messagingHandle->session, "receiver-link", role_receiver, receiveSource, receiveTarget)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_06_002: [ IoTHubMessaging_LL_Open shall add the version property to the receiver by calling the link_set_attach_properties ] */ + else if (attachServiceClientTypeToLink(messagingHandle->receiver_link) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create link."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_025: [ IoTHubMessaging_LL_Open shall set the AMQP receiver link settle mode to receiver_settle_mode_first by calling link_set_rcv_settle_mode ] */ + else if (link_set_rcv_settle_mode(messagingHandle->receiver_link, receiver_settle_mode_first) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set the sender settle mode."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_029: [ IoTHubMessaging_LL_Open shall create uAMQP message receiver by calling the messagereceiver_create with the created sender link and the local IoTHubMessaging_LL_ReceiverStateChanged callback ] */ + else if ((messagingHandle->message_receiver = messagereceiver_create(messagingHandle->receiver_link, IoTHubMessaging_LL_ReceiverStateChanged, messagingHandle)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create message receiver."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_029: [ IoTHubMessaging_LL_Open shall create uAMQP message receiver by calling the messagereceiver_create with the created sender link and the local IoTHubMessaging_LL_ReceiverStateChanged callback ] */ + else if (messagereceiver_open(messagingHandle->message_receiver, IoTHubMessaging_LL_FeedbackMessageReceived, messagingHandle) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_030: [ If any of the uAMQP call fails IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not open the message receiver."); + free((char*)messagingHandle->sasl_plain_config.authcid); + free((char*)messagingHandle->sasl_plain_config.passwd); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_031: [ If all of the uAMQP call return 0 (success) IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_OK ] */ + messagingHandle->isOpened = true; + result = IOTHUB_MESSAGING_OK; + } + } + } + } + } + amqpvalue_destroy(sendSource); + amqpvalue_destroy(sendTarget); + amqpvalue_destroy(receiveSource); + amqpvalue_destroy(receiveTarget); + + if (send_target_address != NULL) + { + free(send_target_address); + } + if (receive_target_address != NULL) + { + free(receive_target_address); + } + return result; +} + +void IoTHubMessaging_LL_Close(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + /*Codes_SRS_IOTHUBMESSAGING_12_032: [ If the messagingHandle input parameter is NULL IoTHubMessaging_LL_Close shall return IOTHUB_MESSAGING_INVALID_ARG ] */ + if (messagingHandle == NULL) + { + LogError("Input parameter cannot be NULL"); + } + /*Codes_SRS_IOTHUBMESSAGING_12_033: [ IoTHubMessaging_LL_Close destroy the AMQP transportconnection by calling link_destroy, session_destroy, connection_destroy, xio_destroy, saslmechanism_destroy ] */ + else + { + messagesender_destroy(messagingHandle->message_sender); + messagereceiver_destroy(messagingHandle->message_receiver); + + link_destroy(messagingHandle->sender_link); + link_destroy(messagingHandle->receiver_link); + + session_destroy(messagingHandle->session); + connection_destroy(messagingHandle->connection); + xio_destroy(messagingHandle->sasl_io); + xio_destroy(messagingHandle->tls_io); + saslmechanism_destroy(messagingHandle->sasl_mechanism_handle); + + if (messagingHandle->sasl_plain_config.authcid != NULL) + { + free((char*)messagingHandle->sasl_plain_config.authcid); + } + if (messagingHandle->sasl_plain_config.passwd != NULL) + { + free((char*)messagingHandle->sasl_plain_config.passwd); + } + if (messagingHandle->sasl_plain_config.authzid != NULL) + { + free((char*)messagingHandle->sasl_plain_config.authzid); + } + messagingHandle->isOpened = false; + } +} + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_LL_SetFeedbackMessageCallback(IOTHUB_MESSAGING_HANDLE messagingHandle, IOTHUB_FEEDBACK_MESSAGE_RECEIVED_CALLBACK feedbackMessageReceivedCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + /*Codes_SRS_IOTHUBMESSAGING_12_042: [ IoTHubMessaging_LL_SetCallbacks shall verify the messagingHandle input parameter and if it is NULL then return NULL ] */ + if (messagingHandle == NULL) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_043: [ IoTHubMessaging_LL_SetCallbacks shall save the given feedbackMessageReceivedCallback to use them in local callbacks ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_044: [ IoTHubMessaging_LL_Open shall return IOTHUB_MESSAGING_OK after the callbacks have been set ] */ + messagingHandle->callback_data->feedbackMessageCallback = feedbackMessageReceivedCallback; + messagingHandle->callback_data->feedbackUserContext = userContextCallback; + result = IOTHUB_MESSAGING_OK; + } + return result; +} + + +IOTHUB_MESSAGING_RESULT IoTHubMessaging_LL_Send(IOTHUB_MESSAGING_HANDLE messagingHandle, const char* deviceId, IOTHUB_MESSAGE_HANDLE message, IOTHUB_SEND_COMPLETE_CALLBACK sendCompleteCallback, void* userContextCallback) +{ + IOTHUB_MESSAGING_RESULT result; + + // There is no support for module sending message for callers, but most of plumbing is available should this be enabled via a new API. + const char* moduleId = NULL; + + char* deviceDestinationString; + + /*Codes_SRS_IOTHUBMESSAGING_12_034: [ IoTHubMessaging_LL_SendMessage shall verify the messagingHandle, deviceId, message input parameters and if any of them are NULL then return NULL ] */ + if (messagingHandle == NULL) + { + LogError("Input parameter messagingHandle cannot be NULL"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + /*Codes_SRS_IOTHUBMESSAGING_12_034: [ IoTHubMessaging_LL_SendMessage shall verify the messagingHandle, deviceId, message input parameters and if any of them are NULL then return NULL ] */ + else if (deviceId == NULL) + { + LogError("Input parameter deviceId cannot be NULL"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + /*Codes_SRS_IOTHUBMESSAGING_12_034: [ IoTHubMessaging_LL_SendMessage shall verify the messagingHandle, deviceId, message input parameters and if any of them are NULL then return NULL ] */ + else if (message == NULL) + { + LogError("Input parameter message cannot be NULL"); + result = IOTHUB_MESSAGING_INVALID_ARG; + } + /*Codes_SRS_IOTHUBMESSAGING_12_035: [ IoTHubMessaging_LL_SendMessage shall verify if the AMQP messaging has been established by a successfull call to _Open and if it is not then return IOTHUB_MESSAGING_ERROR ] */ + else if (messagingHandle->isOpened == 0) + { + LogError("Messaging is not opened - call IoTHubMessaging_LL_Open to open"); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_038: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message properties to the given message properties by calling message_set_properties ] */ + else if ((deviceDestinationString = createDeviceDestinationString(deviceId, moduleId)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create a message."); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + unsigned const char* messageContent; + size_t messageContentSize; + + if (getMessageContentAndSize(message, &messageContent, &messageContentSize) != 0) + { + LogError("Failed getting the message content and message size from IOTHUB_MESSAGE_HANDLE instance."); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + MESSAGE_HANDLE amqpMessage; + AMQP_VALUE to_amqp_value; + + /*Codes_SRS_IOTHUBMESSAGING_12_036: [ IoTHubMessaging_LL_SendMessage shall create a uAMQP message by calling message_create ] */ + if ((amqpMessage = message_create()) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create a message."); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_038: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message properties to the given message properties by calling message_set_properties ] */ + else if ((to_amqp_value = amqpvalue_create_string(deviceDestinationString)) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not create properties for message - amqpvalue_create_string"); + message_destroy(amqpMessage); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + BINARY_DATA binary_data; + + binary_data.bytes = messageContent; + binary_data.length = messageContentSize; + + /*Codes_SRS_IOTHUBMESSAGING_12_037: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message body to the given message content by calling message_add_body_amqp_data ] */ + if (message_add_body_amqp_data(amqpMessage, binary_data) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Failed setting the body of the uAMQP message."); + result = IOTHUB_MESSAGING_ERROR; + } + /*Codes_SRS_IOTHUBMESSAGING_12_038: [ IoTHubMessaging_LL_SendMessage shall set the uAMQP message properties to the given message properties by calling message_set_properties ] */ + else if (addPropertiesToAMQPMessage(message, amqpMessage, to_amqp_value) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + message_destroy(amqpMessage); + LogError("Failed setting properties of the uAMQP message."); + result = IOTHUB_MESSAGING_ERROR; + } + else if (addApplicationPropertiesToAMQPMessage(message, amqpMessage) != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + message_destroy(amqpMessage); + LogError("Failed setting application properties of the uAMQP message."); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + messagingHandle->callback_data->sendCompleteCallback = sendCompleteCallback; + messagingHandle->callback_data->sendUserContext = userContextCallback; + + /*Codes_SRS_IOTHUBMESSAGING_12_039: [ IoTHubMessaging_LL_SendMessage shall call uAMQP messagesender_send with the created message with IoTHubMessaging_LL_SendMessageComplete callback by which IoTHubMessaging is notified of completition of send ] */ + if (messagesender_send_async(messagingHandle->message_sender, amqpMessage, (ON_MESSAGE_SEND_COMPLETE)IoTHubMessaging_LL_SendMessageComplete, messagingHandle, 0) == NULL) + { + /*Codes_SRS_IOTHUBMESSAGING_12_040: [ If any of the uAMQP call fails IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_ERROR ] */ + LogError("Could not set outgoing window."); + message_destroy(amqpMessage); + result = IOTHUB_MESSAGING_ERROR; + } + else + { + /*Codes_SRS_IOTHUBMESSAGING_12_041: [ If all uAMQP call return 0 then IoTHubMessaging_LL_SendMessage shall return IOTHUB_MESSAGING_OK ] */ + result = IOTHUB_MESSAGING_OK; + } + } + message_destroy(amqpMessage); + amqpvalue_destroy(to_amqp_value); + } + } + free(deviceDestinationString); + } + return result; +} + + +void IoTHubMessaging_LL_DoWork(IOTHUB_MESSAGING_HANDLE messagingHandle) +{ + /*Codes_SRS_IOTHUBMESSAGING_12_045: [ IoTHubMessaging_LL_DoWork shall verify if uAMQP transport has been initialized and if it is not then return immediately ] */ + if (messagingHandle != 0) + { + /*Codes_SRS_IOTHUBMESSAGING_12_046: [ IoTHubMessaging_LL_DoWork shall call uAMQP connection_dowork ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_047: [ IoTHubMessaging_LL_SendMessageComplete callback given to messagesender_send will be called with MESSAGE_SEND_RESULT ] */ + /*Codes_SRS_IOTHUBMESSAGING_12_048: [ If message has been received the IoTHubMessaging_LL_FeedbackMessageReceived callback given to messagesender_receive will be called with the received MESSAGE_HANDLE ] */ + connection_dowork(messagingHandle->connection); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_registrymanager.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2288 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <ctype.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/httpapiex.h" +#include "azure_c_shared_utility/httpapiexsas.h" +#include "azure_c_shared_utility/connection_string_parser.h" + +#include "parson.h" +#include "iothub_registrymanager.h" +#include "iothub_sc_version.h" + +#define IOTHUB_DEVICE_EX_VERSION_LATEST IOTHUB_DEVICE_EX_VERSION_1 +#define IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_LATEST IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_1 +#define IOTHUB_REGISTRY_DEVICE_UPDATE_EX_VERSION_LATEST IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_1 +#define IOTHUB_MODULE_VERSION_LATEST IOTHUB_MODULE_VERSION_1 +#define IOTHUB_REGISTRY_MODULE_CREATE_VERSION_LATEST IOTHUB_REGISTRY_MODULE_CREATE_VERSION_1 +#define IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_LATEST IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_1 + + +#define IOTHUB_REQUEST_MODE_VALUES \ + IOTHUB_REQUEST_CREATE, \ + IOTHUB_REQUEST_GET, \ + IOTHUB_REQUEST_UPDATE, \ + IOTHUB_REQUEST_DELETE, \ + IOTHUB_REQUEST_GET_DEVICE_LIST, \ + IOTHUB_REQUEST_GET_STATISTICS \ + +DEFINE_ENUM(IOTHUB_REQUEST_MODE, IOTHUB_REQUEST_MODE_VALUES); + +DEFINE_ENUM_STRINGS(IOTHUB_REGISTRYMANAGER_RESULT, IOTHUB_REGISTRYMANAGER_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_REGISTRYMANAGER_AUTH_METHOD, IOTHUB_REGISTRYMANAGER_AUTH_METHOD_VALUES); + +#define HTTP_HEADER_KEY_AUTHORIZATION "Authorization" +#define HTTP_HEADER_VAL_AUTHORIZATION " " +#define HTTP_HEADER_KEY_REQUEST_ID "Request-Id" +#define HTTP_HEADER_VAL_REQUEST_ID "1001" +#define HTTP_HEADER_KEY_USER_AGENT "User-Agent" +#define HTTP_HEADER_VAL_USER_AGENT IOTHUB_SERVICE_CLIENT_TYPE_PREFIX IOTHUB_SERVICE_CLIENT_BACKSLASH IOTHUB_SERVICE_CLIENT_VERSION +#define HTTP_HEADER_KEY_ACCEPT "Accept" +#define HTTP_HEADER_VAL_ACCEPT "application/json" +#define HTTP_HEADER_KEY_CONTENT_TYPE "Content-Type" +#define HTTP_HEADER_VAL_CONTENT_TYPE "application/json; charset=utf-8" +#define HTTP_HEADER_KEY_IFMATCH "If-Match" +#define HTTP_HEADER_VAL_IFMATCH "*" + +static size_t IOTHUB_DEVICES_MAX_REQUEST = 1000; + +static const char* DEVICE_JSON_KEY_DEVICE_NAME = "deviceId"; +static const char* DEVICE_JSON_KEY_MODULE_NAME = "moduleId"; +static const char* DEVICE_JSON_KEY_DEVICE_AUTH_TYPE = "authentication.type"; +static const char* DEVICE_JSON_KEY_DEVICE_AUTH_SAS = "sas"; +static const char* DEVICE_JSON_KEY_DEVICE_AUTH_SELF_SIGNED = "selfSigned"; +static const char* DEVICE_JSON_KEY_DEVICE_AUTH_CERTIFICATE_AUTHORITY = "certificateAuthority"; +static const char* DEVICE_JSON_KEY_DEVICE_AUTH_NONE = "none"; +static const char* DEVICE_JSON_KEY_DEVICE_PRIMARY_KEY = "authentication.symmetricKey.primaryKey"; +static const char* DEVICE_JSON_KEY_DEVICE_SECONDARY_KEY = "authentication.symmetricKey.secondaryKey"; +static const char* DEVICE_JSON_KEY_DEVICE_PRIMARY_THUMBPRINT = "authentication.x509Thumbprint.primaryThumbprint"; +static const char* DEVICE_JSON_KEY_DEVICE_SECONDARY_THUMBPRINT = "authentication.x509Thumbprint.secondaryThumbprint"; +static const char* DEVICE_JSON_KEY_CAPABILITIES_IOTEDGE = "capabilities.iotEdge"; + +static const char* DEVICE_JSON_KEY_DEVICE_GENERATION_ID = "generationId"; +static const char* DEVICE_JSON_KEY_DEVICE_ETAG = "etag"; + +static const char* DEVICE_JSON_KEY_DEVICE_CONNECTIONSTATE = "connectionState"; +static const char* DEVICE_JSON_KEY_DEVICE_CONNECTIONSTATEUPDATEDTIME = "connectionStateUpdatedTime"; + +static const char* DEVICE_JSON_KEY_DEVICE_STATUS = "status"; +static const char* DEVICE_JSON_KEY_DEVICE_STATUSREASON = "statusReason"; +static const char* DEVICE_JSON_KEY_DEVICE_STATUSUPDATEDTIME = "statusUpdatedTime"; + +static const char* DEVICE_JSON_KEY_DEVICE_LASTACTIVITYTIME = "lastActivityTime"; +static const char* DEVICE_JSON_KEY_DEVICE_CLOUDTODEVICEMESSAGECOUNT = "cloudToDeviceMessageCount"; + +static const char* DEVICE_JSON_KEY_DEVICE_ISMANAGED = "isManaged"; +static const char* DEVICE_JSON_KEY_DEVICE_CONFIGURATION = "configuration"; +static const char* DEVICE_JSON_KEY_DEVICE_DEVICEROPERTIES = "deviceProperties"; +static const char* DEVICE_JSON_KEY_DEVICE_SERVICEPROPERTIES = "serviceProperties"; +static const char* DEVICE_JSON_KEY_MANAGED_BY = "managedBy"; + +static const char* DEVICE_JSON_KEY_TOTAL_DEVICECOUNT = "totalDeviceCount"; +static const char* DEVICE_JSON_KEY_ENABLED_DEVICECCOUNT = "enabledDeviceCount"; +static const char* DEVICE_JSON_KEY_DISABLED_DEVICECOUNT = "disabledDeviceCount"; + +static const char* DEVICE_JSON_DEFAULT_VALUE_ENABLED = "enabled"; +static const char* DEVICE_JSON_DEFAULT_VALUE_DISABLED = "disabled"; +static const char* DEVICE_JSON_DEFAULT_VALUE_CONNECTED = "Connected"; +static const char* DEVICE_JSON_DEFAULT_VALUE_TRUE = "true"; + +static const char* URL_API_VERSION = "api-version=2017-11-08-preview"; + +static const char* RELATIVE_PATH_FMT_CRUD = "/devices/%s?%s"; +static const char* RELATIVE_PATH_MODULE_FMT_CRUD = "/devices/%s/modules/%s?%s"; +static const char* RELATIVE_PATH_FMT_LIST = "/devices/?top=%s&%s"; +static const char* RELATIVE_PATH_FMT_STAT = "/statistics/devices?%s"; +static const char* RELATIVE_PATH_FMT_MODULE_LIST = "/devices/%s/modules?%s"; + +typedef enum {IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE, IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE} IOTHUB_REGISTRYMANAGER_MODEL_TYPE; + +typedef struct IOTHUB_DEVICE_OR_MODULE_TAG +{ + IOTHUB_REGISTRYMANAGER_MODEL_TYPE type; + + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + const char* generationId; + const char* eTag; + IOTHUB_DEVICE_CONNECTION_STATE connectionState; + const char* connectionStateUpdatedTime; + IOTHUB_DEVICE_STATUS status; + const char* statusReason; + const char* statusUpdatedTime; + const char* lastActivityTime; + size_t cloudToDeviceMessageCount; + + bool isManaged; + const char* configuration; + const char* deviceProperties; + const char* serviceProperties; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; + + //Device exclusive fields + bool iotEdge_capable; + + //Module exclusive fields + const char* moduleId; + const char* managedBy; +} IOTHUB_DEVICE_OR_MODULE; + +typedef struct IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE_TAG +{ + IOTHUB_REGISTRYMANAGER_MODEL_TYPE type; + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; + //Device exclusive fields + bool iotEdge_capable; + //Module exclusive fields + const char* moduleId; + const char* managedBy; +} IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE; + +typedef struct IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE_TAG +{ + IOTHUB_REGISTRYMANAGER_MODEL_TYPE type; + const char* deviceId; + const char* primaryKey; + const char* secondaryKey; + IOTHUB_DEVICE_STATUS status; + IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod; + //Device exclusive fields + bool iotEdge_capable; + //Module exclusive fields + const char* moduleId; + const char* managedBy; +} IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE; + +static void initializeDeviceOrModuleInfoMembers(IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + if (NULL != deviceOrModuleInfo) + { + memset(deviceOrModuleInfo, 0, sizeof(IOTHUB_DEVICE_OR_MODULE)); + deviceOrModuleInfo->connectionState = IOTHUB_DEVICE_CONNECTION_STATE_DISCONNECTED; + deviceOrModuleInfo->status = IOTHUB_DEVICE_STATUS_DISABLED; + } +} + +void IoTHubRegistryManager_FreeDeviceExMembers(IOTHUB_DEVICE_EX* deviceInfo) +{ + free((void*)deviceInfo->deviceId); + free((void*)deviceInfo->primaryKey); + free((void*)deviceInfo->secondaryKey); + free((void*)deviceInfo->generationId); + free((void*)deviceInfo->eTag); + free((void*)deviceInfo->connectionStateUpdatedTime); + free((void*)deviceInfo->statusReason); + free((void*)deviceInfo->statusUpdatedTime); + free((void*)deviceInfo->lastActivityTime); + free((void*)deviceInfo->configuration); + free((void*)deviceInfo->deviceProperties); + free((void*)deviceInfo->serviceProperties); + memset(deviceInfo, 0, sizeof(*deviceInfo)); +} + +void IoTHubRegistryManager_FreeModuleMembers(IOTHUB_MODULE* moduleInfo) +{ + free((void*)moduleInfo->deviceId); + free((void*)moduleInfo->primaryKey); + free((void*)moduleInfo->secondaryKey); + free((void*)moduleInfo->generationId); + free((void*)moduleInfo->eTag); + free((void*)moduleInfo->connectionStateUpdatedTime); + free((void*)moduleInfo->statusReason); + free((void*)moduleInfo->statusUpdatedTime); + free((void*)moduleInfo->lastActivityTime); + free((void*)moduleInfo->configuration); + free((void*)moduleInfo->deviceProperties); + free((void*)moduleInfo->serviceProperties); + free((void*)moduleInfo->moduleId); + free((void*)moduleInfo->managedBy); + memset(moduleInfo, 0, sizeof(*moduleInfo)); +} + +// Frees memory allocated building up deviceInfo, but *NOT* deviceInfo itself as we don't own this +static void free_deviceOrModule_members(IOTHUB_DEVICE_OR_MODULE* deviceInfo) +{ + free((void*)deviceInfo->deviceId); + free((void*)deviceInfo->primaryKey); + free((void*)deviceInfo->secondaryKey); + free((void*)deviceInfo->generationId); + free((void*)deviceInfo->eTag); + free((void*)deviceInfo->connectionStateUpdatedTime); + free((void*)deviceInfo->statusReason); + free((void*)deviceInfo->statusUpdatedTime); + free((void*)deviceInfo->lastActivityTime); + free((void*)deviceInfo->configuration); + free((void*)deviceInfo->deviceProperties); + free((void*)deviceInfo->serviceProperties); + free((void*)deviceInfo->moduleId); + free((void*)deviceInfo->managedBy); + memset(deviceInfo, 0, sizeof(*deviceInfo)); +} + +static void free_device_members(IOTHUB_DEVICE* deviceInfo) +{ + free((void*)deviceInfo->deviceId); + free((void*)deviceInfo->primaryKey); + free((void*)deviceInfo->secondaryKey); + free((void*)deviceInfo->generationId); + free((void*)deviceInfo->eTag); + free((void*)deviceInfo->connectionStateUpdatedTime); + free((void*)deviceInfo->statusReason); + free((void*)deviceInfo->statusUpdatedTime); + free((void*)deviceInfo->lastActivityTime); + free((void*)deviceInfo->configuration); + free((void*)deviceInfo->deviceProperties); + free((void*)deviceInfo->serviceProperties); + memset(deviceInfo, 0, sizeof(*deviceInfo)); +} + +static void move_deviceOrModule_members_to_device(IOTHUB_DEVICE_OR_MODULE* deviceOrModule, IOTHUB_DEVICE* device) +{ + if ((deviceOrModule != NULL) && (device != NULL)) + { + device->deviceId = deviceOrModule->deviceId; + device->primaryKey = deviceOrModule->primaryKey; + device->secondaryKey = deviceOrModule->secondaryKey; + device->generationId = deviceOrModule->generationId; + device->eTag = deviceOrModule->eTag; + device->connectionState = deviceOrModule->connectionState; + device->connectionStateUpdatedTime = deviceOrModule->connectionStateUpdatedTime; + device->status = deviceOrModule->status; + device->statusReason = deviceOrModule->statusReason; + device->statusUpdatedTime = deviceOrModule->statusUpdatedTime; + device->lastActivityTime = deviceOrModule->lastActivityTime; + device->cloudToDeviceMessageCount = deviceOrModule->cloudToDeviceMessageCount; + device->isManaged = deviceOrModule->isManaged; + device->configuration = deviceOrModule->configuration; + device->deviceProperties = deviceOrModule->deviceProperties; + device->serviceProperties = deviceOrModule->serviceProperties; + device->authMethod = deviceOrModule->authMethod; + } +} + +static void free_nonDevice_members_from_deviceOrModule(IOTHUB_DEVICE_OR_MODULE* deviceOrModule) +{ + if (deviceOrModule != NULL) + { + //free module exclusive fields + free((void*)deviceOrModule->moduleId); + deviceOrModule->moduleId = NULL; + free((void*)deviceOrModule->managedBy); + deviceOrModule->managedBy = NULL; + //free device_ex exclusive fields (none currently require deallocation) + } +} + +static void move_deviceOrModule_members_to_deviceEx(IOTHUB_DEVICE_OR_MODULE* deviceOrModule, IOTHUB_DEVICE_EX* device) +{ + if ((deviceOrModule != NULL) && (device != NULL)) + { + if (device->version >= IOTHUB_DEVICE_EX_VERSION_1) + { + device->deviceId = deviceOrModule->deviceId; + device->primaryKey = deviceOrModule->primaryKey; + device->secondaryKey = deviceOrModule->secondaryKey; + device->generationId = deviceOrModule->generationId; + device->eTag = deviceOrModule->eTag; + device->connectionState = deviceOrModule->connectionState; + device->connectionStateUpdatedTime = deviceOrModule->connectionStateUpdatedTime; + device->status = deviceOrModule->status; + device->statusReason = deviceOrModule->statusReason; + device->statusUpdatedTime = deviceOrModule->statusUpdatedTime; + device->lastActivityTime = deviceOrModule->lastActivityTime; + device->cloudToDeviceMessageCount = deviceOrModule->cloudToDeviceMessageCount; + device->isManaged = deviceOrModule->isManaged; + device->configuration = deviceOrModule->configuration; + device->deviceProperties = deviceOrModule->deviceProperties; + device->serviceProperties = deviceOrModule->serviceProperties; + device->authMethod = deviceOrModule->authMethod; + device->iotEdge_capable = deviceOrModule->iotEdge_capable; + } + } +} + +static void free_nonDeviceEx_members_from_deviceOrModule(IOTHUB_DEVICE_OR_MODULE* deviceOrModule) +{ + if (deviceOrModule != NULL) + { + //free module exclusive fields + free((void*)deviceOrModule->moduleId); + deviceOrModule->moduleId = NULL; + free((void*)deviceOrModule->managedBy); + deviceOrModule->managedBy = NULL; + } +} + +static void move_deviceOrModule_members_to_module(IOTHUB_DEVICE_OR_MODULE* deviceOrModule, IOTHUB_MODULE* module) +{ + if ((deviceOrModule != NULL) && (module != NULL)) + { + if (module->version >= IOTHUB_MODULE_VERSION_1) + { + module->deviceId = deviceOrModule->deviceId; + module->primaryKey = deviceOrModule->primaryKey; + module->secondaryKey = deviceOrModule->secondaryKey; + module->generationId = deviceOrModule->generationId; + module->eTag = deviceOrModule->eTag; + module->connectionState = deviceOrModule->connectionState; + module->connectionStateUpdatedTime = deviceOrModule->connectionStateUpdatedTime; + module->status = deviceOrModule->status; + module->statusReason = deviceOrModule->statusReason; + module->statusUpdatedTime = deviceOrModule->statusUpdatedTime; + module->lastActivityTime = deviceOrModule->lastActivityTime; + module->cloudToDeviceMessageCount = deviceOrModule->cloudToDeviceMessageCount; + module->isManaged = deviceOrModule->isManaged; + module->configuration = deviceOrModule->configuration; + module->deviceProperties = deviceOrModule->deviceProperties; + module->serviceProperties = deviceOrModule->serviceProperties; + module->authMethod = deviceOrModule->authMethod; + module->moduleId = deviceOrModule->moduleId; + module->managedBy = deviceOrModule->managedBy; + } + } +} + +static bool isAuthTypeAllowed(IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod) +{ + bool result; + switch (authMethod) + { + case IOTHUB_REGISTRYMANAGER_AUTH_SPK: + case IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT: + case IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY: + case IOTHUB_REGISTRYMANAGER_AUTH_NONE: + result = true; + break; + + default: + result = false; + break; + } + + return result; +} + +static int strHasNoWhitespace(const char* s) +{ + while (*s) + { + if (isspace(*s) != 0) + return 1; + s++; + } + return 0; +} + +static const char *getStatusStringForJson(IOTHUB_DEVICE_STATUS status) +{ + const char *statusForJson; + + if (IOTHUB_DEVICE_STATUS_DISABLED == status) + { + statusForJson = DEVICE_JSON_DEFAULT_VALUE_DISABLED; + } + else + { + statusForJson = DEVICE_JSON_DEFAULT_VALUE_ENABLED; + } + + return statusForJson; +} + +static const char *getAuthTypeStringForJson(IOTHUB_REGISTRYMANAGER_AUTH_METHOD authMethod) +{ + const char *authTypeForJson; + + if (IOTHUB_REGISTRYMANAGER_AUTH_SPK == authMethod) + { + authTypeForJson = DEVICE_JSON_KEY_DEVICE_AUTH_SAS; + } + else if (IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT == authMethod) + { + authTypeForJson = DEVICE_JSON_KEY_DEVICE_AUTH_SELF_SIGNED; + } + else if (IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY == authMethod) + { + authTypeForJson = DEVICE_JSON_KEY_DEVICE_AUTH_CERTIFICATE_AUTHORITY; + } + else if (IOTHUB_REGISTRYMANAGER_AUTH_NONE == authMethod) + { + authTypeForJson = DEVICE_JSON_KEY_DEVICE_AUTH_NONE; + } + else + { + LogError("Cannot map <%d> to auth type for JSON string", authMethod); + authTypeForJson = NULL; + } + + return authTypeForJson; +} + +static BUFFER_HANDLE constructDeviceOrModuleJson(const IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + BUFFER_HANDLE result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_010: [ IoTHubRegistryManager_CreateDevice shall create a flat "key1:value2,key2:value2..." JSON representation from the given deviceCreateInfo parameter using the following parson APIs: json_value_init_object, json_value_get_object, json_object_set_string, json_object_dotset_string ] */ + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + JSON_Status jsonStatus; + + const char *authTypeForJson; + + int iotEdge_capable = (deviceOrModuleInfo->iotEdge_capable == true) ? 1 : 0; + + if (deviceOrModuleInfo == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("deviceOrModuleInfo cannot be null"); + result = NULL; + } + else if (deviceOrModuleInfo->deviceId == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("Device id cannot be NULL"); + result = NULL; + } + if ((root_value = json_value_init_object()) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_value_init_object failed"); + result = NULL; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_value_get_object failed"); + result = NULL; + } + else if ((json_object_set_string(root_object, DEVICE_JSON_KEY_DEVICE_NAME, deviceOrModuleInfo->deviceId)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_set_string failed for deviceId"); + result = NULL; + } + else if ((deviceOrModuleInfo->moduleId != NULL) && ((json_object_set_string(root_object, DEVICE_JSON_KEY_MODULE_NAME, deviceOrModuleInfo->moduleId)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_set_string failed for moduleId"); + result = NULL; + } + else if ((deviceOrModuleInfo->managedBy != NULL) && ((json_object_set_string(root_object, DEVICE_JSON_KEY_MANAGED_BY, deviceOrModuleInfo->managedBy)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_ERROR_CREATING_JSON if the JSON creation failed ] */ + LogError("json_object_set_string failed for managedBy"); + result = NULL; + } + else if (json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_STATUS, getStatusStringForJson(deviceOrModuleInfo->status)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_dotset_string failed for status"); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_31_001: [** IoTHubRegistryManager_CreateDevice shall set 'type' to "sas"/"selfSigned"/"certificateAuthority" based on deviceOrModuleInfo->authMethod IOTHUB_REGISTRYMANAGER_AUTH_SPK/IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT/IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY **]** */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_31_002: [** IoTHubRegistryManager_UpdateDevice shall set 'type' to "sas"/"selfSigned"/"certificateAuthority" based on deviceOrModuleInfo->authMethod IOTHUB_REGISTRYMANAGER_AUTH_SPK/IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT/IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY **]** */ + else if ((NULL == (authTypeForJson = getAuthTypeStringForJson(deviceOrModuleInfo->authMethod))) || ((json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_AUTH_TYPE, authTypeForJson)) != JSONSuccess)) + { + LogError("json_object_dotset_string failed for authType"); + result = NULL; + } + // + // Static function here. We make the assumption that the auth method has been validated by the caller of this function. + // + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_002: [ IoTHubRegistryManager_CreateDevice shall, if deviceCreateInfo->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_SPK", set "authorization.symmetricKey.primaryKey" to deviceCreateInfo->primaryKey and "authorization.symmetricKey.secondaryKey" to deviceCreateInfo->secondaryKey ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_004: [ IoTHubRegistryManager_UpdateDevice shall, if deviceUpdate->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_SPK", set "authorization.symmetricKey.primaryKey" to deviceCreateInfo->primaryKey and "authorization.symmetricKey.secondaryKey" to deviceCreateInfo->secondaryKey ] */ + else if ((deviceOrModuleInfo->authMethod == IOTHUB_REGISTRYMANAGER_AUTH_SPK) && ((json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_PRIMARY_KEY, deviceOrModuleInfo->primaryKey)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_dotset_string failed for primarykey"); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_002: [ IoTHubRegistryManager_CreateDevice shall, if deviceCreateInfo->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_SPK", set "authorization.symmetricKey.primaryKey" to deviceCreateInfo->primaryKey and "authorization.symmetricKey.secondaryKey" to deviceCreateInfo->secondaryKey ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_004: [ IoTHubRegistryManager_UpdateDevice shall, if deviceUpdate->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_SPK", set "authorization.symmetricKey.primaryKey" to deviceCreateInfo->primaryKey and "authorization.symmetricKey.secondaryKey" to deviceCreateInfo->secondaryKey ] */ + else if ((deviceOrModuleInfo->authMethod == IOTHUB_REGISTRYMANAGER_AUTH_SPK) && ((json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_SECONDARY_KEY, deviceOrModuleInfo->secondaryKey)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_dotset_string failed for secondaryKey"); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_001: [ IoTHubRegistryManager_CreateDevice shall, if deviceCreateInfo->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT", set "authorization.x509Thumbprint.primaryThumbprint" to deviceCreateInfo->primaryKey and "authorization.x509Thumbprint.secondaryThumbprint" to deviceCreateInfo->secondaryKey ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_003: [ IoTHubRegistryManager_UpdateDevice shall, if deviceUpdate->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT", set "authorization.x509Thumbprint.primaryThumbprint" to deviceCreateInfo->primaryKey and "authorization.x509Thumbprint.secondaryThumbprint" to deviceCreateInfo->secondaryKey ] */ + else if ((deviceOrModuleInfo->authMethod == IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT) && ((json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_PRIMARY_THUMBPRINT, deviceOrModuleInfo->primaryKey)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_dotset_string failed for primaryThumbprint"); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_001: [ IoTHubRegistryManager_CreateDevice shall, if deviceCreateInfo->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT", set "authorization.x509Thumbprint.primaryThumbprint" to deviceCreateInfo->primaryKey and "authorization.x509Thumbprint.secondaryThumbprint" to deviceCreateInfo->secondaryKey ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_003: [ IoTHubRegistryManager_UpdateDevice shall, if deviceUpdate->authMethod is equal to "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT", set "authorization.x509Thumbprint.primaryThumbprint" to deviceCreateInfo->primaryKey and "authorization.x509Thumbprint.secondaryThumbprint" to deviceCreateInfo->secondaryKey ] */ + else if ((deviceOrModuleInfo->authMethod == IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT) && ((json_object_dotset_string(root_object, DEVICE_JSON_KEY_DEVICE_SECONDARY_THUMBPRINT, deviceOrModuleInfo->secondaryKey)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_dotset_string failed for secondaryThumbprint"); + result = NULL; + } + else if ((deviceOrModuleInfo->moduleId == NULL) && (json_object_dotset_boolean(root_object, DEVICE_JSON_KEY_CAPABILITIES_IOTEDGE, iotEdge_capable)) != JSONSuccess) + { + LogError("json_object_dotset_string failed for iotEdge capable"); + result = NULL; + } + else + { + char* serialized_string; + if ((serialized_string = json_serialize_to_string(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_serialize_to_string failed"); + result = NULL; + } + else + { + if ((result = BUFFER_create((const unsigned char*)serialized_string, strlen(serialized_string))) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("Buffer_Create failed"); + result = NULL; + } + json_free_serialized_string(serialized_string); + } + } + + if ((jsonStatus = json_object_clear(root_object)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("json_object_clear failed"); + BUFFER_delete(result); + result = NULL; + } + if(root_value != NULL) + json_value_free(root_value); + + return result; +} + +static IOTHUB_REGISTRYMANAGER_RESULT parseDeviceOrModuleJsonObject(JSON_Object* root_object, IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + const char* deviceId = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_NAME); + const char* moduleId = json_object_get_string(root_object, DEVICE_JSON_KEY_MODULE_NAME); + const char* managedBy = json_object_get_string(root_object, DEVICE_JSON_KEY_MANAGED_BY); + const char* primaryKey = NULL; + const char* secondaryKey = NULL; + const char* authType = json_object_dotget_string(root_object, DEVICE_JSON_KEY_DEVICE_AUTH_TYPE); + const char* generationId = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_GENERATION_ID); + const char* eTag = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_ETAG); + const char* connectionState = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_CONNECTIONSTATE); + const char* connectionStateUpdatedTime = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_CONNECTIONSTATEUPDATEDTIME); + const char* status = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_STATUS); + const char* statusReason = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_STATUSREASON); + const char* statusUpdatedTime = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_STATUSUPDATEDTIME); + const char* lastActivityTime = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_LASTACTIVITYTIME); + const char* cloudToDeviceMessageCount = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_CLOUDTODEVICEMESSAGECOUNT); + const char* isManaged = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_ISMANAGED); + const char* configuration = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_CONFIGURATION); + const char* deviceProperties = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_DEVICEROPERTIES); + const char* serviceProperties = json_object_get_string(root_object, DEVICE_JSON_KEY_DEVICE_SERVICEPROPERTIES); + int iotEdge_capable = json_object_dotget_boolean(root_object, DEVICE_JSON_KEY_CAPABILITIES_IOTEDGE); + + if (NULL != authType) + { + if (0 == strcmp(authType, DEVICE_JSON_KEY_DEVICE_AUTH_SAS)) + { + primaryKey = (char*)json_object_dotget_string(root_object, DEVICE_JSON_KEY_DEVICE_PRIMARY_KEY); + secondaryKey = (char*)json_object_dotget_string(root_object, DEVICE_JSON_KEY_DEVICE_SECONDARY_KEY); + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_SPK; + } + else if (0 == strcmp(authType, DEVICE_JSON_KEY_DEVICE_AUTH_SELF_SIGNED)) + { + primaryKey = (char*)json_object_dotget_string(root_object, DEVICE_JSON_KEY_DEVICE_PRIMARY_THUMBPRINT); + secondaryKey = (char*)json_object_dotget_string(root_object, DEVICE_JSON_KEY_DEVICE_SECONDARY_THUMBPRINT); + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT; + } + else if (0 == strcmp(authType, DEVICE_JSON_KEY_DEVICE_AUTH_CERTIFICATE_AUTHORITY)) + { + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_X509_CERTIFICATE_AUTHORITY; + } + else if (0 == strcmp(authType, DEVICE_JSON_KEY_DEVICE_AUTH_NONE)) + { + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_NONE; + } + else + { + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_UNKNOWN; + } + } + else + { + deviceOrModuleInfo->authMethod = IOTHUB_REGISTRYMANAGER_AUTH_UNKNOWN; + } + + if ((deviceId != NULL) && (mallocAndStrcpy_s((char**)&(deviceOrModuleInfo->deviceId), deviceId) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for deviceId"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((moduleId != NULL) && (mallocAndStrcpy_s((char**)&(deviceOrModuleInfo->moduleId), moduleId) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for moduleId"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((primaryKey != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->primaryKey, primaryKey) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for primaryKey"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((secondaryKey != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->secondaryKey, secondaryKey) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for secondaryKey"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((generationId != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->generationId, generationId) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for generationId"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((eTag != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->eTag, eTag) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for eTag"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((connectionStateUpdatedTime != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->connectionStateUpdatedTime, connectionStateUpdatedTime) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for connectionStateUpdatedTime"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((statusReason != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->statusReason, statusReason) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for statusReason"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((statusUpdatedTime != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->statusUpdatedTime, statusUpdatedTime) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for statusUpdatedTime"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((lastActivityTime != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->lastActivityTime, lastActivityTime) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for lastActivityTime"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((configuration != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->configuration, configuration) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for configuration"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((deviceProperties != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->deviceProperties, deviceProperties) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for deviceProperties"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((serviceProperties != NULL) && (mallocAndStrcpy_s((char**)&deviceOrModuleInfo->serviceProperties, serviceProperties) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("mallocAndStrcpy_s failed for serviceProperties"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((managedBy != NULL) && (mallocAndStrcpy_s((char**)&(deviceOrModuleInfo->managedBy), managedBy) != 0)) + { + LogError("mallocAndStrcpy_s failed for managedBy"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + if ((connectionState != NULL) && (strcmp(connectionState, DEVICE_JSON_DEFAULT_VALUE_CONNECTED) == 0)) + { + deviceOrModuleInfo->connectionState = IOTHUB_DEVICE_CONNECTION_STATE_CONNECTED; + } + if ((status != NULL) && (strcmp(status, DEVICE_JSON_DEFAULT_VALUE_ENABLED) == 0)) + { + deviceOrModuleInfo->status = IOTHUB_DEVICE_STATUS_ENABLED; + } + if (cloudToDeviceMessageCount != NULL) + { + deviceOrModuleInfo->cloudToDeviceMessageCount = atoi(cloudToDeviceMessageCount); + } + if ((isManaged != NULL) && (strcmp(isManaged, DEVICE_JSON_DEFAULT_VALUE_TRUE) == 0)) + { + deviceOrModuleInfo->isManaged = true; + } + if ((iotEdge_capable == -1) || (iotEdge_capable == 0)) + { + deviceOrModuleInfo->iotEdge_capable = false; + } + else + { + deviceOrModuleInfo->iotEdge_capable = true; + } + result = IOTHUB_REGISTRYMANAGER_OK; + } + + return result; +} + +static IOTHUB_REGISTRYMANAGER_RESULT parseDeviceOrModuleJson(BUFFER_HANDLE jsonBuffer, IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_024: [ If the deviceOrModuleInfo out parameter is not NULL IoTHubRegistryManager_CreateDevice shall save the received deviceOrModuleInfo to the out parameter and return IOTHUB_REGISTRYMANAGER_OK ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_033: [ IoTHubRegistryManager_GetDevice shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceOrModuleInfo for the following properties: deviceId, primaryKey, secondaryKey, generationId, eTag, connectionState, connectionstateUpdatedTime, status, statusReason, statusUpdatedTime, lastActivityTime, cloudToDeviceMessageCount ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_034: [ If any of the property field above missing from the JSON the property value will not be populated ] */ + if (jsonBuffer == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("jsonBuffer cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if (deviceOrModuleInfo == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("deviceOrModuleInfo cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + const char* bufferStr = NULL; + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + JSON_Status jsonStatus; + + if ((bufferStr = (const char*)BUFFER_u_char(jsonBuffer)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("BUFFER_u_char failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((root_value = json_parse_string(bufferStr)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("json_parse_string failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("json_value_get_object failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + result = parseDeviceOrModuleJsonObject(root_object, deviceOrModuleInfo); + } + + if ((jsonStatus = json_object_clear(root_object)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + LogError("json_object_clear failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + + json_value_free(root_value); + + if (result != IOTHUB_REGISTRYMANAGER_OK) + { + free_deviceOrModule_members(deviceOrModuleInfo); + } + } + return result; +} + +static int addDeviceOrModuleToLinkedListAsDevice(IOTHUB_DEVICE_OR_MODULE* iothubDeviceOrModule, SINGLYLINKEDLIST_HANDLE deviceOrModuleList) +{ + int result; + IOTHUB_DEVICE* device = NULL; + + if ((device = (IOTHUB_DEVICE*)malloc(sizeof(IOTHUB_DEVICE))) == NULL) + { + LogError("Malloc failed for device"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else + { + //Convert to a Device struct + move_deviceOrModule_members_to_device(iothubDeviceOrModule, device); + + if ((singlylinkedlist_add(deviceOrModuleList, device)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_072: [** If populating the deviceList parameter fails IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_ERROR **] */ + LogError("singlylinkedlist_add failed"); + free(device); //only free structure. Because members are still referenced by iothubDeviceOrModule we will free them after + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + result = IOTHUB_REGISTRYMANAGER_OK; + } + } + + return result; +} + +static int addDeviceOrModuleToLinkedListAsModule(IOTHUB_DEVICE_OR_MODULE* iothubDeviceOrModule, SINGLYLINKEDLIST_HANDLE deviceOrModuleList, int version) +{ + int result; + IOTHUB_MODULE* module = NULL; + + if ((module = (IOTHUB_MODULE*)malloc(sizeof(IOTHUB_MODULE))) == NULL) + { + LogError("Malloc failed for module"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else + { + //Convert to a Module struct + module->version = version; + move_deviceOrModule_members_to_module(iothubDeviceOrModule, module); + + if ((singlylinkedlist_add(deviceOrModuleList, module)) == NULL) + { + LogError("singlylinkedlist_add failed"); + free(module); //only free structure. Because members are still referenced by iothubDeviceOrModule we will free them after + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + result = IOTHUB_REGISTRYMANAGER_OK; + } + } + + return result; +} + +static IOTHUB_REGISTRYMANAGER_RESULT parseDeviceOrModuleListJson(BUFFER_HANDLE jsonBuffer, SINGLYLINKEDLIST_HANDLE deviceOrModuleList, IOTHUB_REGISTRYMANAGER_MODEL_TYPE struct_type, int struct_version) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + const char* bufferStr = NULL; + JSON_Value* root_value = NULL; + JSON_Array* device_or_module_array = NULL; + JSON_Status jsonStatus = JSONFailure; + + if (jsonBuffer == NULL) + { + LogError("jsonBuffer cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if (deviceOrModuleList == NULL) + { + LogError("deviceOrModuleList cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + if ((bufferStr = (const char*)BUFFER_u_char(jsonBuffer)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_072: [** If populating the deviceList parameter fails IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_ERROR **] */ + LogError("BUFFER_u_char failed"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else if ((root_value = json_parse_string(bufferStr)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [** If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR **] */ + LogError("json_parse_string failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((device_or_module_array = json_value_get_array(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [** If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR **] */ + LogError("json_value_get_object failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + result = IOTHUB_REGISTRYMANAGER_OK; + + size_t array_count = json_array_get_count(device_or_module_array); + for (size_t i = 0; i < array_count; i++) + { + JSON_Object* device_or_module_object = NULL; + IOTHUB_DEVICE_OR_MODULE iothubDeviceOrModule; + + if ((device_or_module_object = json_array_get_object(device_or_module_array, i)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [** If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR **] */ + LogError("json_array_get_object failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + initializeDeviceOrModuleInfoMembers(&iothubDeviceOrModule); + + result = parseDeviceOrModuleJsonObject(device_or_module_object, &iothubDeviceOrModule); + if (IOTHUB_REGISTRYMANAGER_OK != result) + { + free_deviceOrModule_members(&iothubDeviceOrModule); + } + else + { + if (struct_type == IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE) + { + result = addDeviceOrModuleToLinkedListAsDevice(&iothubDeviceOrModule, deviceOrModuleList); + if (result == IOTHUB_REGISTRYMANAGER_OK) //only free these if we pass, if we fail, will deal with below + { + free_nonDevice_members_from_deviceOrModule(&iothubDeviceOrModule); + } + } + else //IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE + { + result = addDeviceOrModuleToLinkedListAsModule(&iothubDeviceOrModule, deviceOrModuleList, struct_version); + //no nonModule members to free... yet + } + + if (result != IOTHUB_REGISTRYMANAGER_OK) + { + free_deviceOrModule_members(&iothubDeviceOrModule); + } + } + } + + if ((device_or_module_object != NULL) && ((jsonStatus = json_object_clear(device_or_module_object)) != JSONSuccess)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [** If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR **] */ + LogError("json_object_clear failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + if (result != IOTHUB_REGISTRYMANAGER_OK) + { + break; + } + } + } + } + if (device_or_module_array != NULL) + { + if ((jsonStatus = json_array_clear(device_or_module_array)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [** If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR **] */ + LogError("json_array_clear failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + } + + if (root_value != NULL) + { + json_value_free(root_value); + } + + if (result != IOTHUB_REGISTRYMANAGER_OK) + { + if (deviceOrModuleList != NULL) + { + LIST_ITEM_HANDLE itemHandle = singlylinkedlist_get_head_item(deviceOrModuleList); + while (itemHandle != NULL) + { + const void* curr_item = singlylinkedlist_item_get_value(itemHandle); + if (struct_type == IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE) + { + IOTHUB_DEVICE* deviceInfo = (IOTHUB_DEVICE*)curr_item; + free_device_members(deviceInfo); + free(deviceInfo); + } + else //IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE + { + IOTHUB_MODULE* moduleInfo = (IOTHUB_MODULE*)curr_item; + IoTHubRegistryManager_FreeModuleMembers(moduleInfo); + free(moduleInfo); + } + + LIST_ITEM_HANDLE lastHandle = itemHandle; + itemHandle = singlylinkedlist_get_next_item(itemHandle); + singlylinkedlist_remove(deviceOrModuleList, lastHandle); + } + } + } + return result; +} + +static IOTHUB_REGISTRYMANAGER_RESULT parseStatisticsJson(BUFFER_HANDLE jsonBuffer, IOTHUB_REGISTRY_STATISTICS* registryStatistics) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_083: [ IoTHubRegistryManager_GetStatistics shall save the registry statistics to the out value and return IOTHUB_REGISTRYMANAGER_OK ] */ + if (jsonBuffer == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("jsonBuffer cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if (registryStatistics == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("registryStatistics cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + const char* bufferStr = NULL; + JSON_Value* root_value = NULL; + JSON_Object* root_object = NULL; + JSON_Status jsonStatus; + + if ((bufferStr = (const char*)BUFFER_u_char(jsonBuffer)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("BUFFER_u_char failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((root_value = json_parse_string(bufferStr)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("json_parse_string failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else if ((root_object = json_value_get_object(root_value)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("json_value_get_object failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_083: [ IoTHubRegistryManager_GetStatistics shall save the registry statistics to the out value and return IOTHUB_REGISTRYMANAGER_OK ] */ + registryStatistics->totalDeviceCount = (size_t)json_object_get_number(root_object, DEVICE_JSON_KEY_TOTAL_DEVICECOUNT); + registryStatistics->enabledDeviceCount = (size_t)json_object_get_number(root_object, DEVICE_JSON_KEY_ENABLED_DEVICECCOUNT); + registryStatistics->disabledDeviceCount = (size_t)json_object_get_number(root_object, DEVICE_JSON_KEY_DISABLED_DEVICECOUNT); + + result = IOTHUB_REGISTRYMANAGER_OK; + } + + if ((jsonStatus = json_object_clear(root_object)) != JSONSuccess) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("json_object_clear failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + json_value_free(root_value); + } + return result; +} + +static IOTHUB_REGISTRYMANAGER_RESULT createRelativePath(IOTHUB_REQUEST_MODE iotHubRequestMode, const char* deviceName, const char* moduleId, size_t numberOfDevices, char* relativePath) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if ((iotHubRequestMode == IOTHUB_REQUEST_GET_DEVICE_LIST) && (deviceName == NULL)) + { + if ((numberOfDevices <= 0) || (numberOfDevices > IOTHUB_DEVICES_MAX_REQUEST)) + { + numberOfDevices = IOTHUB_DEVICES_MAX_REQUEST; + } + + char numberStr[256]; + result = IOTHUB_REGISTRYMANAGER_ERROR; + if (snprintf(numberStr, 256, "%lu", numberOfDevices) > 0) + { + if (snprintf(relativePath, 256, RELATIVE_PATH_FMT_LIST, numberStr, URL_API_VERSION) > 0) + { + result = IOTHUB_REGISTRYMANAGER_OK; + } + else + { + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + } + else + { + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + } + else if ((iotHubRequestMode == IOTHUB_REQUEST_GET_DEVICE_LIST) && (deviceName != NULL)) + { + if (snprintf(relativePath, 256, RELATIVE_PATH_FMT_MODULE_LIST, deviceName, URL_API_VERSION)) + { + result = IOTHUB_REGISTRYMANAGER_OK; + } + else + { + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + } + else if (iotHubRequestMode == IOTHUB_REQUEST_GET_STATISTICS) + { + if (snprintf(relativePath, 256, RELATIVE_PATH_FMT_STAT, URL_API_VERSION) > 0) + { + result = IOTHUB_REGISTRYMANAGER_OK; + } + else + { + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + } + else + { + if (moduleId != NULL) + { + result = (snprintf(relativePath, 256, RELATIVE_PATH_MODULE_FMT_CRUD, deviceName, moduleId, URL_API_VERSION)) > 0 ? IOTHUB_REGISTRYMANAGER_OK : IOTHUB_REGISTRYMANAGER_ERROR; + } + else + { + result = (snprintf(relativePath, 256, RELATIVE_PATH_FMT_CRUD, deviceName, URL_API_VERSION) > 0) ? IOTHUB_REGISTRYMANAGER_OK : IOTHUB_REGISTRYMANAGER_ERROR; + } + } + + return result; +} + +static HTTP_HEADERS_HANDLE createHttpHeader(IOTHUB_REQUEST_MODE iotHubRequestMode) +{ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_015: [ IoTHubRegistryManager_CreateDevice shall create an HTTP PUT request using the following HTTP headers: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_027: [ IoTHubRegistryManager_GetDevice shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_054: [ IoTHubRegistryManager_DeleteDevice shall add the following headers to the created HTTP GET request : authorization = sasToken, Request - Id = 1001, Accept = application / json, Content - Type = application / json, charset = utf - 8 ] */ + HTTP_HEADERS_HANDLE httpHeader; + + if ((httpHeader = HTTPHeaders_Alloc()) == NULL) + { + LogError("HTTPHeaders_Alloc failed"); + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_AUTHORIZATION, HTTP_HEADER_VAL_AUTHORIZATION) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Authorization header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_REQUEST_ID, HTTP_HEADER_VAL_REQUEST_ID) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for RequestId header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_USER_AGENT, HTTP_HEADER_VAL_USER_AGENT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for User-Agent header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_ACCEPT, HTTP_HEADER_VAL_ACCEPT) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Accept header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_CONTENT_TYPE, HTTP_HEADER_VAL_CONTENT_TYPE) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for Content-Type header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + + if ((iotHubRequestMode == IOTHUB_REQUEST_DELETE) || (iotHubRequestMode == IOTHUB_REQUEST_UPDATE)) + { + if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_IFMATCH, HTTP_HEADER_VAL_IFMATCH) != HTTP_HEADERS_OK) + { + LogError("HTTPHeaders_AddHeaderNameValuePair failed for If-Match header"); + HTTPHeaders_Free(httpHeader); + httpHeader = NULL; + } + + } + return httpHeader; +} + +static STRING_HANDLE createUriPath(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle) +{ + if (registryManagerHandle->deviceId != NULL) + { + return STRING_construct_sprintf("%s%%2Fdevices%%2F%s", registryManagerHandle->hostname, registryManagerHandle->deviceId); + } + else + { + return STRING_construct(registryManagerHandle->hostname); + } +} + +static IOTHUB_REGISTRYMANAGER_RESULT sendHttpRequestCRUD(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REQUEST_MODE iotHubRequestMode, const char* deviceName, const char* moduleId, BUFFER_HANDLE deviceJsonBuffer, size_t numberOfDevices, BUFFER_HANDLE responseBuffer) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + STRING_HANDLE uriResource = NULL; + STRING_HANDLE accessKey = NULL; + STRING_HANDLE keyName = NULL; + HTTPAPIEX_SAS_HANDLE httpExApiSasHandle = NULL; + HTTPAPIEX_HANDLE httpExApiHandle = NULL; + HTTP_HEADERS_HANDLE httpHeader = NULL; + + if ((uriResource = createUriPath(registryManagerHandle)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_099: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_103: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("STRING_construct failed for uriResource"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else if ((accessKey = STRING_construct(registryManagerHandle->sharedAccessKey)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_099: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_103: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("STRING_construct failed for accessKey"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else if ((registryManagerHandle->keyName != NULL) && ((keyName = STRING_construct(registryManagerHandle->keyName)) == NULL)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_099: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_103: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("STRING_construct failed for keyName"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_015: [ IoTHubRegistryManager_CreateDevice shall create an HTTP PUT request using the following HTTP headers: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_027: [ IoTHubRegistryManager_GetDevice shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_043: [ IoTHubRegistryManager_UpdateDevice shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_044: [ IoTHubRegistryManager_UpdateDevice shall create an HTTP PUT request using the createdfollowing HTTP headers : authorization = sasToken, Request - Id = 1001, Accept = application / json, Content - Type = application / json, charset = utf - 8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_054: [ IoTHubRegistryManager_DeleteDevice shall add the following headers to the created HTTP GET request : authorization=sasToken, Request-Id=1001, Accept=application/json, Content-Type=application/json, charset=utf-8 ] */ + else if ((httpHeader = createHttpHeader(iotHubRequestMode)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_019: [ If any of the HTTPAPI call fails IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_104: [ If any of the HTTPAPI call fails IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + LogError("HttpHeader creation failed"); + result = IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_016: [ IoTHubRegistryManager_CreateDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_028: [ IoTHubRegistryManager_GetDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_045: [ IoTHubRegistryManager_UpdateDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_055: [ IoTHubRegistryManager_DeleteDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + else if ((httpExApiSasHandle = HTTPAPIEX_SAS_Create(accessKey, uriResource, keyName)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_019: [ If any of the HTTPAPI call fails IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_104: [ If any of the HTTPAPI call fails IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + LogError("HTTPAPIEX_SAS_Create failed"); + result = IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_017: [ IoTHubRegistryManager_CreateDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_029: [ IoTHubRegistryManager_GetDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_046: [ IoTHubRegistryManager_UpdateDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_056: [ IoTHubRegistryManager_DeleteDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + else if ((httpExApiHandle = HTTPAPIEX_Create(registryManagerHandle->hostname)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_019: [ If any of the HTTPAPI call fails IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_104: [ If any of the HTTPAPI call fails IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + LogError("HTTPAPIEX_Create failed"); + result = IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR; + } + else + { + HTTPAPI_REQUEST_TYPE httpApiRequestType = HTTPAPI_REQUEST_GET; + char relativePath[256]; + unsigned int statusCode; + unsigned char is_error = 0; + + if ((iotHubRequestMode == IOTHUB_REQUEST_CREATE) || (iotHubRequestMode == IOTHUB_REQUEST_UPDATE)) + { + httpApiRequestType = HTTPAPI_REQUEST_PUT; + } + else if (iotHubRequestMode == IOTHUB_REQUEST_DELETE) + { + httpApiRequestType = HTTPAPI_REQUEST_DELETE; + } + else if ((iotHubRequestMode == IOTHUB_REQUEST_GET) || (iotHubRequestMode == IOTHUB_REQUEST_GET_DEVICE_LIST) || (iotHubRequestMode == IOTHUB_REQUEST_GET_STATISTICS)) + { + httpApiRequestType = HTTPAPI_REQUEST_GET; + } + else + { + is_error = 1; + } + + if (is_error) + { + LogError("Invalid request type"); + result = IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_026: [ IoTHubRegistryManager_GetDevice shall create HTTP GET request URL using the given deviceId using the following format: url/devices/[deviceId]?api-version=2017-06-30 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_053: [ IoTHubRegistryManager_DeleteDevice shall create HTTP DELETE request URL using the given deviceId using the following format : url/devices/[deviceId]?api-version ] */ + if (createRelativePath(iotHubRequestMode, deviceName, moduleId, numberOfDevices, relativePath) != IOTHUB_REGISTRYMANAGER_OK) + { + LogError("Failure creating relative path"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_014: [ IoTHubRegistryManager_CreateDevice shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_018: [ IoTHubRegistryManager_CreateDevice shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_030: [ IoTHubRegistryManager_GetDevice shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_047: [ IoTHubRegistryManager_UpdateDevice shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_057: [ IoTHubRegistryManager_DeleteDevice shall execute the HTTP DELETE request by calling HTTPAPIEX_ExecuteRequest ] */ + else if (HTTPAPIEX_SAS_ExecuteRequest(httpExApiSasHandle, httpExApiHandle, httpApiRequestType, relativePath, httpHeader, deviceJsonBuffer, &statusCode, NULL, responseBuffer) != HTTPAPIEX_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_019: [ If any of the HTTPAPI call fails IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + LogError("HTTPAPIEX_SAS_ExecuteRequest failed"); + result = IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR; + } + else + { + if (statusCode > 300) + { + if ((iotHubRequestMode == IOTHUB_REQUEST_CREATE) && (statusCode == 409)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_020: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is 409 then return IOTHUB_REGISTRYMANAGER_DEVICE_EXIST ] */ + result = IOTHUB_REGISTRYMANAGER_DEVICE_EXIST; + } + else if ((iotHubRequestMode == IOTHUB_REQUEST_GET) && (statusCode == 404)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_020: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is 404 then return IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST ] */ + result = IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_021: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_HTTP_STATUS_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_032: [ IoTHubRegistryManager_GetDevice shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_058: [ IoTHubRegistryManager_DeleteDevice shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_HTTP_STATUS_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_048: [ IoTHubRegistryManager_UpdateDevice shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("Http Failure status code %d.", statusCode); + result = IOTHUB_REGISTRYMANAGER_HTTP_STATUS_ERROR; + } + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_022: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceInfo ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_059: [ IoTHubRegistryManager_DeleteDevice shall verify the received HTTP status code and if it is less or equal than 300 then return IOTHUB_REGISTRYMANAGER_OK ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_049: [ IoTHubRegistryManager_UpdateDevice shall verify the received HTTP status code and if it is less or equal than 300 then return IOTHUB_REGISTRYMANAGER_OK ] */ + result = IOTHUB_REGISTRYMANAGER_OK; + } + } + } + } + + HTTPHeaders_Free(httpHeader); + HTTPAPIEX_Destroy(httpExApiHandle); + HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); + STRING_delete(keyName); + STRING_delete(accessKey); + STRING_delete(uriResource); + return result; +} + +static void free_registrymanager_handle(IOTHUB_REGISTRYMANAGER *registryManager) +{ + free(registryManager->hostname); + free(registryManager->iothubName); + free(registryManager->iothubSuffix); + free(registryManager->sharedAccessKey); + free(registryManager->deviceId); + free(registryManager); +} + +IOTHUB_REGISTRYMANAGER_HANDLE IoTHubRegistryManager_Create(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + IOTHUB_REGISTRYMANAGER_HANDLE result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_001: [ If the serviceClientHandle input parameter is NULL IoTHubRegistryManager_Create shall return NULL ] */ + if (serviceClientHandle == NULL) + { + LogError("serviceClientHandle input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_084: [ If any member of the serviceClientHandle input parameter is NULL IoTHubRegistryManager_Create shall return NULL ] */ + IOTHUB_SERVICE_CLIENT_AUTH* serviceClientAuth = (IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle; + + if (serviceClientAuth->hostname == NULL) + { + LogError("authInfo->hostName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubName == NULL) + { + LogError("authInfo->iothubName input parameter cannot be NULL"); + result = NULL; + } + else if (serviceClientAuth->iothubSuffix == NULL) + { + LogError("authInfo->iothubSuffix input parameter cannot be NULL"); + result = NULL; + } + else if ((serviceClientAuth->keyName == NULL) && (serviceClientAuth->deviceId == NULL)) + { + LogError("authInfo->keyName and authInfo->deviceId input parameter cannot both be NULL"); + result = NULL; + } + else if (serviceClientAuth->sharedAccessKey == NULL) + { + LogError("authInfo->sharedAccessKey input parameter cannot be NULL"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_002: [ IoTHubRegistryManager_Create shall allocate memory for a new registry manager instance ] */ + result = malloc(sizeof(IOTHUB_REGISTRYMANAGER)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_003: [ If the allocation failed, IoTHubRegistryManager_Create shall return NULL ] */ + LogError("Malloc failed for IOTHUB_REGISTRYMANAGER"); + } + else + { + memset(result, 0, sizeof(IOTHUB_REGISTRYMANAGER)); + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_004: [ If the allocation successful, IoTHubRegistryManager_Create shall create a IOTHUB_REGISTRYMANAGER_HANDLE from the given IOTHUB_REGISTRYMANAGER_AUTH_HANDLE and return with it ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_085: [ IoTHubRegistryManager_Create shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s. ] */ + if (mallocAndStrcpy_s(&result->hostname, serviceClientAuth->hostname) != 0) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_086: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for hostName"); + free_registrymanager_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_087: [ IoTHubRegistryManager_Create shall allocate memory and copy iothubName to result->iothubName by calling mallocAndStrcpy_s. ] */ + else if (mallocAndStrcpy_s(&result->iothubName, serviceClientAuth->iothubName) != 0) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_088: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for iothubName"); + free_registrymanager_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_089: [ IoTHubRegistryManager_Create shall allocate memory and copy iothubSuffix to result->iothubSuffix by calling mallocAndStrcpy_s. ] */ + else if (mallocAndStrcpy_s(&result->iothubSuffix, serviceClientAuth->iothubSuffix) != 0) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_090: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for iothubSuffix"); + free_registrymanager_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_091: [ IoTHubRegistryManager_Create shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s. ] */ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, serviceClientAuth->sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_092: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_registrymanager_handle(result); + result = NULL; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_093: [ IoTHubRegistryManager_Create shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. ] */ + else if ((serviceClientAuth->keyName != NULL) && (mallocAndStrcpy_s(&result->keyName, serviceClientAuth->keyName) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_094: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for keyName"); + free_registrymanager_handle(result); + result = NULL; + } + else if ((serviceClientAuth->deviceId != NULL) && (mallocAndStrcpy_s(&result->deviceId, serviceClientAuth->deviceId) != 0)) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_094: [ If the mallocAndStrcpy_s fails, IoTHubRegistryManager_Create shall do clean up and return NULL. ] */ + LogError("mallocAndStrcpy_s failed for deviceId"); + free_registrymanager_handle(result); + result = NULL; + } + } + } + } + return result; +} + +void IoTHubRegistryManager_Destroy(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle) +{ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_005: [ If the registryManagerHandle input parameter is NULL IoTHubRegistryManager_Destroy shall return ] */ + if (registryManagerHandle != NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_006 : [ If the registryManagerHandle input parameter is not NULL IoTHubRegistryManager_Destroy shall free the memory of it and return ] */ + IOTHUB_REGISTRYMANAGER* regManHandle = (IOTHUB_REGISTRYMANAGER*)registryManagerHandle; + + free(regManHandle->hostname); + free(regManHandle->iothubName); + free(regManHandle->iothubSuffix); + free(regManHandle->sharedAccessKey); + free(regManHandle->keyName); + free(regManHandle->deviceId); + free(regManHandle); + } +} + +static IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateDeviceOrModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE* deviceOrModuleCreateInfo, IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_007: [ IoTHubRegistryManager_CreateDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceOrModuleCreateInfo == NULL) || (deviceOrModuleInfo == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + initializeDeviceOrModuleInfoMembers(deviceOrModuleInfo); + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_008: [ IoTHubRegistryManager_CreateDevice shall verify the deviceOrModuleCreateInfo->deviceId input parameter and if it is NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if (deviceOrModuleCreateInfo->deviceId == NULL) + { + LogError("deviceId cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_009: [ IoTHubRegistryManager_CreateDevice shall verify the deviceOrModuleCreateInfo->deviceId input parameter and if it contains space(s) then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + else if ((strHasNoWhitespace(deviceOrModuleCreateInfo->deviceId)) != 0) + { + LogError("deviceId cannot contain spaces"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (isAuthTypeAllowed(deviceOrModuleCreateInfo->authMethod) == false) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_006: [ IoTHubRegistryManager_CreateDevice shall cleanup and return IOTHUB_REGISTRYMANAGER_INVALID_ARG if deviceUpdate->authMethod is not "IOTHUB_REGISTRYMANAGER_AUTH_SPK" or "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT" ] */ + LogError("Invalid authorization type specified"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_095: [ IoTHubRegistryManager_CreateDevice shall allocate memory for device info structure by calling malloc ] */ + IOTHUB_DEVICE_OR_MODULE* tempDeviceOrModuleInfo; + if ((tempDeviceOrModuleInfo = malloc(sizeof(IOTHUB_DEVICE_OR_MODULE))) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_096 : [ If the malloc fails, IoTHubRegistryManager_Create shall do clean up and return IOTHUB_REGISTRYMANAGER_ERROR. ] */ + LogError("Malloc failed for tempDeviceOrModuleInfo"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_011: [ IoTHubRegistryManager_CreateDevice shall set the "deviceId" value to the deviceOrModuleCreateInfo->deviceId ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_012: [ IoTHubRegistryManager_CreateDevice shall set the "symmetricKey" value to deviceOrModuleCreateInfo->primaryKey and deviceOrModuleCreateInfo->secondaryKey ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_117: [ IoTHubRegistryManager_CreateDevice shall set the "status" value to the IOTHUB_DEVICE_STATUS_ENABLED ] */ + memset(tempDeviceOrModuleInfo, 0, sizeof(*tempDeviceOrModuleInfo)); + tempDeviceOrModuleInfo->deviceId = deviceOrModuleCreateInfo->deviceId; + tempDeviceOrModuleInfo->moduleId = deviceOrModuleCreateInfo->moduleId; + tempDeviceOrModuleInfo->primaryKey = deviceOrModuleCreateInfo->primaryKey; + tempDeviceOrModuleInfo->secondaryKey = deviceOrModuleCreateInfo->secondaryKey; + tempDeviceOrModuleInfo->authMethod = deviceOrModuleCreateInfo->authMethod; + tempDeviceOrModuleInfo->status = IOTHUB_DEVICE_STATUS_ENABLED; + tempDeviceOrModuleInfo->iotEdge_capable = deviceOrModuleCreateInfo->iotEdge_capable; + tempDeviceOrModuleInfo->managedBy = deviceOrModuleCreateInfo->managedBy; + + BUFFER_HANDLE deviceJsonBuffer = NULL; + BUFFER_HANDLE responseBuffer = NULL; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_010: [ IoTHubRegistryManager_CreateDevice shall create a flat "key1:value2,key2:value2..." JSON representation from the given deviceOrModuleCreateInfo parameter using the following parson APIs: json_value_init_object, json_value_get_object, json_object_set_string, json_object_dotset_string ] */ + if ((deviceJsonBuffer = constructDeviceOrModuleJson(tempDeviceOrModuleInfo)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_013: [ IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("Json creation failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_097: [ IoTHubRegistryManager_CreateDevice shall allocate memory for response buffer by calling BUFFER_new ] */ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_098 : [ If the BUFFER_new fails, IoTHubRegistryManager_CreateDevice shall do clean up and return IOTHUB_REGISTRYMANAGER_ERROR. ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_014: [ IoTHubRegistryManager_CreateDevice shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_015: [ IoTHubRegistryManager_CreateDevice shall create an HTTP PUT request using the following HTTP headers: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_016: [ IoTHubRegistryManager_CreateDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_017: [ IoTHubRegistryManager_CreateDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_018: [ IoTHubRegistryManager_CreateDevice shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_CREATE, deviceOrModuleCreateInfo->deviceId, deviceOrModuleCreateInfo->moduleId, deviceJsonBuffer, 0, responseBuffer)) == IOTHUB_REGISTRYMANAGER_ERROR) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_019: [ If any of the HTTPAPI call fails IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_099: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_CreateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("Failure sending HTTP request for create device"); + } + else if (result == IOTHUB_REGISTRYMANAGER_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_022: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceOrModuleInfo ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_023: [ If the JSON parsing failed, IoTHubRegistryManager_CreateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_024: [ If the deviceOrModuleInfo out parameter is not NULL IoTHubRegistryManager_CreateDevice shall save the received deviceOrModuleInfo to the out parameter and return IOTHUB_REGISTRYMANAGER_OK ] */ + result = parseDeviceOrModuleJson(responseBuffer, deviceOrModuleInfo); + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_020: [ IoTHubRegistryManager_CreateDevice shall verify the received HTTP status code and if it is 409 then return IOTHUB_REGISTRYMANAGER_DEVICE_EXIST ] */ + } + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_100: [ IoTHubRegistryManager_CreateDevice shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + if (deviceJsonBuffer != NULL) + { + BUFFER_delete(deviceJsonBuffer); + } + } + free(tempDeviceOrModuleInfo); + } + } + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_DEVICE_CREATE* deviceCreateInfo, IOTHUB_DEVICE* deviceInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_007: [ IoTHubRegistryManager_CreateDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceCreateInfo == NULL) || (deviceInfo == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE deviceOrModuleCreateInfo; + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + + memset(&deviceOrModuleInfo, 0, sizeof(deviceOrModuleInfo)); + memset(&deviceOrModuleCreateInfo, 0, sizeof(deviceOrModuleCreateInfo)); + deviceOrModuleCreateInfo.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE; + deviceOrModuleCreateInfo.deviceId = deviceCreateInfo->deviceId; + deviceOrModuleCreateInfo.primaryKey = deviceCreateInfo->primaryKey; + deviceOrModuleCreateInfo.secondaryKey = deviceCreateInfo->secondaryKey; + deviceOrModuleCreateInfo.authMethod = deviceCreateInfo->authMethod; + deviceOrModuleCreateInfo.iotEdge_capable = false; //IOTHUB_DEVICE does not have this field, so set it to disabled + + result = IoTHubRegistryManager_CreateDeviceOrModule(registryManagerHandle, &deviceOrModuleCreateInfo, &deviceOrModuleInfo); + if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_device(&deviceOrModuleInfo, deviceInfo); + free_nonDevice_members_from_deviceOrModule(&deviceOrModuleInfo); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_DEVICE_CREATE_EX* deviceCreateInfo, IOTHUB_DEVICE_EX* deviceInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if ((registryManagerHandle == NULL) || (deviceCreateInfo == NULL) || (deviceInfo == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if ((deviceCreateInfo->version < IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_1) || + (deviceCreateInfo->version > IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_LATEST)) + { + LogError("deviceCreateInfo must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else if ((deviceInfo->version < IOTHUB_DEVICE_EX_VERSION_1) || + (deviceInfo->version > IOTHUB_DEVICE_EX_VERSION_LATEST)) + { + LogError("deviceInfo must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE deviceOrModuleCreateInfo; + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + + memset(&deviceOrModuleInfo, 0, sizeof(deviceOrModuleInfo)); + memset(&deviceOrModuleCreateInfo, 0, sizeof(deviceOrModuleCreateInfo)); + deviceOrModuleCreateInfo.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE; + + if (deviceCreateInfo->version >= IOTHUB_REGISTRY_DEVICE_CREATE_EX_VERSION_1) + { + deviceOrModuleCreateInfo.deviceId = deviceCreateInfo->deviceId; + deviceOrModuleCreateInfo.primaryKey = deviceCreateInfo->primaryKey; + deviceOrModuleCreateInfo.secondaryKey = deviceCreateInfo->secondaryKey; + deviceOrModuleCreateInfo.authMethod = deviceCreateInfo->authMethod; + deviceOrModuleCreateInfo.iotEdge_capable = deviceCreateInfo->iotEdge_capable; + } + + result = IoTHubRegistryManager_CreateDeviceOrModule(registryManagerHandle, &deviceOrModuleCreateInfo, &deviceOrModuleInfo); + if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_deviceEx(&deviceOrModuleInfo, deviceInfo); + free_nonDeviceEx_members_from_deviceOrModule(&deviceOrModuleInfo); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDeviceOrModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, const char* moduleId, IOTHUB_DEVICE_OR_MODULE* deviceOrModuleInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_025: [ IoTHubRegistryManager_GetDevice shall verify the registryManagerHandle and deviceId input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + BUFFER_HANDLE responseBuffer; + initializeDeviceOrModuleInfoMembers(deviceOrModuleInfo); + + if ((responseBuffer = BUFFER_new()) == NULL) + { + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_026: [ IoTHubRegistryManager_GetDevice shall create HTTP GET request URL using the given deviceId using the following format: url/devices/[deviceId]?api-version=2017-06-30 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_027: [ IoTHubRegistryManager_GetDevice shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_028: [ IoTHubRegistryManager_GetDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_029: [ IoTHubRegistryManager_GetDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_030: [ IoTHubRegistryManager_GetDevice shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_GET, deviceId, moduleId, NULL, 0, responseBuffer)) == IOTHUB_REGISTRYMANAGER_ERROR) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_031: [ If any of the HTTPAPI call fails IoTHubRegistryManager_GetDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("Failure sending HTTP request for create device"); + } + else if (result == IOTHUB_REGISTRYMANAGER_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_033: [ IoTHubRegistryManager_GetDevice shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceInfo for the following properties: deviceId, primaryKey, secondaryKey, generationId, eTag, connectionState, connectionstateUpdatedTime, status, statusReason, statusUpdatedTime, lastActivityTime, cloudToDeviceMessageCount ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_034: [ If any of the property field above missing from the JSON the property value will not be populated ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_035: [ If the JSON parsing failed, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_036: [ If the received JSON is empty, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_037: [ If the deviceInfo out parameter if not NULL IoTHubRegistryManager_GetDevice shall save the received deviceInfo to the out parameter and return IOTHUB_REGISTRYMANAGER_OK ] */ + if ((result = parseDeviceOrModuleJson(responseBuffer, deviceOrModuleInfo)) == IOTHUB_REGISTRYMANAGER_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_036: [ If the received JSON is empty, IoTHubRegistryManager_GetDevice shall return IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST ] */ + if (deviceOrModuleInfo->deviceId == NULL) + { + free_deviceOrModule_members(deviceOrModuleInfo); + result = IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST; + } + } + } + + BUFFER_delete(responseBuffer); + } + return result; + +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, IOTHUB_DEVICE* deviceInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_007: [ IoTHubRegistryManager_CreateDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceId == NULL) || (deviceInfo == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + memset(&deviceOrModuleInfo, 0, sizeof(deviceOrModuleInfo)); + + result = IoTHubRegistryManager_GetDeviceOrModule(registryManagerHandle, deviceId, NULL, &deviceOrModuleInfo); + if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_device(&deviceOrModuleInfo, deviceInfo); + free_nonDevice_members_from_deviceOrModule(&deviceOrModuleInfo); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, IOTHUB_DEVICE_EX* deviceInfo) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_007: [ IoTHubRegistryManager_CreateDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceId == NULL) || (deviceInfo == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if ((deviceInfo->version < IOTHUB_DEVICE_EX_VERSION_1) || + (deviceInfo->version > IOTHUB_DEVICE_EX_VERSION_LATEST)) + { + LogError("deviceInfo must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + memset(&deviceOrModuleInfo, 0, sizeof(deviceOrModuleInfo)); + + result = IoTHubRegistryManager_GetDeviceOrModule(registryManagerHandle, deviceId, NULL, &deviceOrModuleInfo); + if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_deviceEx(&deviceOrModuleInfo, deviceInfo); + free_nonDeviceEx_members_from_deviceOrModule(&deviceOrModuleInfo); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateDeviceOrModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE* deviceOrModuleUpdate) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_038: [ IoTHubRegistryManager_UpdateDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceOrModuleUpdate == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_039: [ IoTHubRegistryManager_UpdateDevice shall verify the deviceCreateInfo->deviceId input parameter and if it is NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if (deviceOrModuleUpdate->deviceId == NULL) + { + LogError("deviceId cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (isAuthTypeAllowed(deviceOrModuleUpdate->authMethod) == false) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_06_005: [ IoTHubRegistryManager_UpdateDevice shall clean up and return IOTHUB_REGISTRYMANAGER_INVALID_ARG if deviceUpdate->authMethod is not "IOTHUB_REGISTRYMANAGER_AUTH_SPK" or "IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT" ] */ + LogError("Invalid authorization type specified"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_106: [ IoTHubRegistryManager_UpdateDevice shall allocate memory for device info structure by calling malloc ] */ + IOTHUB_DEVICE_OR_MODULE* tempDeviceOrModuleInfo; + if ((tempDeviceOrModuleInfo = malloc(sizeof(IOTHUB_DEVICE_OR_MODULE))) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_108: [ If the malloc fails, IoTHubRegistryManager_UpdateDevice shall do clean up and return NULL ] */ + LogError("Malloc failed for tempDeviceOrModuleInfo"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_118: [ IoTHubRegistryManager_CreateDevice shall set the "status" value to the deviceCreateInfo->status ] */ + (void)memset(tempDeviceOrModuleInfo, 0, sizeof(IOTHUB_DEVICE_OR_MODULE)); + tempDeviceOrModuleInfo->deviceId = deviceOrModuleUpdate->deviceId; + tempDeviceOrModuleInfo->primaryKey = deviceOrModuleUpdate->primaryKey; + tempDeviceOrModuleInfo->secondaryKey = deviceOrModuleUpdate->secondaryKey; + tempDeviceOrModuleInfo->authMethod = deviceOrModuleUpdate->authMethod; + tempDeviceOrModuleInfo->status = deviceOrModuleUpdate->status; + tempDeviceOrModuleInfo->moduleId = deviceOrModuleUpdate->moduleId; + tempDeviceOrModuleInfo->iotEdge_capable = deviceOrModuleUpdate->iotEdge_capable; + tempDeviceOrModuleInfo->managedBy = deviceOrModuleUpdate->managedBy; + + BUFFER_HANDLE deviceJsonBuffer = NULL; + BUFFER_HANDLE responseBuffer = NULL; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_041: [ IoTHubRegistryManager_UpdateDevice shall create a flat "key1:value2,key2:value2..." JSON representation from the given deviceCreateInfo parameter using the following parson APIs : json_value_init_object, json_value_get_object, json_object_set_string, json_object_dotset_string ] */ + if ((deviceJsonBuffer = constructDeviceOrModuleJson(tempDeviceOrModuleInfo)) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_042: [ IoTHubRegistryManager_UpdateDevice shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR if the JSON creation failed ] */ + LogError("Json creation failed"); + result = IOTHUB_REGISTRYMANAGER_JSON_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_101: [ IoTHubRegistryManager_UpdateDevice shall allocate memory for response buffer by calling BUFFER_new ] */ + else if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_102: [ If the BUFFER_new fails, IoTHubRegistryManager_UpdateDevice shall do clean up and return IOTHUB_REGISTRYMANAGER_ERROR. ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_043: [ IoTHubRegistryManager_UpdateDevice shall create an HTTP PUT request using the created JSON ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_044: [ IoTHubRegistryManager_UpdateDevice shall create an HTTP PUT request using the createdfollowing HTTP headers : authorization = sasToken, Request - Id = 1001, Accept = application / json, Content - Type = application / json, charset = utf - 8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_045: [ IoTHubRegistryManager_UpdateDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_046: [ IoTHubRegistryManager_UpdateDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_047: [ IoTHubRegistryManager_UpdateDevice shall execute the HTTP PUT request by calling HTTPAPIEX_ExecuteRequest ] */ + else if ((result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_UPDATE, deviceOrModuleUpdate->deviceId, deviceOrModuleUpdate->moduleId, deviceJsonBuffer, 0, responseBuffer)) == IOTHUB_REGISTRYMANAGER_ERROR) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_103: [ If any of the call fails during the HTTP creation IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_104: [ If any of the HTTPAPI call fails IoTHubRegistryManager_UpdateDevice shall fail and return IOTHUB_REGISTRYMANAGER_HTTPAPI_ERROR ] */ + LogError("Failure sending HTTP request for update device"); + } + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_105: [ IoTHubRegistryManager_UpdateDevice shall do clean up before return ] */ + if (deviceJsonBuffer != NULL) + { + BUFFER_delete(deviceJsonBuffer); + } + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + } + free(tempDeviceOrModuleInfo); + } + } + return result; + +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_DEVICE_UPDATE* deviceUpdate) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if (registryManagerHandle == NULL || deviceUpdate == NULL) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE deviceOrModuleUpdate; + memset(&deviceOrModuleUpdate, 0, sizeof(deviceOrModuleUpdate)); + + //Convert to generic update struct + deviceOrModuleUpdate.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE; + deviceOrModuleUpdate.deviceId = deviceUpdate->deviceId; + deviceOrModuleUpdate.primaryKey = deviceUpdate->primaryKey; + deviceOrModuleUpdate.secondaryKey = deviceUpdate->secondaryKey; + deviceOrModuleUpdate.status = deviceUpdate->status; + deviceOrModuleUpdate.authMethod = deviceUpdate->authMethod; + deviceOrModuleUpdate.iotEdge_capable = false; //IOTHUB_REGISTRY_DEVICE_UPDATE does not have this field, so set false + + result = IoTHubRegistryManager_UpdateDeviceOrModule(registryManagerHandle, &deviceOrModuleUpdate); + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateDevice_Ex(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_DEVICE_UPDATE_EX* deviceUpdate) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if (registryManagerHandle == NULL || deviceUpdate == NULL) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (deviceUpdate->version < IOTHUB_REGISTRY_DEVICE_UPDATE_EX_VERSION_1 || deviceUpdate->version > IOTHUB_REGISTRY_DEVICE_UPDATE_EX_VERSION_LATEST) + { + LogError("deviceUpdate must have valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE deviceOrModuleUpdate; + memset(&deviceOrModuleUpdate, 0, sizeof(deviceOrModuleUpdate)); + + //Convert to generic update struct + deviceOrModuleUpdate.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE; + + if (deviceUpdate->version >= IOTHUB_REGISTRY_DEVICE_UPDATE_EX_VERSION_1) + { + deviceOrModuleUpdate.deviceId = deviceUpdate->deviceId; + deviceOrModuleUpdate.primaryKey = deviceUpdate->primaryKey; + deviceOrModuleUpdate.secondaryKey = deviceUpdate->secondaryKey; + deviceOrModuleUpdate.status = deviceUpdate->status; + deviceOrModuleUpdate.authMethod = deviceUpdate->authMethod; + deviceOrModuleUpdate.iotEdge_capable = deviceUpdate->iotEdge_capable; + } + + result = IoTHubRegistryManager_UpdateDeviceOrModule(registryManagerHandle, &deviceOrModuleUpdate); + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_DeleteDevice(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_052: [ IoTHubRegistryManager_DeleteDevice shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_053: [ IoTHubRegistryManager_DeleteDevice shall create HTTP DELETE request URL using the given deviceId using the following format : url / devices / [deviceId] ? api - version ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_054: [ IoTHubRegistryManager_DeleteDevice shall add the following headers to the created HTTP GET request : authorization = sasToken, Request - Id = 1001, Accept = application / json, Content - Type = application / json, charset = utf - 8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_055: [ IoTHubRegistryManager_DeleteDevice shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_056: [ IoTHubRegistryManager_DeleteDevice shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_057: [ IoTHubRegistryManager_DeleteDevice shall execute the HTTP DELETE request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_058: [ IoTHubRegistryManager_DeleteDevice shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_HTTP_STATUS_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_059: [ IoTHubRegistryManager_DeleteDevice shall verify the received HTTP status code and if it is less or equal than 300 then return IOTHUB_REGISTRYMANAGER_OK ] */ + result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_DELETE, deviceId, NULL, NULL, 0, NULL); + } + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetModuleOrDeviceList(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, size_t numberOfDevices, SINGLYLINKEDLIST_HANDLE deviceOrModuleList, IOTHUB_REGISTRYMANAGER_MODEL_TYPE struct_type, int struct_version) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_060: [ IoTHubRegistryManager_GetDeviceList shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (deviceOrModuleList == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_061: [ IoTHubRegistryManager_GetDeviceList shall verify if the numberOfDevices input parameter is between 1 and 1000 and if it is not then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + else if ((numberOfDevices == 0) || (numberOfDevices > IOTHUB_DEVICES_MAX_REQUEST)) + { + LogError("numberOfDevices has to be between 1 and 1000"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if ((struct_type == IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE) && ((struct_version < IOTHUB_MODULE_VERSION_1) || (struct_version > IOTHUB_MODULE_VERSION_LATEST))) + { + LogError("Invalid module version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + BUFFER_HANDLE responseBuffer = NULL; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_109: [ IoTHubRegistryManager_GetDeviceList shall allocate memory for response buffer by calling BUFFER_new ] */ + if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_110: [ If the BUFFER_new fails, IoTHubRegistryManager_GetDeviceList shall do clean up and return NULL ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_062: [ IoTHubRegistryManager_GetDeviceList shall create HTTP GET request for numberOfDevices using the follwoing format: url/devices/?top=[numberOfDevices]&api-version ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_063: [ IoTHubRegistryManager_GetDeviceList shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_064: [ IoTHubRegistryManager_GetDeviceList shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_065: [ IoTHubRegistryManager_GetDeviceList shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_066: [ IoTHubRegistryManager_GetDeviceList shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_067: [ IoTHubRegistryManager_GetDeviceList shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_068: [ IoTHubRegistryManager_GetDeviceList shall verify the received HTTP status code and if it is less or equal than 300 then try to parse the response JSON to deviceList ] */ + else if ((result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_GET_DEVICE_LIST, deviceId, NULL, NULL, numberOfDevices, responseBuffer)) == IOTHUB_REGISTRYMANAGER_ERROR) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_115: [ If any of the HTTPAPI call fails IoTHubRegistryManager_GetDeviceList shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("Failure sending HTTP request for get device list"); + } + else if (result == IOTHUB_REGISTRYMANAGER_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_069: [ IoTHubRegistryManager_GetDeviceList shall use the following parson APIs to parse the response JSON: json_parse_string, json_value_get_object, json_object_get_string, json_object_dotget_string ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_070: [ If any of the parson API fails, IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_JSON_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_071: [ IoTHubRegistryManager_GetDeviceList shall populate the deviceList parameter with structures of type "IOTHUB_DEVICE" ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_072: [ If populating the deviceList parameter fails IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_073: [ If populating the deviceList parameter successful IoTHubRegistryManager_GetDeviceList shall return IOTHUB_REGISTRYMANAGER_OK ] */ + result = parseDeviceOrModuleListJson(responseBuffer, deviceOrModuleList, struct_type, struct_version); + } + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_111: [ IoTHubRegistryManager_GetDeviceList shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + } + return result; +} + +/* DEPRECATED: IoTHubRegistryManager_GetDeviceList is deprecated and may be removed from a future release. */ +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetDeviceList(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, size_t numberOfDevices, SINGLYLINKEDLIST_HANDLE deviceList) +{ + return IoTHubRegistryManager_GetModuleOrDeviceList(registryManagerHandle, NULL, numberOfDevices, deviceList, IOTHUB_REGISTRYMANAGER_MODEL_TYPE_DEVICE, 0); +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetStatistics(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_STATISTICS* registryStatistics) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_074: [ IoTHubRegistryManager_GetStatistics shall verify the input parameters and if any of them are NULL then return IOTHUB_REGISTRYMANAGER_INVALID_ARG ] */ + if ((registryManagerHandle == NULL) || (registryStatistics == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + BUFFER_HANDLE responseBuffer; + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_112: [ IoTHubRegistryManager_GetStatistics shall allocate memory for response buffer by calling BUFFER_new ] */ + if ((responseBuffer = BUFFER_new()) == NULL) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_113: [ If the BUFFER_new fails, IoTHubRegistryManager_GetStatistics shall do clean up and return NULL ] */ + LogError("BUFFER_new failed for responseBuffer"); + result = IOTHUB_REGISTRYMANAGER_ERROR; + } + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_075: [ IoTHubRegistryManager_GetStatistics shall create HTTP GET request for statistics using the following format: url/statistics/devices?api-version ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_076: [ IoTHubRegistryManager_GetStatistics shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_077: [ IoTHubRegistryManager_GetStatistics shall create an HTTPAPIEX_SAS_HANDLE handle by calling HTTPAPIEX_SAS_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_078: [ IoTHubRegistryManager_GetStatistics shall create an HTTPAPIEX_HANDLE handle by calling HTTPAPIEX_Create ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_079: [ IoTHubRegistryManager_GetStatistics shall execute the HTTP GET request by calling HTTPAPIEX_ExecuteRequest ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_080: [ IoTHubRegistryManager_GetStatistics shall verify the received HTTP status code and if it is greater than 300 then return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_081: [ IoTHubRegistryManager_GetStatistics shall verify the received HTTP status code and if it is less or equal than 300 then use the following parson APIs to parse the response JSON to registry statistics structure: json_parse_string, json_value_get_object, json_object_get_string, json_object_dotget_string ] */ + else if ((result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_GET_STATISTICS, NULL, NULL, NULL, 0, responseBuffer)) == IOTHUB_REGISTRYMANAGER_ERROR) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_116: [ If any of the HTTPAPI call fails IoTHubRegistryManager_GetStatistics shall fail and return IOTHUB_REGISTRYMANAGER_ERROR ] */ + LogError("Failure sending HTTP request for get registry statistics"); + } + else if (result == IOTHUB_REGISTRYMANAGER_OK) + { + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_082: [ If the parsing failed, IoTHubRegistryManager_GetStatistics shall return IOTHUB_REGISTRYMANAGER_ERROR ] */ + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_083: [ IoTHubRegistryManager_GetStatistics shall save the registry statistics to the out value and return IOTHUB_REGISTRYMANAGER_OK ] */ + result = parseStatisticsJson(responseBuffer, registryStatistics); + } + + /*Codes_SRS_IOTHUBREGISTRYMANAGER_12_114: [ IoTHubRegistryManager_GetStatistics shall do clean up before return ] */ + if (responseBuffer != NULL) + { + BUFFER_delete(responseBuffer); + } + } + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_CreateModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const IOTHUB_REGISTRY_MODULE_CREATE* moduleCreate, IOTHUB_MODULE* module) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if ((registryManagerHandle == NULL) || (module == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if ((moduleCreate == NULL) || (moduleCreate->moduleId == NULL)) + { + LogError("moduleId cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (moduleCreate->version < IOTHUB_REGISTRY_MODULE_CREATE_VERSION_1 || moduleCreate->version > IOTHUB_REGISTRY_MODULE_CREATE_VERSION_LATEST) + { + LogError("moduleCreate must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else if (module->version < IOTHUB_MODULE_VERSION_1 || module->version > IOTHUB_MODULE_VERSION_LATEST) + { + LogError("module must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_CREATE deviceOrModuleCreateInfo; + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + memset(&deviceOrModuleCreateInfo, 0, sizeof(deviceOrModuleCreateInfo)); + memset(&deviceOrModuleInfo, 0, sizeof(deviceOrModuleInfo)); + + //Convert to generic create struct + deviceOrModuleCreateInfo.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE; + if (moduleCreate->version >= IOTHUB_REGISTRY_MODULE_CREATE_VERSION_1) + { + deviceOrModuleCreateInfo.deviceId = moduleCreate->deviceId; + deviceOrModuleCreateInfo.primaryKey = moduleCreate->primaryKey; + deviceOrModuleCreateInfo.secondaryKey = moduleCreate->secondaryKey; + deviceOrModuleCreateInfo.authMethod = moduleCreate->authMethod; + deviceOrModuleCreateInfo.moduleId = moduleCreate->moduleId; + deviceOrModuleCreateInfo.managedBy = moduleCreate->managedBy; + } + + result = IoTHubRegistryManager_CreateDeviceOrModule(registryManagerHandle, &deviceOrModuleCreateInfo, &deviceOrModuleInfo); + if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_module(&deviceOrModuleInfo, module); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, const char* moduleId, IOTHUB_MODULE* module) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if ((registryManagerHandle == NULL) || (deviceId == NULL) || (moduleId == NULL) || (module == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (module->version < IOTHUB_MODULE_VERSION_1 || module->version > IOTHUB_MODULE_VERSION_LATEST) + { + LogError("module must have a valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_DEVICE_OR_MODULE deviceOrModuleInfo; + + result = IoTHubRegistryManager_GetDeviceOrModule(registryManagerHandle, deviceId, moduleId, &deviceOrModuleInfo); + if (deviceOrModuleInfo.moduleId == NULL) + { + result = IOTHUB_REGISTRYMANAGER_DEVICE_NOT_EXIST; + } + else if (result == IOTHUB_REGISTRYMANAGER_OK) + { + move_deviceOrModule_members_to_module(&deviceOrModuleInfo, module); + } + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_UpdateModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, IOTHUB_REGISTRY_MODULE_UPDATE* moduleUpdate) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if (registryManagerHandle == NULL) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if ((moduleUpdate == NULL) || (moduleUpdate->moduleId == NULL)) + { + LogError("moduleId cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else if (moduleUpdate->version < IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_1 || moduleUpdate->version > IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_LATEST) + { + LogError("moduleUpdate must have valid version"); + result = IOTHUB_REGISTRYMANAGER_INVALID_VERSION; + } + else + { + IOTHUB_REGISTRY_DEVICE_OR_MODULE_UPDATE deviceOrModuleUpdate; + memset(&deviceOrModuleUpdate, 0, sizeof(deviceOrModuleUpdate)); + + //Convert to generic update struct + deviceOrModuleUpdate.type = IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE; + if (moduleUpdate-> version >= IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_1) + { + deviceOrModuleUpdate.deviceId = moduleUpdate->deviceId; + deviceOrModuleUpdate.primaryKey = moduleUpdate->primaryKey; + deviceOrModuleUpdate.secondaryKey = moduleUpdate->secondaryKey; + deviceOrModuleUpdate.status = moduleUpdate->status; + deviceOrModuleUpdate.authMethod = moduleUpdate->authMethod; + deviceOrModuleUpdate.moduleId = moduleUpdate->moduleId; + deviceOrModuleUpdate.managedBy = moduleUpdate->managedBy; + } + + result = IoTHubRegistryManager_UpdateDeviceOrModule(registryManagerHandle, &deviceOrModuleUpdate); + } + + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_DeleteModule(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, const char* moduleId) +{ + IOTHUB_REGISTRYMANAGER_RESULT result; + + if ((registryManagerHandle == NULL) || (deviceId == NULL) || (moduleId == NULL)) + { + LogError("Input parameter cannot be NULL"); + result = IOTHUB_REGISTRYMANAGER_INVALID_ARG; + } + else + { + result = sendHttpRequestCRUD(registryManagerHandle, IOTHUB_REQUEST_DELETE, deviceId, moduleId, NULL, 0, NULL); + } + return result; +} + +IOTHUB_REGISTRYMANAGER_RESULT IoTHubRegistryManager_GetModuleList(IOTHUB_REGISTRYMANAGER_HANDLE registryManagerHandle, const char* deviceId, SINGLYLINKEDLIST_HANDLE moduleList, int module_version) +{ + return IoTHubRegistryManager_GetModuleOrDeviceList(registryManagerHandle, deviceId, IOTHUB_DEVICES_MAX_REQUEST, moduleList, IOTHUB_REGISTRYMANAGER_MODEL_TYPE_MODULE, module_version); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_sc_version.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "iothub_sc_version.h" + +const char* IoTHubServiceClient_GetVersionString(void) +{ + /*Codes_SRS_IOTHUBSERVICECLIENT_12_040: [IoTHubServiceClient_GetVersionString shall return a pointer to a constant string which indicates the version of IoTHubServiceClient API.]*/ + return IOTHUB_SERVICE_CLIENT_VERSION; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_service_client.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,58 @@ +LIBRARY iothub_service_client +EXPORTS + IOTHUB_MESSAGE_RESULTStringStorage + IOTHUBMESSAGE_CONTENT_TYPEStringStorage + IOTHUB_MESSAGE_RESULTStrings + IOTHUB_MESSAGE_RESULT_FromString + IOTHUBMESSAGE_CONTENT_TYPEStrings + IOTHUBMESSAGE_CONTENT_TYPE_FromString + IoTHubMessage_CreateFromByteArray + IoTHubMessage_CreateFromString + IoTHubMessage_Clone + IoTHubMessage_GetByteArray + IoTHubMessage_GetString + IoTHubMessage_GetContentType + IoTHubMessage_Properties + IoTHubMessage_GetMessageId + IoTHubMessage_SetMessageId + IoTHubMessage_GetCorrelationId + IoTHubMessage_SetCorrelationId + IoTHubMessage_Destroy + IoTHubServiceClient_GetVersionString + IoTHubServiceClientAuth_CreateFromConnectionString + IoTHubServiceClientAuth_Destroy + IoTHubDeviceConfiguration_Create + IoTHubDeviceConfiguration_Destroy + IoTHubDeviceConfiguration_GetConfiguration + IoTHubDeviceConfiguration_GetConfigurations + IoTHubDeviceConfiguration_AddConfiguration + IoTHubDeviceConfiguration_UpdateConfiguration + IoTHubDeviceConfiguration_DeleteConfiguration + IoTHubDeviceMethod_Create + IoTHubDeviceMethod_Destroy + IoTHubDeviceMethod_Invoke + IoTHubDeviceTwin_Create + IoTHubDeviceTwin_Destroy + IoTHubDeviceTwin_GetTwin + IoTHubDeviceTwin_UpdateTwin + IoTHubMessaging_LL_Create + IoTHubMessaging_LL_Destroy + IoTHubMessaging_LL_Open + IoTHubMessaging_LL_Close + IoTHubMessaging_LL_Send + IoTHubMessaging_LL_SetFeedbackMessageCallback + IoTHubMessaging_LL_DoWork + IoTHubMessaging_Create + IoTHubMessaging_Destroy + IoTHubMessaging_Open + IoTHubMessaging_Close + IoTHubMessaging_SendAsync + IoTHubMessaging_SetFeedbackMessageCallback + IoTHubRegistryManager_Create + IoTHubRegistryManager_Destroy + IoTHubRegistryManager_CreateDevice + IoTHubRegistryManager_GetDevice + IoTHubRegistryManager_UpdateDevice + IoTHubRegistryManager_DeleteDevice + IoTHubRegistryManager_GetDeviceList + IoTHubRegistryManager_GetStatistics
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iothub_service_client/src/iothub_service_client_auth.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/string_tokenizer.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/connection_string_parser.h" + +#include "iothub_service_client_auth.h" + +static const char* IOTHUBHOSTNAME = "HostName"; +static const char* IOTHUBSHAREDACESSKEYNAME = "SharedAccessKeyName"; +static const char* IOTHUBSHAREDACESSKEY = "SharedAccessKey"; +static const char* IOTHUBDEVICEID = "DeviceId"; + +static void free_service_client_auth(IOTHUB_SERVICE_CLIENT_AUTH* authInfo) +{ + free(authInfo->hostname); + free(authInfo->iothubName); + free(authInfo->iothubSuffix); + free(authInfo->sharedAccessKey); + free(authInfo->keyName); + free(authInfo->deviceId); + free(authInfo); +} + +DEFINE_ENUM_STRINGS(IOTHUB_DEVICE_STATUS, IOTHUB_DEVICE_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_DEVICE_CONNECTION_STATE, IOTHUB_DEVICE_CONNECTION_STATE_VALUES); + +IOTHUB_SERVICE_CLIENT_AUTH_HANDLE IoTHubServiceClientAuth_CreateFromConnectionString(const char* connectionString) +{ + IOTHUB_SERVICE_CLIENT_AUTH_HANDLE result; + + /*Codes_SRS_IOTHUBSERVICECLIENT_12_001: [** IoTHubServiceClientAuth_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL **]*/ + if (connectionString == NULL) + { + LogError("Input parameter is NULL: connectionString"); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_002: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory for a new service client instance. **] */ + result = malloc(sizeof(IOTHUB_SERVICE_CLIENT_AUTH)); + if (result == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_003: [** If the allocation failed, IoTHubServiceClientAuth_CreateFromConnectionString shall return NULL **] */ + LogError("Malloc failed for IOTHUB_SERVICE_CLIENT_AUTH"); + } + else + { + memset(result, 0, sizeof(*result)); + + /*Codes_SRS_IOTHUBSERVICECLIENT_12_009: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create a STRING_HANDLE from the given connection string by calling STRING_construct. **] */ + STRING_HANDLE connection_string; + if ((connection_string = STRING_construct(connectionString)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_010: [** If the STRING_construct fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("STRING_construct failed"); + free_service_client_auth(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_004: [** IoTHubServiceClientAuth_CreateFromConnectionString shall populate hostName, iotHubName, iotHubSuffix, sharedAccessKeyName, sharedAccessKeyValue from the given connection string by calling connectionstringparser_parse **] */ + MAP_HANDLE connection_string_values_map; + if ((connection_string_values_map = connectionstringparser_parse(connection_string)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_005: [** If populating the IOTHUB_SERVICE_CLIENT_AUTH fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL **] */ + LogError("Tokenizing failed on connectionString"); + free_service_client_auth(result); + result = NULL; + } + else + { + STRING_TOKENIZER_HANDLE tokenizer = NULL; + STRING_HANDLE token_key_string = NULL; + STRING_HANDLE token_value_string = NULL; + STRING_HANDLE host_name_string = NULL; + const char* hostName; + const char* keyName; + const char* deviceId; + const char* sharedAccessKey; + const char* iothubName; + const char* iothubSuffix; + + keyName = Map_GetValueFromKey(connection_string_values_map, IOTHUBSHAREDACESSKEYNAME); + deviceId = Map_GetValueFromKey(connection_string_values_map, IOTHUBDEVICEID); + + /*Codes_SRS_IOTHUBSERVICECLIENT_12_004: [** IoTHubServiceClientAuth_CreateFromConnectionString shall populate hostName, iotHubName, iotHubSuffix, sharedAccessKeyName, sharedAccessKeyValue from the given connection string by calling connectionstringparser_parse **] */ + (void)memset(result, 0, sizeof(IOTHUB_SERVICE_CLIENT_AUTH)); + if ((keyName == NULL) && (deviceId == NULL)) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_012: [** If the populating SharedAccessKeyName fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Couldn't find %s or %s in connection string", IOTHUBSHAREDACESSKEYNAME, IOTHUBDEVICEID); + free_service_client_auth(result); + result = NULL; + } + else if ((keyName != NULL) && (deviceId != NULL)) + { + LogError("Both %s and %s in connection string were set, they are mutually exclusive", IOTHUBSHAREDACESSKEYNAME, IOTHUBDEVICEID); + free_service_client_auth(result); + result = NULL; + } + else if ((hostName = Map_GetValueFromKey(connection_string_values_map, IOTHUBHOSTNAME)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_011: [** If the populating HostName fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Couldn't find %s in connection string", IOTHUBHOSTNAME); + free_service_client_auth(result); + result = NULL; + } + else if ((sharedAccessKey = Map_GetValueFromKey(connection_string_values_map, IOTHUBSHAREDACESSKEY)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_013: [** If the populating SharedAccessKey fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Couldn't find %s in connection string", IOTHUBSHAREDACESSKEY); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_038: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create a STRING_handle from hostName by calling STRING_construct. **] */ + else if ((host_name_string = STRING_construct(hostName)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_039: [** If the STRING_construct fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("STRING_construct failed"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_014: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create a STRING_TOKENIZER to parse HostName by calling STRING_TOKENIZER_create. **] */ + else if ((tokenizer = STRING_TOKENIZER_create(host_name_string)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_015: [** If the STRING_TOKENIZER_create fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Error creating STRING tokenizer"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_016: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create a new STRING_HANDLE for token key string by calling STRING_new. **] */ + else if ((token_key_string = STRING_new()) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_017: [** If the STRING_new fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Error creating key token STRING_HANDLE"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_018: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create a new STRING_HANDLE for token value string by calling STRING_new. **] */ + else if ((token_value_string = STRING_new()) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_019: [** If the STRING_new fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Error creating value token STRING_HANDLE"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_020: [** IoTHubServiceClientAuth_CreateFromConnectionString shall call STRING_TOKENIZER_get_next_token to get token key string. **] */ + else if (STRING_TOKENIZER_get_next_token(tokenizer, token_key_string, ".") != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_021: [** If the STRING_TOKENIZER_get_next_token fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Error reading key token STRING"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_022: [** IoTHubServiceClientAuth_CreateFromConnectionString shall call STRING_TOKENIZER_get_next_token to get token value string. **] */ + else if (STRING_TOKENIZER_get_next_token(tokenizer, token_value_string, "0") != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_023: [** If the STRING_TOKENIZER_get_next_token fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("Error reading value token STRING"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_024: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy hostName to result->hostName by calling mallocAndStrcpy_s. **] */ + else if (mallocAndStrcpy_s(&result->hostname, hostName) != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_025: [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for hostName"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_026: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. **] */ + else if ((keyName != NULL) && (mallocAndStrcpy_s(&result->keyName, keyName) != 0)) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_027: [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for keyName"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_026: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy keyName to result->keyName by calling mallocAndStrcpy_s. **] */ + else if ((deviceId != NULL) && (mallocAndStrcpy_s(&result->deviceId, deviceId) != 0)) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_027: [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for keyName"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_028: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy sharedAccessKey to result->sharedAccessKey by calling mallocAndStrcpy_s. **] */ + else if (mallocAndStrcpy_s(&result->sharedAccessKey, sharedAccessKey) != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_029: [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_034: [** IoTHubServiceClientAuth_CreateFromConnectionString shall create C string from token key string handle by calling STRING_c_str. **] */ + else if ((iothubName = STRING_c_str(token_key_string)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_035 : [** If the STRING_c_str fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("STRING_c_str failed for iothubName"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_036 : [** IoTHubServiceClientAuth_CreateFromConnectionString shall create C string from token value string handle by calling STRING_c_str. **] */ + else if ((iothubSuffix = STRING_c_str(token_value_string)) == NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_037 : [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("STRING_c_str failed for iothubSuffix"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_030: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy iothubName to result->iothubName by calling mallocAndStrcpy_s. **] */ + else if (mallocAndStrcpy_s(&result->iothubName, iothubName) != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_031 : [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_032: [** IoTHubServiceClientAuth_CreateFromConnectionString shall allocate memory and copy iothubSuffix to result->iothubSuffix by calling mallocAndStrcpy_s. **] */ + else if (mallocAndStrcpy_s(&result->iothubSuffix, iothubSuffix) != 0) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_033 : [** If the mallocAndStrcpy_s fails, IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return NULL. **] */ + LogError("mallocAndStrcpy_s failed for sharedAccessKey"); + free_service_client_auth(result); + result = NULL; + } + /*Codes_SRS_IOTHUBSERVICECLIENT_12_006: [** If the IOTHUB_SERVICE_CLIENT_AUTH has been populated IoTHubServiceClientAuth_CreateFromConnectionString shall do clean up and return with a IOTHUB_SERVICE_CLIENT_AUTH_HANDLE to it **] */ + STRING_delete(token_key_string); + STRING_delete(token_value_string); + STRING_delete(host_name_string); + STRING_TOKENIZER_destroy(tokenizer); + Map_Destroy(connection_string_values_map); + } + STRING_delete(connection_string); + } + } + } + return result; +} + +void IoTHubServiceClientAuth_Destroy(IOTHUB_SERVICE_CLIENT_AUTH_HANDLE serviceClientHandle) +{ + /*Codes_SRS_IOTHUBSERVICECLIENT_12_007: [** If the serviceClientHandle input parameter is NULL IoTHubServiceClient_Destroy shall return **]*/ + if (serviceClientHandle != NULL) + { + /*Codes_SRS_IOTHUBSERVICECLIENT_12_008: [** If the serviceClientHandle input parameter is not NULL IoTHubServiceClient_Destroy shall free the memory of it and return **]*/ + free_service_client_auth((IOTHUB_SERVICE_CLIENT_AUTH*)serviceClientHandle); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/agenttypesystem.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,718 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef AGENT_DATA_TYPES_H +#define AGENT_DATA_TYPES_H + +#ifdef __cplusplus +#include <cstdint> +#include <ctime> +#include <cstddef> +#else +#if ((defined _WIN32_WCE) && _WIN32_WCE==0x0600) +#include "stdint_ce6.h" +#else +#include <stdint.h> +#endif +#include <stddef.h> +#endif + +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/strings.h" + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_001:[ AGENT_TYPE_SYSTEM shall have the following interface]*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*the following forward declarations the closest implementation to "interface" in OOP*/ +struct AGENT_DATA_TYPE_TAG; +typedef struct AGENT_DATA_TYPE_TAG AGENT_DATA_TYPE; + +/*this file contains the definitions of the data types of EDM*/ +/*the types are taken from */ +/*http://docs.oasis-open.org/odata/odata/v4.0/cos01/part3-csdl/odata-v4.0-cos01-part3-csdl.html*/ +/*chapter 4.4 - "Primitive Data Types" */ + +/*the C implementation of these types follows:*/ +/*even the simpler types are encapsulated in structs to purposely avoid compiler promotions/casts etc*/ + +/*Binary data.*/ +typedef struct EDM_BINARY_TAG +{ + size_t size; + unsigned char* data; +}EDM_BINARY; + +#define EDM_BOOLEANS_VALUES \ + EDM_TRUE, \ + EDM_FALSE + +DEFINE_ENUM(EDM_BOOLEANS, EDM_BOOLEANS_VALUES); + +/*ispositiveinfinity*/ + +#ifdef _MSC_VER +#define ISPOSITIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_PINF) == _FPCLASS_PINF)) +#else +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define ISPOSITIVEINFINITY(x) (isinf((x)) && (signbit((x))==0)) +#else +#error update this file to contain the latest C standard. +#endif +#else +#ifdef __cplusplus +#define ISPOSITIVEINFINITY(x) (std::isinf((x)) && (signbit((x))==0)) +#else +#error unknown (or C89) compiler, must provide a definition for ISPOSITIVEINFINITY +#endif +#endif +#endif + +#ifdef _MSC_VER +/*not exactly signbit*/ +#define ISNEGATIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_NINF) == _FPCLASS_NINF)) +#else +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define ISNEGATIVEINFINITY(x) (isinf((x)) && (signbit((x))!=0)) +#else +#error update this file to contain the latest C standard. +#endif +#else +#ifdef __cplusplus +#define ISNEGATIVEINFINITY(x) (std::isinf((x)) && (signbit((x)) != 0)) +#else +#error unknown (or C89) compiler, must provide a definition for ISNEGATIVEINFINITY +#endif +#endif +#endif + +/*Binary-valued logic.*/ +typedef struct EDM_BOOLEAN_TAG +{ + EDM_BOOLEANS value; +}EDM_BOOLEAN; + +/*Unsigned 8-bit integer*/ +typedef struct EDM_BYTE_TAG +{ + uint8_t value; +} EDM_BYTE; + +/*Date without a time-zone offset*/ +/*The edm:Date expression evaluates to a primitive date value. A date expression MUST be assigned a +value of type xs:date, see [XML-Schema-2], section 3.3.9. The value MUST also conform to rule +dateValue in [OData-ABNF], i.e. it MUST NOT contain a time-zone offset.*/ +/*section 3.3.9: date uses the date/timeSevenPropertyModel, with hour, minute, and second required to be absent.*/ +/*dateValue in OData-ABNF is : dateValue = year "-" month "-" day */ +/*year = [ "-" ] ( "0" 3DIGIT / oneToNine 3*DIGIT ) +month = "0" oneToNine +/ "1" ( "0" / "1" / "2" ) +day = "0" oneToNine +/ ( "1" / "2" ) DIGIT +/ "3" ( "0" / "1" )*/ +typedef struct EDM_DATE_TAG +{ + int16_t year; /*can represent all values for a year*/ /*they can be between -9999 and 9999*/ + uint8_t month; + uint8_t day; +} EDM_DATE; + + +/*The edm:DateTimeOffset expression evaluates to a primitive date/time value with a time-zone offset. +A date/time expression MUST be assigned a value of type xs:dateTimeStamp, see [XML-Schema-2], +section 3.4.28. The value MUST also conform to rule dateTimeOffsetValue in [OData-ABNF], i.e. it +MUST NOT contain an end-of-day fragment (24:00:00).*/ +/*section 3.4.28 says : dateTimeStampLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?*/ +/*[OData-ABNF] says: dateTimeOffsetValue = year "-" month "-" day "T" hour ":" minute [ ":" second [ "." fractionalSeconds ] ] ( "Z" / sign hour ":" minute )*/ +/*fractionalSeconds = 1*12DIGIT, FYI*/ +typedef struct EDM_DATE_TIME_OFFSET_TAG +{ + struct tm dateTime; + uint8_t hasFractionalSecond; + uint64_t fractionalSecond; /*because UINT32 only has 10 digits*/ + uint8_t hasTimeZone; + int8_t timeZoneHour; + uint8_t timeZoneMinute; +}EDM_DATE_TIME_OFFSET; + +/*Edm.Guid*/ +/*16-byte (128-bit) unique identifier*/ +/*The edm:Guid expression evaluates to a primitive 32-character string value. A guid expression MUST be +assigned a value conforming to the rule guidValue in [OData-ABNF].*/ +/*guidValue is 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG*/ +typedef struct EDM_GUID_TAG +{ + uint8_t GUID[16]; +}EDM_GUID; + + +/*Edm.Decimal*/ +/*The edm:Decimal expression evaluates to a primitive decimal value. A decimal expression MUST be +assigned a value conforming to the rule decimalValue in [OData-ABNF].*/ +/*[OData-ABNF] says: decimalValue = [SIGN] 1*DIGIT ["." 1*DIGIT] */ +/* this is binary coded decimal style then*/ +typedef struct EDM_DECIMAL_TAG +{ + STRING_HANDLE value; +} EDM_DECIMAL; + +/*Edm.Double*/ +/*IEEE 754 binary64 floating-point number (15-17 decimal digits)*/ + +typedef struct EDM_DOUBLE_TAG +{ + double value; +} EDM_DOUBLE; + + +/*Edm.Duration*/ +/*Signed duration in days, hours, minutes, and (sub)seconds*/ +/*The edm:Duration expression evaluates to a primitive duration value. A duration expression MUST be +assigned a value of type xs:dayTimeDuration, see [XML-Schema-2], section 3.4.27.*/ +/*XML-Schema section 3.4.27 says: "[...]leaving only those with day, hour, minutes, and/or seconds fields." */ +/*day is "unsignedNoDecimalPtNumeral" and that can have as many digits... ?*/ +typedef struct EDM_DURATION_TAG +{ + size_t nDigits; + char* digits; +} +EDM_DURATION; + + +/*Edm.Int16*/ +/*Signed 16-bit integer*/ +/*[OData=ABNF] says about Int16: numbers in the range from -32768 to 32767 */ +/*this is not C compliant, because C89 has for (read:guarantees) short SHRT_MIN -32767... (notice how it misses -32768)*/ +/*C99 has the same*/ +/*C11 has the same*/ +/*platform types has to check for -32768 compliance*/ +typedef struct EDM_INT16_TAG +{ + int16_t value; +} EDM_INT16; + +/*Edm.Int32*/ +/*Signed 32-bit integer*/ +/*OData-ABNF has for int32Value = [ sign ] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647*/ +/*same issue as for EDM_16*/ +/*platform types has to check compliance based on LONG_MIN #define*/ +typedef struct EDM_INT32_TAG +{ + int32_t value; +} EDM_INT32; + +/*Edm.Int64*/ +/*Signed 64-bit integer*/ +/*OData=ABNF: int64Value = [ sign ] 1*19DIGIT ; numbers in the range from -9223372036854775808 to 9223372036854775807*/ +/*C89 has no mention of anything on 64bits*/ +/*C99 mention LLONG_MIN as -9223372036854775807 and LLONG_MAX as 9223372036854775807*/ +/*C11 is the same as C99*/ +typedef struct EDM_INT64_TAG +{ + int64_t value; /*SINT64 might be a single type or s truct provided by platformTypes, depending on C compiler support*/ +} EDM_INT64; + +/*Edm.SByte*/ +/*Signed 8-bit integer*/ +/*OData=ABNF: sbyteValue = [ sign ] 1*3DIGIT ; numbers in the range from -128 to 127*/ +/*C89, C99, C11 all have SCHAR_MIN, SCHAR_MAX between -127 and 127 (guaranteed)*/ +/*so platformTypes.h has to check that -128 is attainable*/ +typedef struct EDM_SBYTE_TAG +{ + int8_t value; +} EDM_SBYTE; + +/*Edm.Single*/ +/*IEEE 754 binary32 floating-point number (6-9 decimal digits)*/ +/*with the same "fears" as for Edm.Double*/ +typedef struct EDM_SINGLE_TAG +{ + float value; +} EDM_SINGLE; + +/*not clear what this is +typedef EDM_STREAM_TAG +{ + +}EDM_STREAM; +*/ + +/*Edm.String*/ +/*Sequence of UTF-8 characters*/ +typedef struct EDM_STRING_TAG +{ + size_t length; /*number of unsigned char* in the string*/ + char* chars; +} EDM_STRING; + +/*Edm.TimeOfDay*/ +/*Clock time 00:00-23:59:59.999999999999*/ +/*The edm:TimeOfDay expression evaluates to a primitive time value. A time-of-day expression MUST be +assigned a value conforming to the rule timeOfDayValue in [OData-ABNF].*/ +/*timeOfDayValue = hour ":" minute [ ":" second [ "." fractionalSeconds ] ]*/ +typedef struct EDM_TIME_OF_DAY_TAG +{ + uint8_t hour; + uint8_t minute; + uint8_t second; + uint64_t fractionalSecond; +}EDM_TIME_OF_DAY; + +/*positionLiteral = doubleValue SP doubleValue ; longitude, then latitude*/ +typedef struct EDM_POSITION_LITERAL_TAG +{ + double longitude; + double latitude; +}EDM_POSITION_LITERAL; + + +/*sridLiteral = "SRID" EQ 1*5DIGIT SEMI*/ +typedef struct EDM_SRID_LITERAL_TAG +{ + uint16_t digits; +} EDM_SRID_LITERAL; + +/*lineStringData = OPEN positionLiteral 1*( COMMA positionLiteral ) CLOSE*/ +typedef struct EDM_LINE_STRING_DATA_TAG +{ + size_t nPositionLiterals; + EDM_POSITION_LITERAL *positionLiterals; +}EDM_LINE_STRING_DATA; + +/*pointData = OPEN positionLiteral CLOSE*/ +typedef struct EDM_POINT_DATA_TAG +{ + EDM_POSITION_LITERAL positionLiteral; +}EDM_POINT_DATA; + +/*ringLiteral = OPEN positionLiteral *( COMMA positionLiteral ) CLOSE*/ +typedef struct EDM_RING_LITERAL_TAG +{ + size_t nPositionLiterals; + EDM_POSITION_LITERAL *positionLiterals; +}EDM_RING_LITERAL; + +/*pointLiteral ="Point" pointData*/ +typedef struct EDM_POINT_LITERAL_TAG +{ + EDM_POINT_DATA pointData; +} EDM_POINT_LITERAL; + +/*polygonData = OPEN ringLiteral *( COMMA ringLiteral ) CLOSE*/ +typedef struct EDM_POLYGON_DATA_TAG +{ + size_t nRingLiterals; + EDM_RING_LITERAL *ringLiterals; +}EDM_POLYGON_DATA; + +/*polygonLiteral = "Polygon" polygonData*/ +typedef struct EDM_POLYGON_LITERAL_TAG +{ + EDM_POLYGON_DATA polygonData; +}EDM_POLYGON_LITERAL; + +/*fullPolygonLiteral = sridLiteral polygonLiteral*/ +typedef struct EDM_FULL_POLYGON_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_POLYGON_LITERAL polygonLiteral; +}EDM_FULL_POLYGON_LITERAL; + +/*fullPointLiteral = sridLiteral pointLiteral*/ +typedef struct EDM_FULL_POINT_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_POINT_LITERAL pointLiteral; +} EDM_FULL_POINT_LITERAL; + +/*geographyPoint = geographyPrefix SQUOTE fullPointLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_POINT_TAG +{ + EDM_FULL_POINT_LITERAL fullPointLiteral; +}EDM_GEOGRAPHY_POINT; + +/*multiPolygonLiteral = "MultiPolygon(" [ polygonData *( COMMA polygonData ) ] CLOSE*/ +typedef struct EDM_MULTI_POLYGON_LITERAL_TAG +{ + size_t nPolygonDatas; + EDM_POLYGON_DATA * polygonDatas; +}EDM_MULTI_POLYGON_LITERAL; + +/*fullMultiPolygonLiteral = sridLiteral multiPolygonLiteral*/ +typedef struct EDM_FULL_MULTI_POLYGON_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_MULTI_POLYGON_LITERAL multiPolygonLiteral; +}EDM_FULL_MULTI_POLYGON_LITERAL; + +/*multiPointLiteral = "MultiPoint(" [ pointData *( COMMA pointData ) ] CLOSE*/ +typedef struct EDM_MULTI_POINT_LITERAL_TAG +{ + size_t nPointDatas; + EDM_POINT_DATA *pointDatas; +}EDM_MULTI_POINT_LITERAL; + +/*fullMultiPointLiteral = sridLiteral multiPointLiteral*/ +typedef struct EDM_FULL_MULTI_POINT_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_MULTI_POINT_LITERAL multiPointLiteral; +}EDM_FULL_MULTI_POINT_LITERAL; + +/*lineStringLiteral = "LineString" lineStringData*/ +typedef struct EDM_LINE_STRING_LITERAL_TAG +{ + EDM_LINE_STRING_DATA lineStringData; +}EDM_LINE_STRING_LITERAL; + +/*fullLineStringLiteral = sridLiteral lineStringLiteral*/ +typedef struct EDM_FULL_LINE_STRING_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_LINE_STRING_LITERAL lineStringLiteral; +} EDM_FULL_LINE_STRING_LITERAL; + +/*multiLineStringLiteral = "MultiLineString(" [ lineStringData *( COMMA lineStringData ) ] CLOSE*/ +typedef struct EDM_MULTI_LINE_STRING_LITERAL_TAG +{ + size_t nLineStringDatas; + EDM_LINE_STRING_DATA lineStringData; +}EDM_MULTI_LINE_STRING_LITERAL; + +/*fullMultiLineStringLiteral = sridLiteral multiLineStringLiteral*/ +typedef struct EDM_FULL_MULTI_LINE_STRING_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_MULTI_LINE_STRING_LITERAL multiLineStringLiteral; +}EDM_FULL_MULTI_LINE_STRING_LITERAL; + + + +/*forward defines*/ +struct EDM_GEO_LITERAL_TAG; +typedef struct EDM_GEO_LITERAL_TAG EDM_GEO_LITERAL; + +/*collectionLiteral = "Collection(" geoLiteral *( COMMA geoLiteral ) CLOSE*/ +typedef struct EDM_COLLECTION_LITERAL_TAG +{ + size_t nGeoLiterals; + EDM_GEO_LITERAL* geoLiterals; +} EDM_COLLECTION_LITERAL; + +/*geoLiteral = collectionLiteral +/ lineStringLiteral +/ multiPointLiteral +/ multiLineStringLiteral +/ multiPolygonLiteral +/ pointLiteral +/ polygonLiteral +*/ +typedef enum EDM_GEO_LITERAL_TYPE_TAG +{ + EDM_COLLECTION_LITERAL_TYPE, + EDM_LINE_STRING_LITERAL_TYPE, + EDM_MULTI_POINT_LITERAL_TYPE, + EDM_MULTI_LINE_STRING_LITERAL_TYPE, + EDM_MULTI_POLIGON_LITERAL_TYPE, + EDM_POINT_LITERAL_TYPE, + EDM_POLYGON_LITERAL_TYPE +}EDM_GEO_LITERAL_TYPE; + +struct EDM_GEO_LITERAL_TAG +{ + EDM_GEO_LITERAL_TYPE literalType; + union + { + EDM_COLLECTION_LITERAL collectionLiteral; + EDM_LINE_STRING_LITERAL lineStringLiteral; + EDM_MULTI_POINT_LITERAL multiPointLiteral; + EDM_MULTI_LINE_STRING_LITERAL multiLineStringLiteral; + EDM_MULTI_POLYGON_LITERAL multiPolygonLiteral; + EDM_POINT_LITERAL pointLiteral; + EDM_POLYGON_LITERAL polygonLiteral; + } u; +}; + +/*fullCollectionLiteral = sridLiteral collectionLiteral*/ +typedef struct EDM_FULL_COLLECTION_LITERAL_TAG +{ + EDM_SRID_LITERAL srid; + EDM_COLLECTION_LITERAL collectionLiteral; +}EDM_FULL_COLLECTION_LITERAL; + +/*now geography stuff*/ + +/*geographyCollection = geographyPrefix SQUOTE fullCollectionLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_COLLECTION_TAG +{ + EDM_FULL_COLLECTION_LITERAL fullCollectionLiteral; +}EDM_GEOGRAPHY_COLLECTION; + +/*geographyLineString = geographyPrefix SQUOTE fullLineStringLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_LINE_STRING_TAG +{ + EDM_FULL_LINE_STRING_LITERAL fullLineStringLiteral; +}EDM_GEOGRAPHY_LINE_STRING; + +/*geographyMultiLineString = geographyPrefix SQUOTE fullMultiLineStringLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_MULTI_LINE_STRING_TAG +{ + EDM_FULL_MULTI_LINE_STRING_LITERAL fullMultiLineStringLiteral; +}EDM_GEOGRAPHY_MULTI_LINE_STRING; + +/*geographyMultiPoint = geographyPrefix SQUOTE fullMultiPointLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_MULTI_POINT_TAG +{ + EDM_FULL_MULTI_POINT_LITERAL fullMultiPointLiteral; +}EDM_GEOGRAPHY_MULTI_POINT; + +/*geographyMultiPolygon = geographyPrefix SQUOTE fullMultiPolygonLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_MULTI_POLYGON_TAG +{ + EDM_FULL_MULTI_POLYGON_LITERAL fullMultiPolygonLiteral; +}EDM_GEOGRAPHY_MULTI_POLYGON; + +/*geographyPolygon = geographyPrefix SQUOTE fullPolygonLiteral SQUOTE*/ +typedef struct EDM_GEOGRAPHY_POLYGON_TAG +{ + EDM_FULL_POLYGON_LITERAL fullPolygonLiteral; +}EDM_GEOGRAPHY_POLYGON; + +/*geometryCollection = geometryPrefix SQUOTE fullCollectionLiteral SQUOTE*/ +/*forward defines*/ + + +/*geometryPolygon = geometryPrefix SQUOTE fullPolygonLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_POLYGON_TAG +{ + EDM_FULL_POLYGON_LITERAL fullPolygonLiteral; +}EDM_GEOMETRY_POLYGON; + +/*geometryPoint = geometryPrefix SQUOTE fullPointLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_POINT_TAG +{ + EDM_FULL_POINT_LITERAL fullPointLiteral; +}EDM_GEOMETRY_POINT; + +/*geometryMultiPolygon = geometryPrefix SQUOTE fullMultiPolygonLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_MULTI_POLYGON_TAG +{ + EDM_FULL_MULTI_POLYGON_LITERAL fullMultiPolygonLiteral; +} +EDM_GEOMETRY_MULTI_POLYGON; + +/*geometryMultiPoint = geometryPrefix SQUOTE fullMultiPointLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_MULTI_POINT_TAG +{ + EDM_FULL_MULTI_POINT_LITERAL fullMultiPointLiteral; +}EDM_GEOMETRY_MULTI_POINT; + +/*geometryMultiLineString = geometryPrefix SQUOTE fullMultiLineStringLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_MULTI_LINE_STRING_TAG +{ + EDM_FULL_MULTI_LINE_STRING_LITERAL fullMultiLineStringLiteral; +}EDM_GEOMETRY_MULTI_LINE_STRING; + +/*geometryLineString = geometryPrefix SQUOTE fullLineStringLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_LINE_STRING_TAG +{ + EDM_FULL_LINE_STRING_LITERAL fullLineStringLiteral; +}EDM_GEOMETRY_LINE_STRING; + +/*geometryCollection = geometryPrefix SQUOTE fullCollectionLiteral SQUOTE*/ +typedef struct EDM_GEOMETRY_COLLECTION_TAG +{ + EDM_FULL_COLLECTION_LITERAL fullCollectionLiteral; +}EDM_GEOMETRY_COLLECTION; + +/*holds the name and the value of a COMPLEX_TYPE... field*/ +typedef struct COMPLEX_TYPE_FIELD_TAG +{ + const char* fieldName; + AGENT_DATA_TYPE* value; +}COMPLEX_TYPE_FIELD_TYPE; + +/*EDM_COMPLEX_TYPE - this type doesn't exist in EDMX as a primitive type*/ +typedef struct EDM_COMPLEX_TYPE_TAG +{ + size_t nMembers; + COMPLEX_TYPE_FIELD_TYPE *fields; +}EDM_COMPLEX_TYPE; + +#define AGENT_DATA_TYPES_RESULT_VALUES \ + AGENT_DATA_TYPES_OK, \ + AGENT_DATA_TYPES_ERROR, \ + AGENT_DATA_TYPES_INVALID_ARG, \ + AGENT_DATA_TYPES_NOT_IMPLEMENTED, \ + AGENT_DATA_TYPES_JSON_ENCODER_ERRROR + +DEFINE_ENUM(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_RESULT_VALUES); + +#define AGENT_DATA_TYPE_TYPE_VALUES\ + EDM_NO_TYPE, \ + EDM_BINARY_TYPE, \ + EDM_BOOLEAN_TYPE, \ + EDM_BYTE_TYPE, \ + EDM_DATE_TYPE, \ + EDM_DATE_TIME_OFFSET_TYPE, \ + EDM_DECIMAL_TYPE, \ + EDM_DOUBLE_TYPE, \ + EDM_DURATION_TYPE, \ + EDM_GUID_TYPE, \ + EDM_INT16_TYPE, \ + EDM_INT32_TYPE, \ + EDM_INT64_TYPE, \ + EDM_SBYTE_TYPE, \ + EDM_SINGLE_TYPE, \ + EDM_STREAM, /*not supported, because what is stream?*/ \ + EDM_STRING_TYPE, \ + EDM_TIME_OF_DAY_TYPE, \ + EDM_GEOGRAPHY_TYPE, /*not supported because what is "abstract base type"*/ \ + EDM_GEOGRAPHY_POINT_TYPE, \ + EDM_GEOGRAPHY_LINE_STRING_TYPE, \ + EDM_GEOGRAPHY_POLYGON_TYPE, \ + EDM_GEOGRAPHY_MULTI_POINT_TYPE, \ + EDM_GEOGRAPHY_MULTI_LINE_STRING_TYPE, \ + EDM_GEOGRAPHY_MULTI_POLYGON_TYPE, \ + EDM_GEOGRAPHY_COLLECTION_TYPE, \ + EDM_GEOMETRY_TYPE, /*not supported because what is "abstract base type"*/ \ + EDM_GEOMETRY_POINT_TYPE, \ + EDM_GEOMETRY_LINE_STRING_TYPE, \ + EDM_GEOMETRY_POLYGON_TYPE, \ + EDM_GEOMETRY_MULTI_POINT_TYPE, \ + EDM_GEOMETRY_MULTI_LINE_STRING_TYPE, \ + EDM_GEOMETRY_MULTI_POLYGON_TYPE, \ + EDM_GEOMETRY_COLLECTION_TYPE, \ + EDM_COMPLEX_TYPE_TYPE, \ + EDM_NULL_TYPE, \ + EDM_ENTITY_TYPE_TYPE, \ + EDM_STRING_NO_QUOTES_TYPE \ + + +DEFINE_ENUM(AGENT_DATA_TYPE_TYPE, AGENT_DATA_TYPE_TYPE_VALUES); + +struct AGENT_DATA_TYPE_TAG +{ + AGENT_DATA_TYPE_TYPE type; + union + { + EDM_BINARY edmBinary; + EDM_BOOLEAN edmBoolean; + EDM_BYTE edmByte; + EDM_DATE edmDate; + EDM_DATE_TIME_OFFSET edmDateTimeOffset; + EDM_DECIMAL edmDecimal; + EDM_DOUBLE edmDouble; + EDM_DURATION edmDuration; + EDM_GUID edmGuid; + EDM_INT16 edmInt16; + EDM_INT32 edmInt32; + EDM_INT64 edmInt64; + EDM_SBYTE edmSbyte; + EDM_SINGLE edmSingle; + /*EDM_STREAM, not supported, because what is stream?*/ + EDM_STRING edmString; + EDM_STRING edmStringNoQuotes; + EDM_TIME_OF_DAY edmTimeOfDay; + /*EDM_GEOGRAPHY_, not supported because what is "abstract base type"*/ + EDM_GEOGRAPHY_POINT edmGeographyPoint; + EDM_GEOGRAPHY_LINE_STRING edmGeographyLineString; + EDM_GEOGRAPHY_POLYGON edmGeographyPolygon; + EDM_GEOGRAPHY_MULTI_POINT edmGeographyMultiPoint; + EDM_GEOGRAPHY_MULTI_LINE_STRING edmGeographyMultiLineString; + EDM_GEOGRAPHY_MULTI_POLYGON edmGeographyMultiPolygon; + EDM_GEOGRAPHY_COLLECTION edmGeographyCollection; + /* EDM_GEOMETRY_, not supported because what is "abstract base type"*/ + EDM_GEOMETRY_POINT edmGeometryPoint; + EDM_GEOMETRY_LINE_STRING edmGeometryLineString; + EDM_GEOMETRY_POLYGON edmGeometryPolygon; + EDM_GEOMETRY_MULTI_POINT edmGeometryMultiPoint; + EDM_GEOMETRY_MULTI_LINE_STRING edmGeoemtryMultiLineString; + EDM_GEOMETRY_MULTI_POLYGON edmGeometryMultiPolygon; + EDM_GEOMETRY_COLLECTION edmGeometryCollection; + EDM_COMPLEX_TYPE edmComplexType; + } value; +}; + +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, AgentDataTypes_ToString, STRING_HANDLE, destination, const AGENT_DATA_TYPE*, value); + +/*Create/Destroy work in pairs. For some data type not calling Uncreate might be ok. For some, it will lead to memory leaks*/ + +/*creates an AGENT_DATA_TYPE containing a EDM_BOOLEAN from a int*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_EDM_BOOLEAN_from_int, AGENT_DATA_TYPE*, agentData, int, v); + +/*creates an AGENT_DATA_TYPE containing a UINT8*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_UINT8, AGENT_DATA_TYPE*, agentData, uint8_t, v); + +/*creates an AGENT_DATA_TYPE containing a EDM_DATE */ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_date, AGENT_DATA_TYPE*, agentData, int16_t, year, uint8_t, month, uint8_t, day); + +/*create an AGENT_DATA_TYPE containing an EDM_DECIMAL from a string representation*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_EDM_DECIMAL_from_charz, AGENT_DATA_TYPE*, agentData, const char*, v); + +/*create an AGENT_DATA_TYPE containing an EDM_DOUBLE from a double*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_DOUBLE, AGENT_DATA_TYPE*, agentData, double, v); + +/*create an AGENT_DATA_TYPE from INT16_T*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_SINT16, AGENT_DATA_TYPE*, agentData, int16_t, v); + +/*create an AGENT_DATA_TYPE from INT32_T*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_SINT32, AGENT_DATA_TYPE*, agentData, int32_t, v); + +/*create an AGENT_DATA_TYPE from INT64_T*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_SINT64, AGENT_DATA_TYPE*, agentData, int64_t, v); + +/*create an AGENT_DATA_TYPE from int8_t*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_SINT8, AGENT_DATA_TYPE*, agentData, int8_t, v); + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_091:[Creates an AGENT_DATA_TYPE containing an Edm.DateTimeOffset from an EDM_DATE_TIME_OFFSET.]*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET, AGENT_DATA_TYPE*, agentData, EDM_DATE_TIME_OFFSET, v); + +/*creates an AGENT_DATA_TYPE containing a EDM_GUID*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_EDM_GUID, AGENT_DATA_TYPE*, agentData, EDM_GUID, v); + +/*creates an AGENT_DATA_TYPE containing a EDM_BINARY*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_EDM_BINARY, AGENT_DATA_TYPE*, agentData, EDM_BINARY, v); + +/*create an AGENT_DATA_TYPE from SINGLE*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_FLOAT, AGENT_DATA_TYPE*, agentData, float, v); + +/*create an AGENT_DATA_TYPE from ANSI zero terminated string*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_charz, AGENT_DATA_TYPE*, agentData, const char*, v); + +/*create an AGENT_DATA_TYPE from ANSI zero terminated string (no quotes)*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_charz_no_quotes, AGENT_DATA_TYPE*, agentData, const char*, v); + +/*create an AGENT_DATA_TYPE of type EDM_NULL_TYPE */ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_NULL_AGENT_DATA_TYPE, AGENT_DATA_TYPE*, agentData); + +/*create an AGENT_DATA_TYPE that holds a structs from its fields*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_Members, AGENT_DATA_TYPE*,agentData, const char*, typeName, size_t, nMembers, const char* const *, memberNames, const AGENT_DATA_TYPE*, memberValues); + +/*create a complex AGENT_DATA_TYPE from pointers to AGENT_DATA_TYPE fields*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_MemberPointers, AGENT_DATA_TYPE*, agentData, const char*, typeName, size_t, nMembers, const char* const *, memberNames, const AGENT_DATA_TYPE** ,memberPointerValues); + +/*creates a copy of the AGENT_DATA_TYPE*/ +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE, AGENT_DATA_TYPE*, dest, const AGENT_DATA_TYPE*, src); + +MOCKABLE_FUNCTION(, void, Destroy_AGENT_DATA_TYPE, AGENT_DATA_TYPE*, agentData); + +MOCKABLE_FUNCTION(, AGENT_DATA_TYPES_RESULT, CreateAgentDataType_From_String, const char*, source, AGENT_DATA_TYPE_TYPE, type, AGENT_DATA_TYPE*, agentData); + +MOCKABLE_FUNCTION(, COMPLEX_TYPE_FIELD_TYPE*, AgentDataType_GetComplexTypeField, AGENT_DATA_TYPE*, agentData, size_t, index); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/codefirst.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CODEFIRST_H +#define CODEFIRST_H + +#include "methodreturn.h" +#include "agenttypesystem.h" +#include "schema.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/strings.h" +#include "iotdevice.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdarg> +extern "C" { +#else +#include <stddef.h> +#include <stdarg.h> +#include <stdbool.h> +#endif + +typedef char* ascii_char_ptr; +typedef char* ascii_char_ptr_no_quotes; + +typedef enum REFLECTION_TYPE_TAG +{ + REFLECTION_METHOD_TYPE, + REFLECTION_DESIRED_PROPERTY_TYPE, + REFLECTION_REPORTED_PROPERTY_TYPE, + REFLECTION_STRUCT_TYPE, + REFLECTION_FIELD_TYPE, + REFLECTION_PROPERTY_TYPE, + REFLECTION_ACTION_TYPE, + REFLECTION_MODEL_TYPE, + REFLECTION_NOTHING +}REFLECTION_TYPE; + +typedef EXECUTE_COMMAND_RESULT (*actionWrapper)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); + +typedef METHODRETURN_HANDLE (*methodWrapper)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); + +typedef struct REFLECTION_STRUCT_TAG +{ + const char* name; +}REFLECTION_STRUCT; + +typedef struct WRAPPER_ARGUMENT_TAG +{ + const char* type; + const char* name; +}WRAPPER_ARGUMENT; + +typedef struct REFLECTION_ACTION_TAG +{ + const char* name; + size_t nArguments; + const WRAPPER_ARGUMENT* arguments; + actionWrapper wrapper; + const char* modelName; +}REFLECTION_ACTION; + +typedef struct REFLECTION_METHOD_TAG +{ + const char* name; + size_t nArguments; + const WRAPPER_ARGUMENT* arguments; + methodWrapper wrapper; + const char* modelName; +}REFLECTION_METHOD; + +typedef struct REFLECTION_FIELD_TAG +{ + const char* fieldName; + const char* fieldType; + const char* structName; +}REFLECTION_FIELD; + +typedef struct REFLECTION_PROPERTY_TAG +{ + const char* name; + const char* type; + int(*Create_AGENT_DATA_TYPE_from_Ptr)(void* param, AGENT_DATA_TYPE* dest); + size_t offset; + size_t size; + const char* modelName; +} REFLECTION_PROPERTY; + + +typedef struct REFLECTION_REPORTED_PROPERTY_TAG +{ + const char* name; + const char* type; + int(*Create_AGENT_DATA_TYPE_from_Ptr)(void* param, AGENT_DATA_TYPE* dest); + size_t offset; + size_t size; + const char* modelName; +} REFLECTION_REPORTED_PROPERTY; + +typedef struct REFLECTION_DESIRED_PROPERTY_TAG +{ + pfOnDesiredProperty onDesiredProperty; + void(*desiredPropertInitialize)(void* destination); + void(*desiredPropertDeinitialize)(void* destination); + const char* name; + const char* type; + int(*FromAGENT_DATA_TYPE)(const AGENT_DATA_TYPE* source, void* dest); /*destination is "something" everytime. When the DESIRED_PROPERTY is a MODEL, the function is empty*/ + size_t offset; + size_t size; + const char* modelName; +} REFLECTION_DESIRED_PROPERTY; + +typedef struct REFLECTION_MODEL_TAG +{ + const char* name; +} REFLECTION_MODEL; + +typedef struct REFLECTED_SOMETHING_TAG +{ + REFLECTION_TYPE type; + const struct REFLECTED_SOMETHING_TAG* next; + struct what + { + REFLECTION_METHOD method; + REFLECTION_DESIRED_PROPERTY desiredProperty; + REFLECTION_REPORTED_PROPERTY reportedProperty; + REFLECTION_STRUCT structure; + REFLECTION_FIELD field; + REFLECTION_PROPERTY property; + REFLECTION_ACTION action; + REFLECTION_MODEL model; + } what; +} REFLECTED_SOMETHING; + +typedef struct REFLECTED_DATA_FROM_DATAPROVIDER_TAG +{ + const REFLECTED_SOMETHING* reflectedData; +}REFLECTED_DATA_FROM_DATAPROVIDER; + +#define ALL_SOMETHING_REFLECTED(schemaNamespace) C2(schemaNamespace, _allSomethingReflected) +#define ALL_REFLECTED(schemaNamespace) C2(schemaNamespace, _allReflected) +#define ADDRESS_OF_ALL_REFLECTED(schemaNamespace) & C2(schemaNamespace, _allReflected), +#define DECLARE_EXTERN_CONST_DATAPROVIDER_DATA(x) extern const REFLECTED_DATA_FROM_DATAPROVIDER ALL_REFLECTED(x); + +#define CODEFIRST_RESULT_VALUES \ +CODEFIRST_OK, \ +CODEFIRST_INVALID_ARG, \ +CODEFIRST_ALREADY_INIT, \ +CODEFIRST_NOT_INIT, \ +CODEFIRST_ERROR, \ +CODEFIRST_NOT_ENOUGH_MEMORY, \ +CODEFIRST_ACTION_NOT_FOUND, \ +CODEFIRST_ACTION_EXECUTION_ERROR, \ +CODEFIRST_SCHEMA_ERROR, \ +CODEFIRST_AGENT_DATA_TYPE_ERROR, \ +CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR, \ +CODEFIRST_DEVICE_FAILED, \ +CODEFIRST_DEVICE_PUBLISH_FAILED, \ +CODEFIRST_NOT_A_PROPERTY + +DEFINE_ENUM(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES) + +#include "azure_c_shared_utility/umock_c_prod.h" +MOCKABLE_FUNCTION(, CODEFIRST_RESULT, CodeFirst_Init, const char*, overrideSchemaNamespace); +MOCKABLE_FUNCTION(, void, CodeFirst_Deinit); +MOCKABLE_FUNCTION(, SCHEMA_HANDLE, CodeFirst_RegisterSchema, const char*, schemaNamespace, const REFLECTED_DATA_FROM_DATAPROVIDER*, metadata); + +MOCKABLE_FUNCTION(, EXECUTE_COMMAND_RESULT, CodeFirst_InvokeAction, DEVICE_HANDLE, deviceHandle, void*, callbackUserContext, const char*, relativeActionPath, const char*, actionName, size_t, parameterCount, const AGENT_DATA_TYPE*, parameterValues); + +MOCKABLE_FUNCTION(, METHODRETURN_HANDLE, CodeFirst_InvokeMethod, DEVICE_HANDLE, deviceHandle, void*, callbackUserContext, const char*, relativeMethodPath, const char*, methodName, size_t, parameterCount, const AGENT_DATA_TYPE*, parameterValues); + +MOCKABLE_FUNCTION(, EXECUTE_COMMAND_RESULT, CodeFirst_ExecuteCommand, void*, device, const char*, command); + +MOCKABLE_FUNCTION(, METHODRETURN_HANDLE, CodeFirst_ExecuteMethod, void*, device, const char*, methodName, const char*, methodPayload); + +MOCKABLE_FUNCTION(, void*, CodeFirst_CreateDevice, SCHEMA_MODEL_TYPE_HANDLE, model, const REFLECTED_DATA_FROM_DATAPROVIDER*, metadata, size_t, dataSize, bool, includePropertyPath); +MOCKABLE_FUNCTION(, void, CodeFirst_DestroyDevice, void*, device); + +extern CODEFIRST_RESULT CodeFirst_SendAsync(unsigned char** destination, size_t* destinationSize, size_t numProperties, ...); +extern CODEFIRST_RESULT CodeFirst_SendAsyncReported(unsigned char** destination, size_t* destinationSize, size_t numReportedProperties, ...); + +MOCKABLE_FUNCTION(, CODEFIRST_RESULT, CodeFirst_IngestDesiredProperties, void*, device, const char*, jsonPayload, bool, parseDesiredNode); + +MOCKABLE_FUNCTION(, AGENT_DATA_TYPE_TYPE, CodeFirst_GetPrimitiveType, const char*, typeName); + +#ifdef __cplusplus +} +#endif + +#endif /* CODEFIRST_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/commanddecoder.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef COMMAND_DECODER_H +#define COMMAND_DECODER_H + +#include "multitree.h" +#include "schema.h" +#include "agenttypesystem.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "methodreturn.h" + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif + +#define COMMANDDECODER_RESULT_VALUES \ + COMMANDDECODER_OK, \ + COMMANDDECODER_ERROR, \ + COMMANDDECODER_INVALID_ARG + +DEFINE_ENUM(COMMANDDECODER_RESULT, COMMANDDECODER_RESULT_VALUES) + +#define EXECUTE_COMMAND_RESULT_VALUES \ +EXECUTE_COMMAND_SUCCESS, /*when the final recipient of the command indicates a successful execution*/ \ +EXECUTE_COMMAND_FAILED, /*when the final recipient of the command indicates a failure*/ \ +EXECUTE_COMMAND_ERROR /*when a transient error either in the final recipient or until the final recipient*/ + +DEFINE_ENUM(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_RESULT_VALUES) + + + + +/* Codes_SRS_COMMAND_DECODER_99_001:[ The CommandDecoder module shall expose the following API:] */ + +typedef struct COMMAND_DECODER_HANDLE_DATA_TAG* COMMAND_DECODER_HANDLE; +typedef EXECUTE_COMMAND_RESULT(*ACTION_CALLBACK_FUNC)(void* actionCallbackContext, const char* relativeActionPath, const char* actionName, size_t argCount, const AGENT_DATA_TYPE* args); +typedef METHODRETURN_HANDLE(*METHOD_CALLBACK_FUNC)(void* methodCallbackContext, const char* relativeMethodPath, const char* methodName, size_t argCount, const AGENT_DATA_TYPE* args); + +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(,COMMAND_DECODER_HANDLE, CommandDecoder_Create, SCHEMA_MODEL_TYPE_HANDLE, modelHandle, ACTION_CALLBACK_FUNC, actionCallback, void*, actionCallbackContext, METHOD_CALLBACK_FUNC, methodCallback, void*, methodCallbackContext); +MOCKABLE_FUNCTION(,EXECUTE_COMMAND_RESULT, CommandDecoder_ExecuteCommand, COMMAND_DECODER_HANDLE, handle, const char*, command); +MOCKABLE_FUNCTION(,METHODRETURN_HANDLE, CommandDecoder_ExecuteMethod, COMMAND_DECODER_HANDLE, handle, const char*, fullMethodName, const char*, methodPayload); +MOCKABLE_FUNCTION(,void, CommandDecoder_Destroy, COMMAND_DECODER_HANDLE, commandDecoderHandle); + +MOCKABLE_FUNCTION(, EXECUTE_COMMAND_RESULT, CommandDecoder_IngestDesiredProperties, void*, startAddress, COMMAND_DECODER_HANDLE, handle, const char*, jsonPayload, bool, parseDesiredNode); + +#ifdef __cplusplus +} +#endif + +#endif /* COMMAND_DECODER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/datamarshaller.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef DATAMARSHALLER_H +#define DATAMARSHALLER_H + +#include <stdbool.h> +#include "agenttypesystem.h" +#include "schema.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/vector.h" +#ifdef __cplusplus +extern "C" +{ +#endif + +/*Codes_SRS_DATA_MARSHALLER_99_002:[ DataMarshaller shall have the following interface]*/ +#define DATA_MARSHALLER_RESULT_VALUES \ +DATA_MARSHALLER_OK, \ +DATA_MARSHALLER_INVALID_ARG, \ +DATA_MARSHALLER_NO_DEVICE_IDENTITY, \ +DATA_MARSHALLER_SCHEMA_FAILED, \ +DATA_MARSHALLER_INVALID_MODEL_PROPERTY, \ +DATA_MARSHALLER_JSON_ENCODER_ERROR, \ +DATA_MARSHALLER_ERROR, \ +DATA_MARSHALLER_AGENT_DATA_TYPES_ERROR, \ +DATA_MARSHALLER_MULTITREE_ERROR, \ +DATA_MARSHALLER_ONLY_ONE_VALUE_ALLOWED \ + +DEFINE_ENUM(DATA_MARSHALLER_RESULT, DATA_MARSHALLER_RESULT_VALUES); + +typedef struct DATA_MARSHALLER_VALUE_TAG +{ + const char* PropertyPath; + const AGENT_DATA_TYPE* Value; +} DATA_MARSHALLER_VALUE; + +typedef struct DATA_MARSHALLER_HANDLE_DATA_TAG* DATA_MARSHALLER_HANDLE; +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(,DATA_MARSHALLER_HANDLE, DataMarshaller_Create, SCHEMA_MODEL_TYPE_HANDLE, modelHandle, bool, includePropertyPath); +MOCKABLE_FUNCTION(,void, DataMarshaller_Destroy, DATA_MARSHALLER_HANDLE, dataMarshallerHandle); +MOCKABLE_FUNCTION(,DATA_MARSHALLER_RESULT, DataMarshaller_SendData, DATA_MARSHALLER_HANDLE, dataMarshallerHandle, size_t, valueCount, const DATA_MARSHALLER_VALUE*, values, unsigned char**, destination, size_t*, destinationSize); + +MOCKABLE_FUNCTION(, DATA_MARSHALLER_RESULT, DataMarshaller_SendData_ReportedProperties, DATA_MARSHALLER_HANDLE, dataMarshallerHandle, VECTOR_HANDLE, values, unsigned char**, destination, size_t*, destinationSize); + +#ifdef __cplusplus +} +#endif + +#endif /* DATAMARSHALLER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/datapublisher.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef DATA_PUBLISHER_H +#define DATA_PUBLISHER_H + +#include "agenttypesystem.h" +#include "schema.h" +/* Normally we could include <stdbool> for cpp, but some toolchains are not well behaved and simply don't have it - ARM CC for example */ +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DATA_PUBLISHER_RESULT_VALUES \ +DATA_PUBLISHER_OK, \ +DATA_PUBLISHER_INVALID_ARG, \ +DATA_PUBLISHER_MARSHALLER_ERROR, \ +DATA_PUBLISHER_EMPTY_TRANSACTION, \ +DATA_PUBLISHER_AGENT_DATA_TYPES_ERROR, \ +DATA_PUBLISHER_SCHEMA_FAILED, \ +DATA_PUBLISHER_BUFFER_STORAGE_ERROR, \ +DATA_PUBLISHER_ERROR + +DEFINE_ENUM(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_RESULT_VALUES); + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct TRANSACTION_HANDLE_DATA_TAG* TRANSACTION_HANDLE; +typedef struct REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA_TAG* REPORTED_PROPERTIES_TRANSACTION_HANDLE; +typedef struct DATA_PUBLISHER_HANDLE_DATA_TAG* DATA_PUBLISHER_HANDLE; + +MOCKABLE_FUNCTION(,DATA_PUBLISHER_HANDLE, DataPublisher_Create, SCHEMA_MODEL_TYPE_HANDLE, modelHandle, bool, includePropertyPath); +MOCKABLE_FUNCTION(,void, DataPublisher_Destroy, DATA_PUBLISHER_HANDLE, dataPublisherHandle); + +MOCKABLE_FUNCTION(,TRANSACTION_HANDLE, DataPublisher_StartTransaction, DATA_PUBLISHER_HANDLE, dataPublisherHandle); +MOCKABLE_FUNCTION(,DATA_PUBLISHER_RESULT, DataPublisher_PublishTransacted, TRANSACTION_HANDLE, transactionHandle, const char*, propertyPath, const AGENT_DATA_TYPE*, data); +MOCKABLE_FUNCTION(,DATA_PUBLISHER_RESULT, DataPublisher_EndTransaction, TRANSACTION_HANDLE, transactionHandle, unsigned char**, destination, size_t*, destinationSize); +MOCKABLE_FUNCTION(,DATA_PUBLISHER_RESULT, DataPublisher_CancelTransaction, TRANSACTION_HANDLE, transactionHandle); +MOCKABLE_FUNCTION(,void, DataPublisher_SetMaxBufferSize, size_t, value); +MOCKABLE_FUNCTION(,size_t, DataPublisher_GetMaxBufferSize); + +MOCKABLE_FUNCTION(, REPORTED_PROPERTIES_TRANSACTION_HANDLE, DataPublisher_CreateTransaction_ReportedProperties, DATA_PUBLISHER_HANDLE, dataPublisherHandle); +MOCKABLE_FUNCTION(, DATA_PUBLISHER_RESULT, DataPublisher_PublishTransacted_ReportedProperty, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle, const char*, reportedPropertyPath, const AGENT_DATA_TYPE*, data); +MOCKABLE_FUNCTION(, DATA_PUBLISHER_RESULT, DataPublisher_CommitTransaction_ReportedProperties, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle, unsigned char**, destination, size_t*, destinationSize); +MOCKABLE_FUNCTION(, void, DataPublisher_DestroyTransaction_ReportedProperties, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle); + + +#ifdef __cplusplus +} +#endif + +#endif /* DATA_PUBLISHER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/dataserializer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef DATASERIALIZER_H +#define DATASERIALIZER_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "multitree.h" +#include "azure_c_shared_utility/buffer_.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#endif + +/*Codes_SRS_DATA_SERIALIZER_07_001: [DataSerializer will have the following interface]*/ +#define DATA_SERIALIZER_RESULT_VALUES \ +DATA_SERIALIZER_INVALID_ARG, \ +DATA_SERIALIZER_ERROR \ + +DEFINE_ENUM(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_RESULT_VALUES); + +#define DATA_SERIALIZER_MULTITREE_TYPE_VALUES \ + DATA_SERIALIZER_TYPE_CHAR_PTR, \ + DATA_SERIALIZER_TYPE_AGENT_DATA \ + +DEFINE_ENUM(DATA_SERIALIZER_MULTITREE_TYPE, DATA_SERIALIZER_MULTITREE_TYPE_VALUES); + +typedef BUFFER_HANDLE (*DATA_SERIALIZER_ENCODE_FUNC)(MULTITREE_HANDLE multiTreeHandle, DATA_SERIALIZER_MULTITREE_TYPE dataType); +typedef MULTITREE_HANDLE (*DATA_SERIALIZER_DECODE_FUNC)(BUFFER_HANDLE decodeData); + +extern BUFFER_HANDLE DataSerializer_Encode(MULTITREE_HANDLE multiTreeHandle, DATA_SERIALIZER_MULTITREE_TYPE dataType, DATA_SERIALIZER_ENCODE_FUNC encodeFunc); +extern MULTITREE_HANDLE DataSerializer_Decode(BUFFER_HANDLE data, DATA_SERIALIZER_DECODE_FUNC decodeFunc); + +#ifdef __cplusplus +} +#endif + +#endif // DATASERIALIZER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/iotdevice.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTDEVICE_H +#define IOTDEVICE_H + +#include <stdbool.h> +#include "azure_c_shared_utility/macro_utils.h" +#include "schema.h" +#include "datapublisher.h" +#include "commanddecoder.h" +#include "methodreturn.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#define DEVICE_RESULT_VALUES \ + DEVICE_OK, \ + DEVICE_INVALID_ARG, \ + DEVICE_DATA_PUBLISHER_FAILED, \ + DEVICE_COMMAND_DECODER_FAILED, \ + DEVICE_ERROR + +DEFINE_ENUM(DEVICE_RESULT, DEVICE_RESULT_VALUES) + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct DEVICE_HANDLE_DATA_TAG* DEVICE_HANDLE; +typedef EXECUTE_COMMAND_RESULT (*pfDeviceActionCallback)(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeActionPath, const char* actionName, size_t argCount, const AGENT_DATA_TYPE* args); +typedef METHODRETURN_HANDLE (*pfDeviceMethodCallback)(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeMethodPath, const char* methodName, size_t argCount, const AGENT_DATA_TYPE* args); + +MOCKABLE_FUNCTION(,DEVICE_RESULT, Device_Create, SCHEMA_MODEL_TYPE_HANDLE, modelHandle, pfDeviceActionCallback, deviceActionCallback, void*, callbackUserContext, pfDeviceMethodCallback, methodCallback, void*, methodCallbackContext, bool, includePropertyPath, DEVICE_HANDLE*, deviceHandle); +MOCKABLE_FUNCTION(, void, Device_Destroy, DEVICE_HANDLE, deviceHandle); + +MOCKABLE_FUNCTION(,TRANSACTION_HANDLE, Device_StartTransaction, DEVICE_HANDLE, deviceHandle); +MOCKABLE_FUNCTION(,DEVICE_RESULT, Device_PublishTransacted, TRANSACTION_HANDLE, transactionHandle, const char*, propertyPath, const AGENT_DATA_TYPE*, data); +MOCKABLE_FUNCTION(,DEVICE_RESULT, Device_EndTransaction, TRANSACTION_HANDLE, transactionHandle, unsigned char**, destination, size_t*, destinationSize); +MOCKABLE_FUNCTION(,DEVICE_RESULT, Device_CancelTransaction, TRANSACTION_HANDLE, transactionHandle); + +MOCKABLE_FUNCTION(, REPORTED_PROPERTIES_TRANSACTION_HANDLE, Device_CreateTransaction_ReportedProperties, DEVICE_HANDLE, deviceHandle); +MOCKABLE_FUNCTION(, DEVICE_RESULT, Device_PublishTransacted_ReportedProperty, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle, const char*, reportedPropertyPath, const AGENT_DATA_TYPE*, data); +MOCKABLE_FUNCTION(, DEVICE_RESULT, Device_CommitTransaction_ReportedProperties, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle, unsigned char**, destination, size_t*, destinationSize); +MOCKABLE_FUNCTION(, void, Device_DestroyTransaction_ReportedProperties, REPORTED_PROPERTIES_TRANSACTION_HANDLE, transactionHandle); + +MOCKABLE_FUNCTION(, EXECUTE_COMMAND_RESULT, Device_ExecuteCommand, DEVICE_HANDLE, deviceHandle, const char*, command); +MOCKABLE_FUNCTION(, METHODRETURN_HANDLE, Device_ExecuteMethod, DEVICE_HANDLE, deviceHandle, const char*, methodName, const char*, methodPayload); + +MOCKABLE_FUNCTION(, DEVICE_RESULT, Device_IngestDesiredProperties, void*, startAddress, DEVICE_HANDLE, deviceHandle, const char*, jsonPayload, bool, parseDesiredNode); +#ifdef __cplusplus +} +#endif + +#endif /* IOTDEVICE_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/jsondecoder.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef JSONDECODER_H +#define JSONDECODER_H + +#include "multitree.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +typedef enum JSON_DECODER_RESULT_TAG +{ + JSON_DECODER_OK, + JSON_DECODER_INVALID_ARG, + JSON_DECODER_PARSE_ERROR, + JSON_DECODER_MULTITREE_FAILED, + JSON_DECODER_ERROR +} JSON_DECODER_RESULT; + +#include "azure_c_shared_utility/umock_c_prod.h" +MOCKABLE_FUNCTION(, JSON_DECODER_RESULT, JSONDecoder_JSON_To_MultiTree, char*, json, MULTITREE_HANDLE*, multiTreeHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* JSONDECODER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/jsonencoder.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef JSONENCODER_H +#define JSONENCODER_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/strings.h" + +#ifdef __cplusplus +#include "cstddef" +extern "C" { +#else +#include "stddef.h" +#endif + +#include "multitree.h" + +#define JSON_ENCODER_RESULT_VALUES \ +JSON_ENCODER_OK, \ +JSON_ENCODER_INVALID_ARG, \ +JSON_ENCODER_ALREADY_EXISTS, \ +JSON_ENCODER_MULTITREE_ERROR, \ +JSON_ENCODER_TOSTRING_FUNCTION_ERROR, \ +JSON_ENCODER_ERROR + +DEFINE_ENUM(JSON_ENCODER_RESULT, JSON_ENCODER_RESULT_VALUES); + +#define JSON_ENCODER_TOSTRING_RESULT_VALUES \ +JSON_ENCODER_TOSTRING_OK, \ +JSON_ENCODER_TOSTRING_INVALID_ARG, \ +JSON_ENCODER_TOSTRING_ERROR + +DEFINE_ENUM(JSON_ENCODER_TOSTRING_RESULT, JSON_ENCODER_TOSTRING_RESULT_VALUES); + +typedef JSON_ENCODER_TOSTRING_RESULT(*JSON_ENCODER_TOSTRING_FUNC)(STRING_HANDLE, const void* value); + +#include "azure_c_shared_utility/umock_c_prod.h" + +MOCKABLE_FUNCTION(, JSON_ENCODER_TOSTRING_RESULT, JSONEncoder_CharPtr_ToString, STRING_HANDLE, destination, const void*, value); +MOCKABLE_FUNCTION(, JSON_ENCODER_RESULT, JSONEncoder_EncodeTree, MULTITREE_HANDLE, treeHandle, STRING_HANDLE, destination, JSON_ENCODER_TOSTRING_FUNC, toStringFunc); + +#ifdef __cplusplus +} +#endif + + +#endif /* JSONENCODER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/methodreturn.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef METHODRETURN_H +#define METHODRETURN_H + +typedef struct METHODRETURN_HANDLE_DATA_TAG* METHODRETURN_HANDLE; + +#include "azure_c_shared_utility/macro_utils.h" + +/*the following macro expands to "const" if X is defined. If X is not defined, then it expands to nothing*/ +#define CONST_BY_COMPILATION_UNIT(X) IF(COUNT_ARG(X),const,) + +typedef struct METHODRETURN_DATA_TAG +{ + CONST_BY_COMPILATION_UNIT(METHODRETURN_C) int statusCode; + CONST_BY_COMPILATION_UNIT(METHODRETURN_C) char* jsonValue; +}METHODRETURN_DATA; + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MOCKABLE_FUNCTION(, METHODRETURN_HANDLE, MethodReturn_Create, int, statusCode, const char*, jsonValue); +MOCKABLE_FUNCTION(, void, MethodReturn_Destroy, METHODRETURN_HANDLE, handle); +MOCKABLE_FUNCTION(, const METHODRETURN_DATA*, MethodReturn_GetReturn, METHODRETURN_HANDLE, handle); + +#ifdef __cplusplus +} +#endif + + +#endif /*METHODRETURN_H*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/multitree.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MULTITREE_H +#define MULTITREE_H + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" +{ +#else +#include <stddef.h> +#include <stdbool.h> +#endif + +typedef struct MULTITREE_HANDLE_DATA_TAG* MULTITREE_HANDLE; + +#define MULTITREE_RESULT_VALUES \ + MULTITREE_OK, \ + MULTITREE_INVALID_ARG, \ + MULTITREE_ALREADY_HAS_A_VALUE, \ + MULTITREE_EMPTY_CHILD_NAME, \ + MULTITREE_EMPTY_VALUE, \ + MULTITREE_OUT_OF_RANGE_INDEX, \ + MULTITREE_ERROR, \ + MULTITREE_CHILD_NOT_FOUND \ + +DEFINE_ENUM(MULTITREE_RESULT, MULTITREE_RESULT_VALUES); + +typedef void (*MULTITREE_FREE_FUNCTION)(void* value); +typedef int (*MULTITREE_CLONE_FUNCTION)(void** destination, const void* source); + +#include "azure_c_shared_utility/umock_c_prod.h" +MOCKABLE_FUNCTION(, MULTITREE_HANDLE, MultiTree_Create, MULTITREE_CLONE_FUNCTION, cloneFunction, MULTITREE_FREE_FUNCTION, freeFunction); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_AddLeaf, MULTITREE_HANDLE, treeHandle, const char*, destinationPath, const void*, value); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_AddChild, MULTITREE_HANDLE, treeHandle, const char*, childName, MULTITREE_HANDLE*, childHandle); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetChildCount, MULTITREE_HANDLE, treeHandle, size_t*, count); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetChild, MULTITREE_HANDLE, treeHandle, size_t, index, MULTITREE_HANDLE*, childHandle); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetChildByName, MULTITREE_HANDLE, treeHandle, const char*, childName, MULTITREE_HANDLE*, childHandle); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetName, MULTITREE_HANDLE, treeHandle, STRING_HANDLE, destination); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetValue, MULTITREE_HANDLE, treeHandle, const void**, destination); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_GetLeafValue, MULTITREE_HANDLE, treeHandle, const char*, leafPath, const void**, destination); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_SetValue, MULTITREE_HANDLE, treeHandle, void*, value); +MOCKABLE_FUNCTION(, void, MultiTree_Destroy, MULTITREE_HANDLE, treeHandle); +MOCKABLE_FUNCTION(, MULTITREE_RESULT, MultiTree_DeleteChild, MULTITREE_HANDLE, treeHandle, const char*, childName); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/schema.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft. All rights reserved.MOCKABLE_FUNCTION(, +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SCHEMA_H +#define SCHEMA_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "agenttypesystem.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +/* Codes_SRS_SCHEMA_99_095: [Schema shall expose the following API:] */ + +typedef struct SCHEMA_HANDLE_DATA_TAG* SCHEMA_HANDLE; +typedef struct SCHEMA_MODEL_TYPE_HANDLE_DATA_TAG* SCHEMA_MODEL_TYPE_HANDLE; +typedef struct SCHEMA_STRUCT_TYPE_HANDLE_DATA_TAG* SCHEMA_STRUCT_TYPE_HANDLE; +typedef struct SCHEMA_PROPERTY_HANDLE_DATA_TAG* SCHEMA_PROPERTY_HANDLE; +typedef struct SCHEMA_REPORTED_PROPERTY_HANDLE_DATA_TAG* SCHEMA_REPORTED_PROPERTY_HANDLE; +typedef struct SCHEMA_DESIRED_PROPERTY_HANDLE_DATA_TAG* SCHEMA_DESIRED_PROPERTY_HANDLE; +typedef struct SCHEMA_ACTION_HANDLE_DATA_TAG* SCHEMA_ACTION_HANDLE; +typedef struct SCHEMA_ACTION_ARGUMENT_HANDLE_DATA_TAG* SCHEMA_ACTION_ARGUMENT_HANDLE; +typedef struct SCHEMA_METHOD_ARGUMENT_HANDLE_DATA_TAG* SCHEMA_METHOD_ARGUMENT_HANDLE; +typedef struct SCHEMA_METHOD_HANDLE_DATA_TAG* SCHEMA_METHOD_HANDLE; + + +typedef void(*pfOnDesiredProperty)(void* model); +typedef int(*pfDesiredPropertyFromAGENT_DATA_TYPE)(const AGENT_DATA_TYPE* source, void* dest); +typedef void(*pfDesiredPropertyInitialize)(void* destination); +typedef void(*pfDesiredPropertyDeinitialize)(void* destination); + + +#define SCHEMA_RESULT_VALUES \ +SCHEMA_OK, \ +SCHEMA_INVALID_ARG, \ +SCHEMA_DUPLICATE_ELEMENT, \ +SCHEMA_ELEMENT_NOT_FOUND, \ +SCHEMA_MODEL_IN_USE, \ +SCHEMA_DEVICE_COUNT_ZERO, \ +SCHEMA_ERROR + +DEFINE_ENUM(SCHEMA_RESULT, SCHEMA_RESULT_VALUES) + +#define SCHEMA_ELEMENT_TYPE_VALUES \ +SCHEMA_NOT_FOUND, \ +SCHEMA_SEARCH_INVALID_ARG, \ +SCHEMA_PROPERTY, \ +SCHEMA_REPORTED_PROPERTY, \ +SCHEMA_DESIRED_PROPERTY, \ +SCHEMA_MODEL_ACTION, \ +SCHEMA_MODEL_IN_MODEL + +DEFINE_ENUM(SCHEMA_ELEMENT_TYPE, SCHEMA_ELEMENT_TYPE_VALUES); + +typedef struct SCHEMA_MODEL_ELEMENT_TAG +{ + SCHEMA_ELEMENT_TYPE elementType; + union ELEMENT_HANDLE_UNION_TAG + { + SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle; + SCHEMA_PROPERTY_HANDLE propertyHandle; + SCHEMA_REPORTED_PROPERTY_HANDLE reportedPropertyHandle; + SCHEMA_ACTION_HANDLE actionHandle; + SCHEMA_MODEL_TYPE_HANDLE modelHandle; + } elementHandle; +}SCHEMA_MODEL_ELEMENT; + +MOCKABLE_FUNCTION(, SCHEMA_HANDLE, Schema_Create, const char*, schemaNamespace, void*, metadata); +MOCKABLE_FUNCTION(, void*, Schema_GetMetadata, SCHEMA_HANDLE, schemaHandle); +MOCKABLE_FUNCTION(, size_t, Schema_GetSchemaCount); +MOCKABLE_FUNCTION(, SCHEMA_HANDLE, Schema_GetSchemaByNamespace, const char*, schemaNamespace); +MOCKABLE_FUNCTION(, SCHEMA_HANDLE, Schema_GetSchemaForModel, const char*, modelName); +MOCKABLE_FUNCTION(, const char*, Schema_GetSchemaNamespace, SCHEMA_HANDLE, schemaHandle); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddDeviceRef, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_ReleaseDeviceRef, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle); + +MOCKABLE_FUNCTION(, SCHEMA_MODEL_TYPE_HANDLE, Schema_CreateModelType, SCHEMA_HANDLE, schemaHandle, const char*, modelName); +MOCKABLE_FUNCTION(, SCHEMA_HANDLE, Schema_GetSchemaForModelType, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetModelName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle); + +MOCKABLE_FUNCTION(, SCHEMA_STRUCT_TYPE_HANDLE, Schema_CreateStructType, SCHEMA_HANDLE, schemaHandle, const char*, structTypeName); + +MOCKABLE_FUNCTION(, const char*, Schema_GetStructTypeName, SCHEMA_STRUCT_TYPE_HANDLE, structTypeHandle); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddStructTypeProperty, SCHEMA_STRUCT_TYPE_HANDLE, structTypeHandle, const char*, propertyName, const char*, propertyType); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelProperty, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName, const char*, propertyType); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelReportedProperty, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, reportedPropertyName, const char*, reportedPropertyType); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelDesiredProperty, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, desiredPropertyName, const char*, desiredPropertyType, pfDesiredPropertyFromAGENT_DATA_TYPE, desiredPropertyFromAGENT_DATA_TYPE, pfDesiredPropertyInitialize, desiredPropertyInitialize, pfDesiredPropertyDeinitialize, desiredPropertyDeinitialize, size_t, offset, pfOnDesiredProperty, onDesiredProperty); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelModel, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName, SCHEMA_MODEL_TYPE_HANDLE, modelType, size_t, offset, pfOnDesiredProperty, onDesiredProperty); +MOCKABLE_FUNCTION(, SCHEMA_ACTION_HANDLE, Schema_CreateModelAction, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, actionName); +MOCKABLE_FUNCTION(, SCHEMA_METHOD_HANDLE, Schema_CreateModelMethod, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, methodName); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelActionArgument, SCHEMA_ACTION_HANDLE, actionHandle, const char*, argumentName, const char*, argumentType); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_AddModelMethodArgument, SCHEMA_METHOD_HANDLE, methodHandle, const char*, argumentName, const char*, argumentType); +MOCKABLE_FUNCTION(, pfDesiredPropertyFromAGENT_DATA_TYPE, Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); +MOCKABLE_FUNCTION(, pfOnDesiredProperty, Schema_GetModelDesiredProperty_pfOnDesiredProperty, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); + + +MOCKABLE_FUNCTION(, size_t, Schema_GetModelModelByName_Offset, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName); +MOCKABLE_FUNCTION(, pfOnDesiredProperty, Schema_GetModelModelByName_OnDesiredProperty, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName); + +MOCKABLE_FUNCTION(, size_t, Schema_GetModelModelByIndex_Offset, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, size_t, Schema_GetModelDesiredProperty_offset, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetModelDesiredPropertyType, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); +MOCKABLE_FUNCTION(, pfDesiredPropertyDeinitialize, Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); +MOCKABLE_FUNCTION(, pfDesiredPropertyInitialize, Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize, SCHEMA_DESIRED_PROPERTY_HANDLE, desiredPropertyHandle); + +MOCKABLE_FUNCTION(, SCHEMA_MODEL_ELEMENT, Schema_GetModelElementByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, elementName); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelCount, SCHEMA_HANDLE, schemaHandle, size_t*, modelCount); +MOCKABLE_FUNCTION(, SCHEMA_MODEL_TYPE_HANDLE, Schema_GetModelByName, SCHEMA_HANDLE, schemaHandle, const char*, modelName); +MOCKABLE_FUNCTION(, SCHEMA_MODEL_TYPE_HANDLE, Schema_GetModelByIndex, SCHEMA_HANDLE, schemaHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelPropertyCount, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t*, propertyCount); +MOCKABLE_FUNCTION(, SCHEMA_PROPERTY_HANDLE, Schema_GetModelPropertyByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName); +MOCKABLE_FUNCTION(, SCHEMA_PROPERTY_HANDLE, Schema_GetModelPropertyByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelReportedPropertyCount, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t*, reportedPropertyCount); +MOCKABLE_FUNCTION(, SCHEMA_REPORTED_PROPERTY_HANDLE, Schema_GetModelReportedPropertyByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, reportedPropertyName); +MOCKABLE_FUNCTION(, SCHEMA_REPORTED_PROPERTY_HANDLE, Schema_GetModelReportedPropertyByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelDesiredPropertyCount, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t*, desiredPropertyCount); +MOCKABLE_FUNCTION(, SCHEMA_DESIRED_PROPERTY_HANDLE, Schema_GetModelDesiredPropertyByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, desiredPropertyName); +MOCKABLE_FUNCTION(, SCHEMA_DESIRED_PROPERTY_HANDLE, Schema_GetModelDesiredPropertyByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelModelCount, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t*, modelCount); +MOCKABLE_FUNCTION(, SCHEMA_MODEL_TYPE_HANDLE, Schema_GetModelModelByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyName); +MOCKABLE_FUNCTION(, SCHEMA_MODEL_TYPE_HANDLE, Schema_GetModelModelyByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); +MOCKABLE_FUNCTION(, const char*, Schema_GetModelModelPropertyNameByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, bool, Schema_ModelPropertyByPathExists, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, propertyPath); + +MOCKABLE_FUNCTION(, bool, Schema_ModelReportedPropertyByPathExists, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, reportedPropertyPath); +MOCKABLE_FUNCTION(, bool, Schema_ModelDesiredPropertyByPathExists, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, desiredPropertyPath); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelActionCount, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t*, actionCount); +MOCKABLE_FUNCTION(, SCHEMA_ACTION_HANDLE, Schema_GetModelActionByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, actionName); +MOCKABLE_FUNCTION(, SCHEMA_METHOD_HANDLE, Schema_GetModelMethodByName, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, const char*, methodName); +MOCKABLE_FUNCTION(, SCHEMA_ACTION_HANDLE, Schema_GetModelActionByIndex, SCHEMA_MODEL_TYPE_HANDLE, modelTypeHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelActionArgumentCount, SCHEMA_ACTION_HANDLE, actionHandle, size_t*, argumentCount); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetModelMethodArgumentCount, SCHEMA_METHOD_HANDLE, methodHandle, size_t*, argumentCount); +MOCKABLE_FUNCTION(, const char*, Schema_GetModelActionName, SCHEMA_ACTION_HANDLE, actionHandle); + +MOCKABLE_FUNCTION(, SCHEMA_ACTION_ARGUMENT_HANDLE, Schema_GetModelActionArgumentByName, SCHEMA_ACTION_HANDLE, actionHandle, const char*, actionArgumentName); +MOCKABLE_FUNCTION(, SCHEMA_ACTION_ARGUMENT_HANDLE, Schema_GetModelActionArgumentByIndex, SCHEMA_ACTION_HANDLE, actionHandle, size_t, argumentIndex); +MOCKABLE_FUNCTION(, SCHEMA_METHOD_ARGUMENT_HANDLE, Schema_GetModelMethodArgumentByIndex, SCHEMA_METHOD_HANDLE, actionHandle, size_t, argumentIndex); +MOCKABLE_FUNCTION(, const char*, Schema_GetActionArgumentName, SCHEMA_ACTION_ARGUMENT_HANDLE, actionArgumentHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetMethodArgumentName, SCHEMA_METHOD_ARGUMENT_HANDLE, methodArgumentHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetActionArgumentType, SCHEMA_ACTION_ARGUMENT_HANDLE, actionArgumentHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetMethodArgumentType, SCHEMA_METHOD_ARGUMENT_HANDLE, methodArgumentHandle); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetStructTypeCount, SCHEMA_HANDLE, schemaHandle, size_t*, structTypeCount); +MOCKABLE_FUNCTION(, SCHEMA_STRUCT_TYPE_HANDLE, Schema_GetStructTypeByName, SCHEMA_HANDLE, schemaHandle, const char*, structTypeName); +MOCKABLE_FUNCTION(, SCHEMA_STRUCT_TYPE_HANDLE, Schema_GetStructTypeByIndex, SCHEMA_HANDLE, schemaHandle, size_t, index); + +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_GetStructTypePropertyCount, SCHEMA_STRUCT_TYPE_HANDLE, structTypeHandle, size_t*, propertyCount); +MOCKABLE_FUNCTION(, SCHEMA_PROPERTY_HANDLE, Schema_GetStructTypePropertyByName, SCHEMA_STRUCT_TYPE_HANDLE, structTypeHandle, const char*, propertyName); +MOCKABLE_FUNCTION(, SCHEMA_PROPERTY_HANDLE, Schema_GetStructTypePropertyByIndex, SCHEMA_STRUCT_TYPE_HANDLE, structTypeHandle, size_t, index); +MOCKABLE_FUNCTION(, const char*, Schema_GetPropertyName, SCHEMA_PROPERTY_HANDLE, propertyHandle); +MOCKABLE_FUNCTION(, const char*, Schema_GetPropertyType, SCHEMA_PROPERTY_HANDLE, propertyHandle); + +MOCKABLE_FUNCTION(, void, Schema_Destroy, SCHEMA_HANDLE, schemaHandle); +MOCKABLE_FUNCTION(, SCHEMA_RESULT, Schema_DestroyIfUnused,SCHEMA_MODEL_TYPE_HANDLE, modelHandle); + +#ifdef __cplusplus +} +#endif + +#endif /* SCHEMA_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/schemalib.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file schemalib.h + * @brief The IoT Hub Serializer APIs allows developers to define models for +* their devices + */ + +#ifndef SCHEMALIB_H +#define SCHEMALIB_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/strings.h" +#include "iotdevice.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Codes_SRS_SCHEMALIB__99_001:[ IoTHubSchema_Client shall expose the following API: ... ] */ +#define SERIALIZER_RESULT_VALUES \ + SERIALIZER_OK, \ + SERIALIZER_INVALID_ARG, \ + SERIALIZER_CODEFIRST_INIT_FAILED, \ + SERIALIZER_SCHEMA_FAILED, \ + SERIALIZER_HTTP_API_INIT_FAILED, \ + SERIALIZER_ALREADY_INIT, \ + SERIALIZER_ERROR, \ + SERIALIZER_NOT_INITIALIZED, \ + SERIALIZER_ALREADY_STARTED, \ + SERIALIZER_DEVICE_CREATE_FAILED, \ + SERIALIZER_GET_MODEL_HANDLE_FAILED, \ + SERIALIZER_SERVICEBUS_FAILED + +/** @brief Enumeration specifying the status of calls to various APIs in this + * module. + */ +DEFINE_ENUM(SERIALIZER_RESULT, SERIALIZER_RESULT_VALUES); + +#define SERIALIZER_CONFIG_VALUES \ + CommandPollingInterval, \ + SerializeDelayedBufferMaxSize + +/** @brief Enumeration specifying the option to set on the serializer when + * calling ::serializer_setconfig. + */ +DEFINE_ENUM(SERIALIZER_CONFIG, SERIALIZER_CONFIG_VALUES); + +/** + * @brief Initializes the library. + * + * @param overrideSchemaNamespace An override schema namespace to use for all + * models. Optional, can be @c NULL. + * + * If @p schemaNamespace is not @c NULL, its value shall be used + * instead of the namespace defined for each model by using the + * @c DECLARE_XXX macro. + * + * @return @c SERIALIZER_OK on success and any other error on failure. + */ +extern SERIALIZER_RESULT serializer_init(const char* overrideSchemaNamespace); + +/** @brief Shuts down the IOT library. + * + * The library will track all created devices and upon a call to + * ::serializer_deinit it will de-initialize all devices. + */ +extern void serializer_deinit(void); + +/** + * @brief Set serializer options. + * + * @param which The option to be set. + * @param value The value to set for the given option. + * + * @return @c SERIALIZER_OK on success and any other error on failure. + */ +extern SERIALIZER_RESULT serializer_setconfig(SERIALIZER_CONFIG which, void* value); + +#ifdef __cplusplus +} +#endif + +#endif /* SCHEMALIB_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/schemaserializer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SCHEMASERIALIZER_H +#define SCHEMASERIALIZER_H + +#include "schema.h" +#include "azure_c_shared_utility/strings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SCHEMA_SERIALIZER_RESULT_VALUES \ + SCHEMA_SERIALIZER_OK, \ + SCHEMA_SERIALIZER_INVALID_ARG, \ + SCHEMA_SERIALIZER_ERROR + +DEFINE_ENUM(SCHEMA_SERIALIZER_RESULT, SCHEMA_SERIALIZER_RESULT_VALUES) + +extern SCHEMA_SERIALIZER_RESULT SchemaSerializer_SerializeCommandMetadata(SCHEMA_MODEL_TYPE_HANDLE modelHandle, STRING_HANDLE schemaText); + +#ifdef __cplusplus +} +#endif + +#endif /* SCHEMASERIALIZER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/serializer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1520 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file serializer.h +* @brief The IoT Hub Serializer APIs allows developers to define models for +* their devices +* +* @details The IoT Hub Serializer APIs allows developers to quickly and easily define +* models for their devices directly as code, while supporting the required +* features for modeling devices (including multiple models and multiple +* devices within the same application). For example: +* +* <pre> +* BEGIN_NAMESPACE(Contoso); +* +* DECLARE_STRUCT(SystemProperties, +* ascii_char_ptr, DeviceID, +* _Bool, Enabled +* ); +* +* DECLARE_MODEL(VendingMachine, +* +* WITH_DATA(int, SensorValue), +* +* WITH_DATA(ascii_char_ptr, ObjectName), +* WITH_DATA(ascii_char_ptr, ObjectType), +* WITH_DATA(ascii_char_ptr, Version), +* WITH_DATA(SystemProperties, SystemProperties), +* WITH_DATA(ascii_char_ptr_no_quotes, Commands), +* +* WITH_ACTION(SetItemPrice, ascii_char_ptr, itemId, ascii_char_ptr, price) +* ); +* +* END_NAMESPACE(Contoso); +* </pre> +*/ + +#ifndef SERIALIZER_H +#define SERIALIZER_H + +#ifdef __cplusplus +#include <cstdlib> +#include <cstdarg> + +#else +#include <stdlib.h> +#include <stdarg.h> +#endif + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "iotdevice.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "methodreturn.h" +#include "schemalib.h" +#include "codefirst.h" +#include "agenttypesystem.h" +#include "schema.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* IOT Agent Macros */ + + /** + * @def BEGIN_NAMESPACE(schemaNamespace) + * This macro marks the start of a section that declares IOT model + * elements (like complex types, etc.). Declarations are typically + * placed in header files, so that they can be shared between + * translation units. + */ + /* Codes_SRS_SERIALIZER_99_001:[For each completed schema declaration block, a unique storage container for schema metadata shall be available in the translation unit at runtime.] */ +#define BEGIN_NAMESPACE(schemaNamespace) \ + REFLECTED_END_OF_LIST + +/** +* @def END_NAMESPACE(schemaNamespace) +* This macro marks the end of a section that declares IOT model +* elements. +*/ +#define END_NAMESPACE(schemaNamespace) \ + REFLECTED_LIST_HEAD(schemaNamespace) + +#define GLOBAL_INITIALIZE_STRUCT_FIELD(structType, destination, type, name) GlobalInitialize_##type((char*)destination+offsetof(structType, name)); +#define GLOBAL_DEINITIALIZE_STRUCT_FIELD(structType, destination, type, name) GlobalDeinitialize_##type((char*)destination+offsetof(structType, name)); +/** +* @def DECLARE_STRUCT(name, ...) +* This macro allows the definition of a struct type that can then be used as +* part of a model definition. +* +* @param name Name of the struct +* @param element1, element2... Specifies a list of struct members +*/ +/* Codes_SRS_SERIALIZER_99_080:[ The DECLARE_STRUCT declaration shall insert metadata describing a complex data type.] */ +#define DECLARE_STRUCT(name, ...) \ + /* Codes_SRS_SERIALIZER_99_096:[ DECLARE_STRUCT shall declare a matching C struct data type named name, which can be referenced from any code that can access the declaration.] */ \ + typedef struct name##_TAG { \ + FOR_EACH_2(INSERT_FIELD_INTO_STRUCT, __VA_ARGS__) \ + } name; \ + /* Codes_SRS_SERIALIZER_99_081:[ DECLARE_STRUCT's name argument shall uniquely identify the struct within the schema.] */ \ + REFLECTED_STRUCT(name) \ + /* Codes_SRS_SERIALIZER_99_082:[ DECLARE_STRUCT's field<n>Name argument shall uniquely name a field within the struct.] */ \ + FOR_EACH_2_KEEP_1(REFLECTED_FIELD, name, __VA_ARGS__) \ + TO_AGENT_DATA_TYPE(name, __VA_ARGS__) \ + /*Codes_SRS_SERIALIZER_99_042:[ The parameter types are either predefined parameter types (specs SRS_SERIALIZER_99_004-SRS_SERIALIZER_99_014) or a type introduced by DECLARE_STRUCT.]*/ \ + static AGENT_DATA_TYPES_RESULT FromAGENT_DATA_TYPE_##name(const AGENT_DATA_TYPE* source, name* destination) \ + { \ + AGENT_DATA_TYPES_RESULT result; \ + if(source->type != EDM_COMPLEX_TYPE_TYPE) \ + { \ + result = AGENT_DATA_TYPES_INVALID_ARG; \ + } \ + else if(DIV2(COUNT_ARG(__VA_ARGS__)) != source->value.edmComplexType.nMembers) \ + { \ + /*too many or too few fields*/ \ + result = AGENT_DATA_TYPES_INVALID_ARG; \ + } \ + else \ + { \ + result = AGENT_DATA_TYPES_OK; \ + FOR_EACH_2(BUILD_DESTINATION_FIELD, __VA_ARGS__); \ + } \ + return result; \ + } \ + static void C2(destroyLocalParameter, name)(name * value) \ + { \ + FOR_EACH_2_KEEP_1(UNBUILD_DESTINATION_FIELD, value, __VA_ARGS__); \ + } \ + static void C2(GlobalInitialize_, name)(void* destination) \ + { \ + FOR_EACH_2_KEEP_2(GLOBAL_INITIALIZE_STRUCT_FIELD, name, destination, __VA_ARGS__); \ + } \ + static void C2(GlobalDeinitialize_, name)(void* destination) \ + { \ + FOR_EACH_2_KEEP_2(GLOBAL_DEINITIALIZE_STRUCT_FIELD, name, destination, __VA_ARGS__); \ + } \ + + +/** + * @def DECLARE_MODEL(name, ...) + * This macro allows declaring a model that can be later used to instantiate + * a device. + * + * @param name Specifies the model name + * @param element1, element2... Specifies a model element which can be + * a property or an action. + * - A property is described in a + * model by using the WITH_DATA + * - An action is described in a + * model by using the ::WITH_ACTION + * macro. + * + */ + /* WITH_DATA's name argument shall be one of the following data types: */ + /* Codes_SRS_SERIALIZER_99_133:[a model type introduced previously by DECLARE_MODEL] */ + +#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_ACTION(...) +#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_METHOD(...) +#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_DESIRED_PROPERTY(type, name, ...) IF(COUNT_ARG(__VA_ARGS__), void __VA_ARGS__ (void*);, ) +#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_PROPERTY(...) +#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_REPORTED_PROPERTY(...) + +#define CREATE_DESIRED_PROPERTY_CALLBACK(...) CREATE_DESIRED_PROPERTY_CALLBACK_##__VA_ARGS__ + +#define SERIALIZER_REGISTER_NAMESPACE(NAMESPACE) CodeFirst_RegisterSchema(#NAMESPACE, & ALL_REFLECTED(NAMESPACE)) + +#define DECLARE_MODEL(name, ...) \ + REFLECTED_MODEL(name) \ + FOR_EACH_1(CREATE_DESIRED_PROPERTY_CALLBACK, __VA_ARGS__) \ + typedef struct name { int :1; FOR_EACH_1(BUILD_MODEL_STRUCT, __VA_ARGS__) } name; \ + FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT, name, __VA_ARGS__) \ + TO_AGENT_DATA_TYPE(name, DROP_FIRST_COMMA_FROM_ARGS(EXPAND_MODEL_ARGS(__VA_ARGS__))) \ + int FromAGENT_DATA_TYPE_##name(const AGENT_DATA_TYPE* source, void* destination) \ + { \ + (void)source; \ + (void)destination; \ + LogError("SHOULD NOT GET CALLED... EVER"); \ + return 0; \ + } \ + static void C2(GlobalInitialize_, name)(void* destination) \ + { \ + (void)destination; \ + FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT_GLOBAL_INITIALIZE, name, __VA_ARGS__) \ + } \ + static void C2(GlobalDeinitialize_, name)(void* destination) \ + { \ + (void)destination; \ + FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT_GLOBAL_DEINITIALIZE, name, __VA_ARGS__) \ + } \ + + + +/** + * @def WITH_DATA(type, name) + * The ::WITH_DATA macro allows declaring a model property in a model. A + * property can be published by using the ::SERIALIZE macro. + * + * @param type Specifies the property type. Can be any of the following + * types: + * - int + * - double + * - float + * - long + * - int8_t + * - uint8_t + * - int16_t + * - int32_t + * - int64_t + * - bool + * - ascii_char_ptr + * - EDM_DATE_TIME_OFFSET + * - EDM_GUID + * - EDM_BINARY + * - Any struct type previously introduced by another ::DECLARE_STRUCT. + * + * @param name Specifies the property name + */ +#define WITH_DATA(type, name) MODEL_PROPERTY(type, name) + + +#define WITH_REPORTED_PROPERTY(type, name) MODEL_REPORTED_PROPERTY(type, name) + +#define WITH_DESIRED_PROPERTY(type, name, ...) MODEL_DESIRED_PROPERTY(type, name, __VA_ARGS__) + +/** + * @def WITH_ACTION(name, ...) + * The ::WITH_ACTION macro allows declaring a model action. + * + * @param name Specifies the action name. + * @param argXtype, argXName... Defines the type and name for the X<sup>th</sup> + * argument of the action. The type can be any of + * the primitive types or a struct type. + */ +/*Codes_SRS_SERIALIZER_99_039:[WITH_ACTION shall declare an action of the current data provider called as the first macro parameter(name) and having the first parameter called parameter1Name of type parameter1Type, the second parameter named parameter2Name having the type parameter2Type and so on.]*/ +#define WITH_ACTION(name, ...) MODEL_ACTION(name, __VA_ARGS__) + + +/** +* @def WITH_METHOD(name, ...) +* The ::WITH_METHOD macro allows declaring a model method. +* +* @param name Specifies the method name. +* @param argXtype, argXName... Defines the type and name for the X<sup>th</sup> +* argument of the method. The type can be any of +* the primitive types or a struct type. +*/ +/*Codes_SRS_SERIALIZER_H_02_029: [ WITH_METHOD shall declare a function with the signature 'METHODRETURN_HANDLE name(param1Type param1Name, ...)', which the developer can define to receive corresponding commands from the IoT service. ]*/ +#define WITH_METHOD(name, ...) MODEL_METHOD(name, __VA_ARGS__) + + +/** + * @def GET_MODEL_HANDLE(schemaNamespace, modelName) + * The ::GET_MODEL_HANDLE macro returns a model handle that can be used in + * subsequent operations like generating the CSDL schema for the model, + * uploading the schema, creating a device, etc. + * + * @param schemaNamespace The namespace to which the model belongs. + * @param modelName The name of the model. + */ +/* Codes_SRS_SERIALIZER_99_110:[ The GET_MODEL_HANDLE function macro shall first register the schema by calling CodeFirst_RegisterSchema, passing schemaNamespace and a pointer to the metadata generated in the schema declaration block.] */ +/* Codes_SRS_SERIALIZER_99_094:[ GET_MODEL_HANDLE shall then call Schema_GetModelByName, passing the schemaHandle obtained from CodeFirst_RegisterSchema and modelName arguments, to retrieve the SCHEMA_MODEL_TYPE_HANDLE corresponding to the modelName argument.] */ +/* Codes_SRS_SERIALIZER_99_112:[ GET_MODEL_HANDLE will return the handle for the named model.] */ +#define GET_MODEL_HANDLE(schemaNamespace, modelName) \ + Schema_GetModelByName(CodeFirst_RegisterSchema(TOSTRING(schemaNamespace), &ALL_REFLECTED(schemaNamespace)), #modelName) + +/* Codes_SRS_SERIALIZER_01_002: [If the argument serializerIncludePropertyPath is specified, its value shall be passed to CodeFirst_Create.] */ +#define CREATE_DEVICE_WITH_INCLUDE_PROPERTY_PATH(schemaNamespace, modelName, serializerIncludePropertyPath) \ + (modelName*)CodeFirst_CreateDevice(GET_MODEL_HANDLE(schemaNamespace, modelName), &ALL_REFLECTED(schemaNamespace), sizeof(modelName), serializerIncludePropertyPath) + +/* Codes_SRS_SERIALIZER_01_003: [If the argument serializerIncludePropertyPath is not specified, CREATE_MODEL_INSTANCE shall pass false to CodeFirst_Create.] */ +#define CREATE_DEVICE_WITHOUT_INCLUDE_PROPERTY_PATH(schemaNamespace, modelName) \ + (modelName*)CodeFirst_CreateDevice(GET_MODEL_HANDLE(schemaNamespace, modelName), &ALL_REFLECTED(schemaNamespace), sizeof(modelName), false) + +/* Codes_SRS_SERIALIZER_99_104:[ CREATE_MODEL_INSTANCE shall call GET_MODEL_HANDLE, passing schemaNamespace and modelName, to get a model handle representing the model defined in the corresponding schema declaration block.] */ +/* Codes_SRS_SERIALIZER_99_106:[ CREATE_MODEL_INSTANCE shall call CodeFirst_CreateDevice, passing the model handle (SCHEMA_MODEL_TYPE_HANDLE]*/ +/* Codes_SRS_SERIALIZER_99_107:[ If CodeFirst_CreateDevice fails, CREATE_MODEL_INSTANCE shall return NULL.] */ +/* Codes_SRS_SERIALIZER_99_108:[ If CodeFirst_CreateDevice succeeds, CREATE_MODEL_INSTANCE shall return a pointer to an instance of the C struct representing the model for the device.] */ +#define CREATE_MODEL_INSTANCE(schemaNamespace, ...) \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), CREATE_DEVICE_WITH_INCLUDE_PROPERTY_PATH, CREATE_DEVICE_WITHOUT_INCLUDE_PROPERTY_PATH) (schemaNamespace, __VA_ARGS__) + +/* Codes_SRS_SERIALIZER_99_109:[ DESTROY_MODEL_INSTANCE shall call CodeFirst_DestroyDevice, passing the pointer returned from CREATE_MODEL_INSTANCE, to release all resources associated with the device.] */ +#define DESTROY_MODEL_INSTANCE(deviceData) \ + CodeFirst_DestroyDevice(deviceData) + +/** + * @def SERIALIZE(destination, destinationSize,...) + * This macro produces JSON serialized representation of the properties. + * + * @param destination Pointer to an @c unsigned @c char* that + * will receive the serialized data. + * @param destinationSize Pointer to a @c size_t that gets + * written with the size in bytes of the + * serialized data + * @param property1, property2... A list of property values to send. The + * order in which the properties appear in + * the list does not matter, all values + * will be sent together. + * + */ +/*Codes_SRS_SERIALIZER_99_113:[ SERIALIZE shall call CodeFirst_SendAsync, passing a destination, destinationSize, the number of properties to publish, and pointers to the values for each property.] */ +/*Codes_SRS_SERIALIZER_99_117:[ If CodeFirst_SendAsync succeeds, SEND will return IOT_AGENT_OK.] */ +/*Codes_SRS_SERIALIZER_99_114:[ If CodeFirst_SendAsync fails, SEND shall return IOT_AGENT_SERIALIZE_FAILED.] */ +#define SERIALIZE(destination, destinationSize,...) CodeFirst_SendAsync(destination, destinationSize, COUNT_ARG(__VA_ARGS__) FOR_EACH_1(ADDRESS_MACRO, __VA_ARGS__)) + +#define SERIALIZE_REPORTED_PROPERTIES(destination, destinationSize,...) CodeFirst_SendAsyncReported(destination, destinationSize, COUNT_ARG(__VA_ARGS__) FOR_EACH_1(ADDRESS_MACRO, __VA_ARGS__)) + + +#define IDENTITY_MACRO(x) ,x +#define SERIALIZE_REPORTED_PROPERTIES_FROM_POINTERS(destination, destinationSize, ...) CodeFirst_SendAsyncReported(destination, destinationSize, COUNT_ARG(__VA_ARGS__) FOR_EACH_1(IDENTITY_MACRO, __VA_ARGS__)) + +/** + * @def EXECUTE_COMMAND(device, command) + * Any action that is declared in a model must also have an implementation as + * a C function. + * + * @param device Pointer to device data. + * @param command Values that match the arguments declared in the model + * action. + */ +/*Codes_SRS_SERIALIZER_02_018: [EXECUTE_COMMAND macro shall call CodeFirst_ExecuteCommand passing device, commandBuffer and commandBufferSize.]*/ +#define EXECUTE_COMMAND(device, command) (CodeFirst_ExecuteCommand(device, command)) + +/** +* @def EXECUTE_METHOD(device, methodName, methodPayload) +* Any method that is declared in a model must also have an implementation as +* a C function. +* +* @param device Pointer to device data. +* @param methodName The method name. +* @param methodPayload The method payload. +*/ +#define EXECUTE_METHOD(device, methodName, methodPayload) CodeFirst_ExecuteMethod(device, methodName, methodPayload) + +/** +* @def INGEST_DESIRED_PROPERTIES(device, desiredProperties) +* +* @param device return of CodeFirst_CreateDevice. +* @param desiredProperties a null terminated string containing in JSON format the desired properties +*/ +#define INGEST_DESIRED_PROPERTIES(device, jsonPayload, parseDesiredNode) (CodeFirst_IngestDesiredProperties(device, jsonPayload, parseDesiredNode)) + +/* Helper macros */ + +/* These macros remove a useless comma from the beginning of an argument list that looks like: +,x1,y1,x2,y2 */ +#ifdef _MSC_VER + +#define DROP_FIRST_COMMA(N, x) \ +x IFCOMMA_NOFIRST(N) + +#define DROP_IF_EMPTY(N, x) \ +IF(COUNT_ARG(x),DROP_FIRST_COMMA(N,x),x) + +#define DROP_FIRST_COMMA_FROM_ARGS(...) \ +FOR_EACH_1_COUNTED(DROP_IF_EMPTY, C1(__VA_ARGS__)) + +#else + +#define DROP_FIRST_COMMA_0(N, x) \ + x IFCOMMA_NOFIRST(N) + +#define DROP_FIRST_COMMA_1(N, x) \ + x + +#define DROP_FIRST_COMMA(empty, N, x) \ + C2(DROP_FIRST_COMMA_,empty)(N,x) + +#define DROP_IF_EMPTY(N, x) \ + DROP_FIRST_COMMA(ISEMPTY(x),N,x) + +#define DROP_FIRST_COMMA_FROM_ARGS(...) \ + FOR_EACH_1_COUNTED(DROP_IF_EMPTY, __VA_ARGS__) + +#endif + +/* These macros expand a sequence of arguments for DECLARE_MODEL that looks like +WITH_DATA(x, y), WITH_DATA(x2, y2) to a list of arguments consumed by the macro that marshalls a struct, like: +x, y, x2, y2 +Actions are discarded, since no marshalling will be done for those when sending state data */ +#define TO_AGENT_DT_EXPAND_MODEL_PROPERTY(x, y) ,x,y + +#define TO_AGENT_DT_EXPAND_MODEL_REPORTED_PROPERTY(x, y) ,x,y + +#define TO_AGENT_DT_EXPAND_MODEL_DESIRED_PROPERTY(x, y, ...) ,x,y + +#define TO_AGENT_DT_EXPAND_MODEL_ACTION(...) + +#define TO_AGENT_DT_EXPAND_MODEL_METHOD(...) + +#define TO_AGENT_DT_EXPAND_ELEMENT_ARGS(N, ...) TO_AGENT_DT_EXPAND_##__VA_ARGS__ + +#define EXPAND_MODEL_ARGS(...) \ + FOR_EACH_1_COUNTED(TO_AGENT_DT_EXPAND_ELEMENT_ARGS, __VA_ARGS__) + +#define TO_AGENT_DATA_TYPE(name, ...) \ + static AGENT_DATA_TYPES_RESULT ToAGENT_DATA_TYPE_##name(AGENT_DATA_TYPE *destination, const name value) \ + { \ + AGENT_DATA_TYPES_RESULT result = AGENT_DATA_TYPES_OK; \ + size_t iMember = 0; \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(phantomName, 1); \ + const char* memberNames[IF(DIV2(C1(COUNT_ARG(__VA_ARGS__))), DIV2(C1(COUNT_ARG(__VA_ARGS__))), 1)] = { 0 }; \ + size_t memberCount = sizeof(memberNames) / sizeof(memberNames[0]); \ + (void)value; \ + if (memberCount == 0) \ + { \ + result = AGENT_DATA_TYPES_OK; \ + } \ + else \ + { \ + AGENT_DATA_TYPE members[sizeof(memberNames) / sizeof(memberNames[0])]; \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(phantomName, 2); \ + FOR_EACH_2(FIELD_AS_STRING, EXPAND_TWICE(__VA_ARGS__)) \ + iMember = 0; \ + { \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(phantomName, 3); \ + FOR_EACH_2(CREATE_AGENT_DATA_TYPE, EXPAND_TWICE(__VA_ARGS__)) \ + {DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(phantomName, 4); } \ + result = ((result == AGENT_DATA_TYPES_OK) && (Create_AGENT_DATA_TYPE_from_Members(destination, #name, sizeof(memberNames) / sizeof(memberNames[0]), memberNames, members) == AGENT_DATA_TYPES_OK)) \ + ? AGENT_DATA_TYPES_OK \ + : AGENT_DATA_TYPES_ERROR; \ + { \ + size_t jMember; \ + for (jMember = 0; jMember < iMember; jMember++) \ + { \ + Destroy_AGENT_DATA_TYPE(&members[jMember]); \ + } \ + } \ + } \ + } \ + return result; \ + } + +#define FIELD_AS_STRING(x,y) memberNames[iMember++] = #y; + +#define REFLECTED_LIST_HEAD(name) \ + static const REFLECTED_DATA_FROM_DATAPROVIDER ALL_REFLECTED(name) = { &C2(REFLECTED_, C1(DEC(__COUNTER__))) }; +#define REFLECTED_STRUCT(name) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_STRUCT_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {0}, {TOSTRING(name)}, {0}, {0}, {0}, {0}} }; +#define REFLECTED_FIELD(XstructName, XfieldType, XfieldName) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_FIELD_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {TOSTRING(XfieldName), TOSTRING(XfieldType), TOSTRING(XstructName)}, {0}, {0}, {0} } }; +#define REFLECTED_MODEL(name) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_MODEL_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {TOSTRING(name)} } }; +#define REFLECTED_PROPERTY(type, name, modelName) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_PROPERTY_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {TOSTRING(name), TOSTRING(type), Create_AGENT_DATA_TYPE_From_Ptr_##modelName##name, offsetof(modelName, name), sizeof(type), TOSTRING(modelName)}, {0}, {0} } }; +#define REFLECTED_REPORTED_PROPERTY(type, name, modelName) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_REPORTED_PROPERTY_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {TOSTRING(name), TOSTRING(type), Create_AGENT_DATA_TYPE_From_Ptr_##modelName##name, offsetof(modelName, name), sizeof(type), TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0} } }; + + +#define REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE(type, name, modelName, COUNTER, onDesiredPropertyChange) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(COUNTER))) = { REFLECTION_DESIRED_PROPERTY_TYPE, &C2(REFLECTED_, C1(DEC(COUNTER))), { {0}, {onDesiredPropertyChange, DesiredPropertyInitialize_##modelName##name, DesiredPropertyDeinitialize_##modelName##name, TOSTRING(name), TOSTRING(type), (int(*)(const AGENT_DATA_TYPE*, void*))FromAGENT_DATA_TYPE_##type, offsetof(modelName, name), sizeof(type), TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0}, {0}} }; + +#define REFLECTED_DESIRED_PROPERTY(type, name, modelName, ...) \ + IF(COUNT_ARG(__VA_ARGS__), \ + MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,__COUNTER__, __VA_ARGS__), \ + MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,DEC(__COUNTER__), NULL) \ + ) \ + +#define REFLECTED_ACTION(name, argc, argv, fn, modelName) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_ACTION_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {0}, {TOSTRING(name), argc, argv, fn, TOSTRING(modelName)}, {0}} }; + +#define REFLECTED_METHOD(name, argc, argv, fn, modelName) \ + static const REFLECTED_SOMETHING C2(REFLECTED_, C1(INC(__COUNTER__))) = { REFLECTION_METHOD_TYPE, &C2(REFLECTED_, C1(DEC(DEC(__COUNTER__)))), { {TOSTRING(name), argc, argv, fn, TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0}, {0}, {0}} }; + + +#define REFLECTED_END_OF_LIST \ + static const REFLECTED_SOMETHING C2(REFLECTED_, __COUNTER__) = { REFLECTION_NOTHING, NULL, { {0},{0}, {0}, {0}, {0}, {0}, {0}, {0}} }; + +#define EXPAND_MODEL_PROPERTY(type, name) EXPAND_ARGS(MODEL_PROPERTY, type, name) + +#define EXPAND_MODEL_REPORTED_PROPERTY(type, name) EXPAND_ARGS(MODEL_REPORTED_PROPERTY, type, name) + +#define EXPAND_MODEL_DESIRED_PROPERTY(type, name, ...) EXPAND_ARGS(MODEL_DESIRED_PROPERTY, type, name, __VA_ARGS__) + +#define EXPAND_MODEL_ACTION(...) EXPAND_ARGS(MODEL_ACTION, __VA_ARGS__) + +#define EXPAND_MODEL_METHOD(...) EXPAND_ARGS(MODEL_METHOD, __VA_ARGS__) + +#define BUILD_MODEL_STRUCT(elem) INSERT_FIELD_FOR_##elem + +#define CREATE_MODEL_ENTITY(modelName, callType, ...) EXPAND_ARGS(CREATE_##callType(modelName, __VA_ARGS__)) +#define CREATE_SOMETHING(modelName, ...) EXPAND_ARGS(CREATE_MODEL_ENTITY(modelName, __VA_ARGS__)) +#define CREATE_ELEMENT(modelName, elem) EXPAND_ARGS(CREATE_SOMETHING(modelName, EXPAND_ARGS(EXPAND_##elem))) + +#define CREATE_MODEL_ELEMENT(modelName, elem) EXPAND_ARGS(CREATE_ELEMENT(modelName, elem)) + + +#define CREATE_MODEL_ENTITY_GLOBAL_INITIALIZATION(modelName, callType, ...) EXPAND_ARGS(CREATE_GLOBAL_INITIALIZE_##callType(modelName, __VA_ARGS__)) +#define CREATE_SOMETHING_GLOBAL_INITIALIZATION(modelName, ...) EXPAND_ARGS(CREATE_MODEL_ENTITY_GLOBAL_INITIALIZATION(modelName, __VA_ARGS__)) +#define CREATE_ELEMENT_GLOBAL_INITIALIZATION(modelName, elem) EXPAND_ARGS(CREATE_SOMETHING_GLOBAL_INITIALIZATION(modelName, EXPAND_ARGS(EXPAND_##elem))) +#define CREATE_MODEL_ELEMENT_GLOBAL_INITIALIZE(modelName, elem) EXPAND_ARGS(CREATE_ELEMENT_GLOBAL_INITIALIZATION(modelName, elem)) + +#define CREATE_MODEL_ENTITY_GLOBAL_DEINITIALIZATION(modelName, callType, ...) EXPAND_ARGS(CREATE_GLOBAL_DEINITIALIZE_##callType(modelName, __VA_ARGS__)) +#define CREATE_SOMETHING_GLOBAL_DEINITIALIZATION(modelName, ...) EXPAND_ARGS(CREATE_MODEL_ENTITY_GLOBAL_DEINITIALIZATION(modelName, __VA_ARGS__)) +#define CREATE_ELEMENT_GLOBAL_DEINITIALIZATION(modelName, elem) EXPAND_ARGS(CREATE_SOMETHING_GLOBAL_DEINITIALIZATION(modelName, EXPAND_ARGS(EXPAND_##elem))) +#define CREATE_MODEL_ELEMENT_GLOBAL_DEINITIALIZE(modelName, elem) EXPAND_ARGS(CREATE_ELEMENT_GLOBAL_DEINITIALIZATION(modelName, elem)) + +#define INSERT_FIELD_INTO_STRUCT(x, y) x y; + + +#define INSERT_FIELD_FOR_MODEL_PROPERTY(type, name) INSERT_FIELD_INTO_STRUCT(type, name) +#define CREATE_GLOBAL_INITIALIZE_MODEL_PROPERTY(modelName, type, name) /*do nothing, this is written by user*/ +#define CREATE_GLOBAL_DEINITIALIZE_MODEL_PROPERTY(modelName, type, name) /*do nothing, this is user's stuff*/ + +/*REPORTED_PROPERTY is not different than regular WITH_DATA*/ +#define INSERT_FIELD_FOR_MODEL_REPORTED_PROPERTY(type, name) INSERT_FIELD_INTO_STRUCT(type, name) +#define CREATE_GLOBAL_INITIALIZE_MODEL_REPORTED_PROPERTY(modelName, type,name) GlobalInitialize_##type((char*)destination+offsetof(modelName, name)); +#define CREATE_GLOBAL_DEINITIALIZE_MODEL_REPORTED_PROPERTY(modelName, type,name) GlobalDeinitialize_##type((char*)destination+offsetof(modelName, name)); + +/*DESIRED_PROPERTY is not different than regular WITH_DATA*/ +#define INSERT_FIELD_FOR_MODEL_DESIRED_PROPERTY(type, name, ...) INSERT_FIELD_INTO_STRUCT(type, name) +#define CREATE_GLOBAL_INITIALIZE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) /*do nothing*/ +#define CREATE_GLOBAL_DEINITIALIZE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) /*do nothing*/ + +#define INSERT_FIELD_FOR_MODEL_ACTION(name, ...) /* action isn't a part of the model struct */ +#define INSERT_FIELD_FOR_MODEL_METHOD(name, ...) /* method isn't a part of the model struct */ + +#define CREATE_GLOBAL_INITIALIZE_MODEL_ACTION(...) /*do nothing*/ +#define CREATE_GLOBAL_DEINITIALIZE_MODEL_ACTION(...) /*do nothing*/ + +#define CREATE_GLOBAL_INITIALIZE_MODEL_METHOD(...) /*do nothing*/ +#define CREATE_GLOBAL_DEINITIALIZE_MODEL_METHOD(...) /*do nothing*/ + +#define CREATE_MODEL_PROPERTY(modelName, type, name) \ + IMPL_PROPERTY(type, name, modelName) + +#define CREATE_MODEL_REPORTED_PROPERTY(modelName, type, name) \ + IMPL_REPORTED_PROPERTY(type, name, modelName) + +#define CREATE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) \ + IMPL_DESIRED_PROPERTY(type, name, modelName, __VA_ARGS__) + +#define IMPL_PROPERTY(propertyType, propertyName, modelName) \ + static int Create_AGENT_DATA_TYPE_From_Ptr_##modelName##propertyName(void* param, AGENT_DATA_TYPE* dest) \ + { \ + return C1(ToAGENT_DATA_TYPE_##propertyType)(dest, *(propertyType*)param); \ + } \ + REFLECTED_PROPERTY(propertyType, propertyName, modelName) + +#define IMPL_REPORTED_PROPERTY(propertyType, propertyName, modelName) \ + static int Create_AGENT_DATA_TYPE_From_Ptr_##modelName##propertyName(void* param, AGENT_DATA_TYPE* dest) \ + { \ + return C1(ToAGENT_DATA_TYPE_##propertyType)(dest, *(propertyType*)param); \ + } \ + REFLECTED_REPORTED_PROPERTY(propertyType, propertyName, modelName) + +#define IMPL_DESIRED_PROPERTY(propertyType, propertyName, modelName, ...) \ + static void DesiredPropertyInitialize_##modelName##propertyName(void* destination) \ + { \ + GlobalInitialize_##propertyType(destination); \ + } \ + static void DesiredPropertyDeinitialize_##modelName##propertyName(void* destination) \ + { \ + GlobalDeinitialize_##propertyType(destination); \ + } \ + REFLECTED_DESIRED_PROPERTY(propertyType, propertyName, modelName, __VA_ARGS__) \ + +#define CREATE_MODEL_ACTION(modelName, actionName, ...) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(modelName##actionName, 1); \ + EXECUTE_COMMAND_RESULT actionName (modelName* device FOR_EACH_2(DEFINE_FUNCTION_PARAMETER, __VA_ARGS__)); \ + static EXECUTE_COMMAND_RESULT C2(actionName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); \ + /*for macro purposes, this array always has at least 1 element*/ \ + /*Codes_SRS_SERIALIZER_99_043:[ It is valid for a method not to have any parameters.]*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 1); \ + static const WRAPPER_ARGUMENT C2(actionName, WRAPPERARGUMENTS)[DIV2(INC(INC(COUNT_ARG(__VA_ARGS__))))] = { FOR_EACH_2_COUNTED(MAKE_WRAPPER_ARGUMENT, __VA_ARGS__) IFCOMMA(INC(INC(COUNT_ARG(__VA_ARGS__)))) {0} }; \ + REFLECTED_ACTION(actionName, DIV2(COUNT_ARG(__VA_ARGS__)), C2(actionName, WRAPPERARGUMENTS), C2(actionName, WRAPPER), modelName) \ + /*Codes_SRS_SERIALIZER_99_040:[ In addition to declaring the function, DECLARE_IOT_METHOD shall provide a definition for a wrapper that takes as parameters a size_t parameterCount and const AGENT_DATA_TYPE*.] */ \ + /*Codes_SRS_SERIALIZER_99_041:[ This wrapper shall convert all the arguments to predefined types and then call the function written by the data provider developer.]*/ \ + static EXECUTE_COMMAND_RESULT C2(actionName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values) \ + { \ + EXECUTE_COMMAND_RESULT result; \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 2); \ + /*Codes_SRS_SERIALIZER_99_045:[ If the number of passed parameters doesn't match the number of declared parameters, wrapper execution shall fail and return DATA_PROVIDER_INVALID_ARG;]*/ \ + if(ParameterCount != DIV2(COUNT_ARG(__VA_ARGS__))) \ + { \ + result = EXECUTE_COMMAND_ERROR; \ + } \ + else \ + { \ + /*the below line takes care of initialized but not referenced parameter warning*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 3); \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), size_t iParameter = 0;, ) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 4); \ + /*the below line takes care of an unused parameter when values is really never questioned*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 5); \ + FOR_EACH_2(DEFINE_LOCAL_PARAMETER, __VA_ARGS__) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 6); \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), , (void)values;) \ + { \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 7); \ + } \ + FOR_EACH_2_KEEP_1(START_BUILD_LOCAL_PARAMETER, EXECUTE_COMMAND_ERROR, __VA_ARGS__) \ + { \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 8); \ + } \ + result = actionName((modelName*)device FOR_EACH_2(PUSH_LOCAL_PARAMETER, __VA_ARGS__)); \ + FOR_EACH_2_REVERSE(END_BUILD_LOCAL_PARAMETER, __VA_ARGS__) \ + } \ + return result; \ + } + +#define CREATE_MODEL_METHOD(modelName, methodName, ...) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(modelName##methodName, 1); \ + METHODRETURN_HANDLE methodName (modelName* device FOR_EACH_2(DEFINE_FUNCTION_PARAMETER, __VA_ARGS__)); \ + static METHODRETURN_HANDLE C2(methodName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); \ + /*for macro purposes, this array always has at least 1 element*/ \ + /*Codes_SRS_SERIALIZER_H_02_030: [ It is valid for a method function not to have any parameters. ]*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 1); \ + static const WRAPPER_ARGUMENT C2(methodName, WRAPPERARGUMENTS)[DIV2(INC(INC(COUNT_ARG(__VA_ARGS__))))] = { FOR_EACH_2_COUNTED(MAKE_WRAPPER_ARGUMENT, __VA_ARGS__) IFCOMMA(INC(INC(COUNT_ARG(__VA_ARGS__)))) {0} }; \ + REFLECTED_METHOD(methodName, DIV2(COUNT_ARG(__VA_ARGS__)), C2(methodName, WRAPPERARGUMENTS), C2(methodName, WRAPPER), modelName) \ + /*Codes_SRS_SERIALIZER_H_02_034: [ WITH_METHOD shall result in the declaration of a conversion function with the prototype METHODRETURN_HANDLE nameWRAPPER(size_t ParameterCount, const AGENT_DATA_TYPE* values)' ]*/ \ + /*Codes_SRS_SERIALIZER_H_02_031: [ The function shall convert the input arguments to the types declared in the method parameter list and then call the user-defined method function. ]*/ \ + static METHODRETURN_HANDLE C2(methodName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values) \ + { \ + METHODRETURN_HANDLE result; \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 2); \ + /*Codes_SRS_SERIALIZER_H_02_032: [ If the number of arguments passed to the conversion function does not match the expected count, the function shall return DATAPROVIDER_INVALID_ARG. ]*/ \ + if(ParameterCount != DIV2(COUNT_ARG(__VA_ARGS__))) \ + { \ + LogError("expected parameter count (%lu) does not match the actual parameter count (%lu)", ParameterCount, COUNT_ARG(__VA_ARGS__)); \ + result = NULL; \ + } \ + else \ + { \ + /*the below line takes care of initialized but not referenced parameter warning*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 3); \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), size_t iParameter = 0;, ) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 4); \ + /*the below line takes care of an unused parameter when values is really never questioned*/ \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 5); \ + FOR_EACH_2(DEFINE_LOCAL_PARAMETER, __VA_ARGS__) \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 6); \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), , (void)values;) \ + { \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 7); \ + } \ + FOR_EACH_2_KEEP_1(START_BUILD_LOCAL_PARAMETER, NULL,__VA_ARGS__) \ + { \ + DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 8); \ + } \ + result = methodName((modelName*)device FOR_EACH_2(PUSH_LOCAL_PARAMETER, __VA_ARGS__)); \ + FOR_EACH_2_REVERSE(END_BUILD_LOCAL_PARAMETER, __VA_ARGS__) \ + } \ + return result; \ + } + +#define CREATE_AGENT_DATA_TYPE(type, name) \ + result = (( result==AGENT_DATA_TYPES_OK) && (ToAGENT_DATA_TYPE_##type( &(members[iMember]), value.name) == AGENT_DATA_TYPES_OK))?AGENT_DATA_TYPES_OK:AGENT_DATA_TYPES_ERROR; \ + iMember+= ((result==AGENT_DATA_TYPES_OK)?1:0); + +#define BUILD_DESTINATION_FIELD(type, name) \ + if(result == AGENT_DATA_TYPES_OK) \ + { \ + size_t i; \ + bool wasFieldConverted = false; \ + for (i = 0; i < source->value.edmComplexType.nMembers; i++) \ + { \ + /*the name of the field of the complex type must match the name of the field of the structure (parameter name here)*/ \ + if (strcmp(source->value.edmComplexType.fields[i].fieldName, TOSTRING(name)) == 0) \ + { /*Codes_SRS_SERIALIZER_99_017:[ These types can either be one of the types mentioned in WITH_DATA or it can be a type introduced by a previous DECLARE_STRUCT.]*/ \ + wasFieldConverted = (C2(FromAGENT_DATA_TYPE_, type)(source->value.edmComplexType.fields[i].value, &(destination->name)) == AGENT_DATA_TYPES_OK); \ + break; \ + } \ + } \ + result = (wasFieldConverted == true)? AGENT_DATA_TYPES_OK: AGENT_DATA_TYPES_INVALID_ARG; \ + } \ + else \ + { \ + /*fallthrough*/ \ + } + +#define UNBUILD_DESTINATION_FIELD(value, type, name) \ + C2(destroyLocalParameter, type)(&(value->name)); + + +#define ADDRESS_MACRO(x) ,&x + +#define KEEP_FIRST_(X, ...) X +#ifdef _MSC_VER +#define KEEP_FIRST(X) KEEP_FIRST_ LPAREN X) +#else +#define KEEP_FIRST(X) KEEP_FIRST_(X) +#endif + +#define PROMOTIONMAP_float double, double +#define PROMOTIONMAP_int8_t int, int +#define PROMOTIONMAP_uint8_t int, int +#define PROMOTIONMAP_int16_t int, int +#define PROMOTIONMAP__Bool int, int +#define PROMOTIONMAP_bool int, int + +#define CASTMAP_float (float), (float) +#define CASTMAP_int8_t (int8_t), (int8_t) +#define CASTMAP_uint8_t (uint8_t), (uint8_t) +#define CASTMAP_int16_t (int16_t), (int16_t) +#define CASTMAP__Bool 0!=, 0!= +#define CASTMAP_bool 0!=, 0!= + +#define EMPTY_TOKEN + +#define ANOTHERIF(x) C2(ANOTHERIF,x) +#define ANOTHERIF0(a,b) a +#define ANOTHERIF1(a,b) b +#define ANOTHERIF2(a,b) b +#define ANOTHERIF3(a,b) b +#define ANOTHERIF4(a,b) b +#define ANOTHERIF5(a,b) b +#define ANOTHERIF6(a,b) b +#define ANOTHERIF7(a,b) b +#define ANOTHERIF8(a,b) b +#define ANOTHERIF9(a,b) b +#define ANOTHERIF10(a,b) b +#define ANOTHERIF11(a,b) b +#define ANOTHERIF12(a,b) b + +#define MAP_PROMOTED_TYPE(X) ANOTHERIF(DEC(COUNT_ARG(PROMOTIONMAP_##X))) (X, KEEP_FIRST(PROMOTIONMAP_##X)) +#define MAP_CAST_TYPE(X) ANOTHERIF(DEC(COUNT_ARG(CASTMAP_##X))) (EMPTY_TOKEN, KEEP_FIRST(CASTMAP_##X) ) + +#define IFCOMMA(N) C2(IFCOMMA_, N) +#define IFCOMMA_0 +#define IFCOMMA_2 +#define IFCOMMA_4 , +#define IFCOMMA_6 , +#define IFCOMMA_8 , +#define IFCOMMA_10 , +#define IFCOMMA_12 , +#define IFCOMMA_14 , +#define IFCOMMA_16 , +#define IFCOMMA_18 , +#define IFCOMMA_20 , +#define IFCOMMA_22 , +#define IFCOMMA_24 , +#define IFCOMMA_26 , +#define IFCOMMA_28 , +#define IFCOMMA_30 , +#define IFCOMMA_32 , +#define IFCOMMA_34 , +#define IFCOMMA_36 , +#define IFCOMMA_38 , +#define IFCOMMA_40 , +#define IFCOMMA_42 , +#define IFCOMMA_44 , +#define IFCOMMA_46 , +#define IFCOMMA_48 , +#define IFCOMMA_50 , +#define IFCOMMA_52 , +#define IFCOMMA_54 , +#define IFCOMMA_56 , +#define IFCOMMA_58 , +#define IFCOMMA_60 , +#define IFCOMMA_62 , +#define IFCOMMA_64 , +#define IFCOMMA_66 , +#define IFCOMMA_68 , +#define IFCOMMA_70 , +#define IFCOMMA_72 , +#define IFCOMMA_74 , +#define IFCOMMA_76 , +#define IFCOMMA_78 , +#define IFCOMMA_80 , +#define IFCOMMA_82 , +#define IFCOMMA_84 , +#define IFCOMMA_86 , +#define IFCOMMA_88 , +#define IFCOMMA_90 , +#define IFCOMMA_92 , +#define IFCOMMA_94 , +#define IFCOMMA_96 , +#define IFCOMMA_98 , +#define IFCOMMA_100 , +#define IFCOMMA_102 , +#define IFCOMMA_104 , +#define IFCOMMA_106 , +#define IFCOMMA_108 , +#define IFCOMMA_110 , +#define IFCOMMA_112 , +#define IFCOMMA_114 , +#define IFCOMMA_116 , +#define IFCOMMA_118 , +#define IFCOMMA_120 , +#define IFCOMMA_122 , +#define IFCOMMA_124 , +#define IFCOMMA_126 , +#define IFCOMMA_128 , + +#define DEFINE_LOCAL_PARAMETER(type, name) type C2(name,_local); GlobalInitialize_##type(& C2(name, _local)); + +#define START_BUILD_LOCAL_PARAMETER(errorWhenItFails, type, name) \ + if (C2(FromAGENT_DATA_TYPE_, type)(&values[iParameter], &C2(name, _local)) != AGENT_DATA_TYPES_OK) \ + { \ + /*Codes_SRS_SERIALIZER_99_046:[ If the types of the parameters do not match the declared types, DATAPROVIDER_INVALID_ARG shall be returned.]*/ \ + result = errorWhenItFails; \ + }\ + else \ + { \ + iParameter++; + +#define END_BUILD_LOCAL_PARAMETER(type, name) \ + (void)C2(destroyLocalParameter, type)(&C2(name, _local)); \ + } + +/*The following constructs have been devised to work around the precompiler bug of Visual Studio 2005, version 14.00.50727.42*/ +/* The bug is explained in https://connect.microsoft.com/VisualStudio/feedback/details/278752/comma-missing-when-using-va-args */ +/*A short description is: preprocessor is mysteriously eating commas ','. +In order to feed the appetite of the preprocessor, several constructs have +been devised that can sustain a missing ',' while still compiling and while still doing nothing +and while hopefully being eliminated from the code based on "doesn't do anything" so no code size penalty +*/ + +/*the reason why all these constructs work is: +if two strings separated by a comma will lose the comma (myteriously) then they will become just one string: +"a", "b" ------Preprocessor------> "a" "b" -----Compiler----> "ab" +*/ + +#define LOTS_OF_COMMA_TO_BE_EATEN /*there were witnesses where as many as THREE commas have been eaten!*/ \ +"0" "1", "2", "3", "4", "5", "6", "7", "8", "9" +#define DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(name, instance) static const char* eatThese_COMMA_##name##instance[] = {LOTS_OF_COMMA_TO_BE_EATEN} + +#define PUSH_LOCAL_PARAMETER(type, name) , C2(name, _local) +#define DEFINE_FUNCTION_PARAMETER(type, name) , type name +#define MAKE_WRAPPER_ARGUMENT(N, type, name) {TOSTRING(type), TOSTRING(name)} IFCOMMA(N) + +/*Codes_SRS_SERIALIZER_99_019:[ Create_AGENT_DATA_TYPE_from_DOUBLE]*/ +/*Codes_SRS_SERIALIZER_99_004:[ The propertyType can be any of the following data types: double]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, double)(AGENT_DATA_TYPE* dest, double source) +{ + return Create_AGENT_DATA_TYPE_from_DOUBLE(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, double)(const AGENT_DATA_TYPE* agentData, double* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_DOUBLE_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmDouble.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, double)(void* dest) +{ + *(double*)dest = 0.0; +} + +static void C2(GlobalDeinitialize_, double)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_021:[ Create_AGENT_DATA_TYPE_from_FLOAT]*/ +/*Codes_SRS_SERIALIZER_99_006:[ float]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, float)(AGENT_DATA_TYPE* dest, float source) +{ + return Create_AGENT_DATA_TYPE_from_FLOAT(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, float)(const AGENT_DATA_TYPE* agentData, float* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_SINGLE_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmSingle.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, float)(void* dest) +{ + *(float*)dest = 0.0f; +} + +static void C2(GlobalDeinitialize_, float)(void* dest) +{ + (void)(dest); +} + + +/*Codes_SRS_SERIALIZER_99_020:[ Create_AGENT_DATA_TYPE_from_SINT32]*/ +/*Codes_SRS_SERIALIZER_99_005:[ int], */ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, int)(AGENT_DATA_TYPE* dest, int source) +{ + return Create_AGENT_DATA_TYPE_from_SINT32(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, int)(const AGENT_DATA_TYPE* agentData, int* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_INT32_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmInt32.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, int)(void* dest) +{ + *(int*)dest = 0; +} + +static void C2(GlobalDeinitialize_, int)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_022:[ Create_AGENT_DATA_TYPE_from_SINT64]*/ +/*Codes_SRS_SERIALIZER_99_007:[ long]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, long)(AGENT_DATA_TYPE* dest, long source) +{ + return Create_AGENT_DATA_TYPE_from_SINT64(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, long)(const AGENT_DATA_TYPE* agentData, long* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_INT64_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = (long)agentData->value.edmInt64.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, long)(void* dest) +{ + *(long*)dest = 0; +} + +static void C2(GlobalDeinitialize_, long)(void* dest) +{ + (void)(dest); +} + + +/*Codes_SRS_SERIALIZER_99_023:[ Create_AGENT_DATA_TYPE_from_SINT8]*/ +/*Codes_SRS_SERIALIZER_99_008:[ int8_t]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, int8_t)(AGENT_DATA_TYPE* dest, int8_t source) +{ + return Create_AGENT_DATA_TYPE_from_SINT8(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, int8_t)(const AGENT_DATA_TYPE* agentData, int8_t* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_SBYTE_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmSbyte.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, int8_t)(void* dest) +{ + *(int8_t*)dest = 0; +} + +static void C2(GlobalDeinitialize_, int8_t)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_024:[ Create_AGENT_DATA_TYPE_from_UINT8]*/ +/*Codes_SRS_SERIALIZER_99_009:[ uint8_t]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, uint8_t)(AGENT_DATA_TYPE* dest, uint8_t source) +{ + return Create_AGENT_DATA_TYPE_from_UINT8(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, uint8_t)(const AGENT_DATA_TYPE* agentData, uint8_t* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_BYTE_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmByte.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, uint8_t)(void* dest) +{ + *(uint8_t*)dest = 0; +} + +static void C2(GlobalDeinitialize_, uint8_t)(void* dest) +{ + (void)(dest); +} + + +/*Codes_SRS_SERIALIZER_99_025:[ Create_AGENT_DATA_TYPE_from_SINT16]*/ +/*Codes_SRS_SERIALIZER_99_010:[ int16_t]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, int16_t)(AGENT_DATA_TYPE* dest, int16_t source) +{ + return Create_AGENT_DATA_TYPE_from_SINT16(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, int16_t)(const AGENT_DATA_TYPE* agentData, int16_t* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_INT16_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmInt16.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, int16_t)(void* dest) +{ + *(int16_t*)dest = 0; +} + +static void C2(GlobalDeinitialize_, int16_t)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_026:[ Create_AGENT_DATA_TYPE_from_SINT32]*/ +/*Codes_SRS_SERIALIZER_99_011:[ int32_t]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, int32_t)(AGENT_DATA_TYPE* dest, int32_t source) +{ + return Create_AGENT_DATA_TYPE_from_SINT32(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, int32_t)(const AGENT_DATA_TYPE* agentData, int32_t* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_INT32_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmInt32.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, int32_t)(void* dest) +{ + *(int32_t*)dest = 0; +} + +static void C2(GlobalDeinitialize_, int32_t)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_027:[ Create_AGENT_DATA_TYPE_from_SINT64]*/ +/*Codes_SRS_SERIALIZER_99_012:[ int64_t]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, int64_t)(AGENT_DATA_TYPE* dest, int64_t source) +{ + return Create_AGENT_DATA_TYPE_from_SINT64(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, int64_t)(const AGENT_DATA_TYPE* agentData, int64_t* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_INT64_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmInt64.value; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, int64_t)(void* dest) +{ + *(int64_t*)dest = 0; +} + +static void C2(GlobalDeinitialize_, int64_t)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_013:[ bool]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, bool)(AGENT_DATA_TYPE* dest, bool source) +{ + return Create_EDM_BOOLEAN_from_int(dest, source == true); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, bool)(const AGENT_DATA_TYPE* agentData, bool* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_BOOLEAN_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = (agentData->value.edmBoolean.value == EDM_TRUE) ? true : false; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, bool)(void* dest) +{ + *(bool*)dest = false; +} + +static void C2(GlobalDeinitialize_, bool)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_014:[ ascii_char_ptr]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, ascii_char_ptr)(AGENT_DATA_TYPE* dest, ascii_char_ptr source) +{ + return Create_AGENT_DATA_TYPE_from_charz(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, ascii_char_ptr)(const AGENT_DATA_TYPE* agentData, ascii_char_ptr* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_STRING_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + if (*dest != NULL) + { + free(*dest); + *dest = NULL; + } + + if (mallocAndStrcpy_s(dest, agentData->value.edmString.chars) != 0) + { + LogError("failure in mallocAndStrcpy_s"); + result = AGENT_DATA_TYPES_ERROR; + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + } + return result; +} + +static void C2(GlobalInitialize_, ascii_char_ptr)(void* dest) +{ + *(ascii_char_ptr*)dest = NULL; +} + +static void C2(GlobalDeinitialize_, ascii_char_ptr)(void* dest) +{ + if (*(ascii_char_ptr*)dest != NULL) + { + free(*(ascii_char_ptr*)dest); + } +} + +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, ascii_char_ptr_no_quotes)(AGENT_DATA_TYPE* dest, ascii_char_ptr_no_quotes source) +{ + return Create_AGENT_DATA_TYPE_from_charz_no_quotes(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, ascii_char_ptr_no_quotes)(const AGENT_DATA_TYPE* agentData, ascii_char_ptr_no_quotes* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_STRING_NO_QUOTES_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + if (*dest != NULL) + { + free(*dest); + *dest = NULL; + } + + if (mallocAndStrcpy_s(dest, agentData->value.edmStringNoQuotes.chars) != 0) + { + LogError("failure in mallocAndStrcpy_s"); + result = AGENT_DATA_TYPES_ERROR; + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + return result; +} + +static void C2(GlobalInitialize_, ascii_char_ptr_no_quotes)(void* dest) +{ + *(ascii_char_ptr_no_quotes*)dest = NULL; +} + +static void C2(GlobalDeinitialize_, ascii_char_ptr_no_quotes)(void* dest) +{ + if (*(ascii_char_ptr_no_quotes*)dest != NULL) + { + free(*(ascii_char_ptr_no_quotes*)dest); + } +} + +/*Codes_SRS_SERIALIZER_99_051:[ EDM_DATE_TIME_OFFSET*/ +/*Codes_SRS_SERIALIZER_99_053:[Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, EDM_DATE_TIME_OFFSET)(AGENT_DATA_TYPE* dest, EDM_DATE_TIME_OFFSET source) +{ + return Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET(dest, source); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, EDM_DATE_TIME_OFFSET)(const AGENT_DATA_TYPE* agentData, EDM_DATE_TIME_OFFSET* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_DATE_TIME_OFFSET_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + *dest = agentData->value.edmDateTimeOffset; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, EDM_DATE_TIME_OFFSET)(void* dest) +{ + memset(dest, 0, sizeof(EDM_DATE_TIME_OFFSET)); +} + +static void C2(GlobalDeinitialize_, EDM_DATE_TIME_OFFSET)(void* dest) +{ + (void)(dest); +} + +/*Codes_SRS_SERIALIZER_99_072:[ EDM_GUID]*/ +/*Codes_SRS_SERIALIZER_99_073:[ Create_AGENT_DATA_TYPE_from_EDM_GUID]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, EDM_GUID)(AGENT_DATA_TYPE* dest, EDM_GUID guid) +{ + return Create_AGENT_DATA_TYPE_from_EDM_GUID(dest, guid); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, EDM_GUID)(const AGENT_DATA_TYPE* agentData, EDM_GUID* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_GUID_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + (void)memcpy(dest->GUID, agentData->value.edmGuid.GUID, 16); + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static void C2(GlobalInitialize_, EDM_GUID)(void* dest) +{ + memset(dest, 0, sizeof(EDM_GUID)); +} + +static void C2(GlobalDeinitialize_, EDM_GUID)(void* dest) +{ + (void)(dest); +} + + +/*Codes_SRS_SERIALIZER_99_074:[ EDM_BINARY]*/ +/*Codes_SRS_SERIALIZER_99_075:[ Create_AGENT_DATA_TYPE_from_EDM_BINARY]*/ +static AGENT_DATA_TYPES_RESULT C2(ToAGENT_DATA_TYPE_, EDM_BINARY)(AGENT_DATA_TYPE* dest, EDM_BINARY edmBinary) +{ + return Create_AGENT_DATA_TYPE_from_EDM_BINARY(dest, edmBinary); +} + +static AGENT_DATA_TYPES_RESULT C2(FromAGENT_DATA_TYPE_, EDM_BINARY)(const AGENT_DATA_TYPE* agentData, EDM_BINARY* dest) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData->type != EDM_BINARY_TYPE) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + if ((dest->data = (unsigned char *)malloc(agentData->value.edmBinary.size)) == NULL) /*cast because this get included in a C++ file.*/ + { + result = AGENT_DATA_TYPES_ERROR; + } + else + { + (void)memcpy(dest->data, agentData->value.edmBinary.data, agentData->value.edmBinary.size); + dest->size = agentData->value.edmBinary.size; + result = AGENT_DATA_TYPES_OK; + } + } + return result; +} + +static void C2(GlobalInitialize_, EDM_BINARY)(void* dest) +{ + ((EDM_BINARY*)dest)->data = NULL; + ((EDM_BINARY*)dest)->size = 0; +} + +static void C2(GlobalDeinitialize_, EDM_BINARY)(void* dest) +{ + if ((((EDM_BINARY*)dest)->data) != NULL) + { + free(((EDM_BINARY*)dest)->data); + } +} + +static void C2(destroyLocalParameter, EDM_BINARY)(EDM_BINARY* value) +{ + if (value != NULL) + { + free(value->data); + value->data = NULL; + value->size = 0; + } +} + +static void C2(destroyLocalParameter, EDM_BOOLEAN)(EDM_BOOLEAN* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_BYTE)(EDM_BYTE* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_DATE)(EDM_DATE* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_DATE_TIME_OFFSET)(EDM_DATE_TIME_OFFSET* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_DECIMAL)(EDM_DECIMAL* value) +{ + if (value != NULL) + { + STRING_delete(value->value); + value->value = NULL; + } +} + +static void C2(destroyLocalParameter, EDM_DOUBLE)(EDM_DOUBLE* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_DURATION)(EDM_DURATION* value) +{ + if (value != NULL) + { + free(value->digits); + value->digits = NULL; + value->nDigits = 0; + } +} + +static void C2(destroyLocalParameter, EDM_GUID)(EDM_GUID* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_INT16)(EDM_INT16* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_INT32)(EDM_INT32* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_INT64)(EDM_INT64* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_SBYTE)(EDM_SBYTE* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_SINGLE)(EDM_SINGLE* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, EDM_STRING)(EDM_STRING* value) +{ + (void)value; +} + + +static void C2(destroyLocalParameter, EDM_TIME_OF_DAY)(EDM_TIME_OF_DAY* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, int)(int* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, float)(float* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, double)(double* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, long)(long* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, int8_t)(int8_t* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, uint8_t)(uint8_t* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, int16_t)(int16_t* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, int32_t)(int32_t* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, int64_t)(int64_t* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, bool)(bool* value) +{ + (void)value; +} + +static void C2(destroyLocalParameter, ascii_char_ptr)(ascii_char_ptr* value) +{ + if (value != NULL) + { + free(*value); + } + +} + +static void C2(destroyLocalParameter, ascii_char_ptr_no_quotes)(ascii_char_ptr_no_quotes* value) +{ + if (value != NULL) + { + free(*value); + } +} + +#ifdef __cplusplus + } +#endif + +#endif /*SERIALIZER_H*/ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/inc/serializer_devicetwin.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,472 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#ifndef SERIALIZER_DEVICE_TWIN_H +#define SERIALIZER_DEVICE_TWIN_H + +#include "serializer.h" + +#include "iothub_client.h" +#include "iothub_client_ll.h" +#include "parson.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/vector.h" +#include "methodreturn.h" + +static void serializer_ingest(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) +{ + /*by convention, userContextCallback is a pointer to a model instance created with CodeFirst_CreateDevice*/ + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_001: [ serializer_ingest shall clone the payload into a null terminated string. ]*/ + char* copyOfPayload = (char*)malloc(size + 1); + if (copyOfPayload == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("unable to malloc\n"); + } + else + { + (void)memcpy(copyOfPayload, payLoad, size); + copyOfPayload[size] = '\0'; + + bool parseDesiredNode = (update_state == DEVICE_TWIN_UPDATE_COMPLETE); + + if (CodeFirst_IngestDesiredProperties(userContextCallback, copyOfPayload, parseDesiredNode) != CODEFIRST_OK) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure ingesting desired properties\n"); + } + else + { + /*all is fine*/ + } + + free(copyOfPayload); + } +} + +/*both LL and convenience layer can be served by the same callback*/ +static int deviceMethodCallback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) +{ + int result; + /*Codes_SRS_SERIALIZERDEVICETWIN_02_021: [ deviceMethodCallback shall transform payload and size into a null terminated string. ]*/ + char* payloadZeroTerminated = (char*)malloc(size + 1); + if (payloadZeroTerminated == NULL) + { + LogError("failure in malloc"); + /*Codes_SRS_SERIALIZERDEVICETWIN_02_026: [ If any failure occurs in the above operations, then deviceMethodCallback shall fail, return 500, set *response to NULL and '*resp_size` to 0. ]*/ + *response = NULL; + *resp_size = 0; + result = 500; + } + else + { + (void)memcpy(payloadZeroTerminated, payload, size); + payloadZeroTerminated[size] = '\0'; + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_022: [ deviceMethodCallback shall call EXECUTE_METHOD passing the userContextCallback, method_name and the null terminated string build before. ]*/ + METHODRETURN_HANDLE mr = EXECUTE_METHOD(userContextCallback, method_name, payloadZeroTerminated); + + if (mr == NULL) + { + LogError("failure in EXECUTE_METHOD"); + /*Codes_SRS_SERIALIZERDEVICETWIN_02_026: [ If any failure occurs in the above operations, then deviceMethodCallback shall fail, return 500, set *response to NULL and '*resp_size` to 0. ]*/ + *response = NULL; + *resp_size = 0; + result = 500; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_023: [ deviceMethodCallback shall get the MethodReturn_Data and shall copy the response JSON value into a new byte array. ]*/ + const METHODRETURN_DATA* data = MethodReturn_GetReturn(mr); + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_025: [ deviceMethodCallback returns the statusCode from the user. ]*/ + result = data->statusCode; + + if (data->jsonValue == NULL) + { + *resp_size = 0; + *response = NULL; + } + else + { + *resp_size = strlen(data->jsonValue); + *response = (unsigned char*)malloc(*resp_size); + if (*response == NULL) + { + LogError("failure in malloc"); + /*Codes_SRS_SERIALIZERDEVICETWIN_02_026: [ If any failure occurs in the above operations, then deviceMethodCallback shall fail, return 500, set *response to NULL and '*resp_size` to 0. ]*/ + *response = NULL; + *resp_size = 0; + result = 500; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_024: [ deviceMethodCallback shall set *response to this new byte array, *resp_size to the size of the array. ]*/ + (void)memcpy(*response, data->jsonValue, *resp_size); + } + } + MethodReturn_Destroy(mr); + } + free(payloadZeroTerminated); + } + return result; +} + +/*an enum that sets the type of the handle used to record IoTHubDeviceTwin_Create was called*/ +#define IOTHUB_CLIENT_HANDLE_TYPE_VALUES \ + IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE, \ + IOTHUB_CLIENT_LL_HANDLE_TYPE + +DEFINE_ENUM(IOTHUB_CLIENT_HANDLE_TYPE, IOTHUB_CLIENT_HANDLE_TYPE_VALUES) + +typedef union IOTHUB_CLIENT_HANDLE_VALUE_TAG +{ + IOTHUB_CLIENT_HANDLE iothubClientHandle; + IOTHUB_CLIENT_LL_HANDLE iothubClientLLHandle; +} IOTHUB_CLIENT_HANDLE_VALUE; + +typedef struct IOTHUB_CLIENT_HANDLE_VARIANT_TAG +{ + IOTHUB_CLIENT_HANDLE_TYPE iothubClientHandleType; + IOTHUB_CLIENT_HANDLE_VALUE iothubClientHandleValue; +} IOTHUB_CLIENT_HANDLE_VARIANT; + +typedef struct SERIALIZER_DEVICETWIN_PROTOHANDLE_TAG /*it is called "PROTOHANDLE" because it is a primitive type of handle*/ +{ + IOTHUB_CLIENT_HANDLE_VARIANT iothubClientHandleVariant; + void* deviceAssigned; +} SERIALIZER_DEVICETWIN_PROTOHANDLE; + +static VECTOR_HANDLE g_allProtoHandles=NULL; /*contains SERIALIZER_DEVICETWIN_PROTOHANDLE*/ + +static int lazilyAddProtohandle(const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle) +{ + int result; + if ((g_allProtoHandles == NULL) && ((g_allProtoHandles = VECTOR_create(sizeof(SERIALIZER_DEVICETWIN_PROTOHANDLE))) == NULL)) + { + LogError("failure in VECTOR_create"); + result = __FAILURE__; + } + else + { + if (VECTOR_push_back(g_allProtoHandles, protoHandle, 1) != 0) + { + LogError("failure in VECTOR_push_back"); + result = __FAILURE__; + + /*leave it as it was*/ + + if (VECTOR_size(g_allProtoHandles) == 0) + { + VECTOR_destroy(g_allProtoHandles); + g_allProtoHandles = NULL; + } + } + else + { + result = 0; + } + } + return result; +} + +static IOTHUB_CLIENT_RESULT Generic_IoTHubClient_SetCallbacks(const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_SERIALIZERDEVICETWIN_02_011: [ IoTHubDeviceTwinCreate_Impl shall set the device twin callback. ]*/ + switch (protoHandle->iothubClientHandleVariant.iothubClientHandleType) + { + case IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE: + { + if ((result = IoTHubClient_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, deviceTwinCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_SetDeviceTwinCallback"); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_027: [ IoTHubDeviceTwinCreate_Impl shall set the device method callback ]*/ + if ((result = IoTHubClient_SetDeviceMethodCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, deviceMethodCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + (void)IoTHubClient_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, NULL, NULL); + LogError("failure in IoTHubClient_SetDeviceMethodCallback"); + } + } + break; + } + case IOTHUB_CLIENT_LL_HANDLE_TYPE: + { + if ((result =IoTHubClient_LL_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, deviceTwinCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_LL_SetDeviceTwinCallback"); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_027: [ IoTHubDeviceTwinCreate_Impl shall set the device method callback ]*/ + if ((result = IoTHubClient_LL_SetDeviceMethodCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, deviceMethodCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + (void)IoTHubClient_LL_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, NULL, NULL); + LogError("failure in IoTHubClient_SetDeviceMethodCallback"); + } + } + break; + } + default: + { + result = IOTHUB_CLIENT_ERROR; + LogError("INTERNAL ERROR"); + } + }/*switch*/ + return result; +} + +static void* IoTHubDeviceTwinCreate_Impl(const char* name, size_t sizeOfName, SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle) +{ + void* result; + /*Codes_SRS_SERIALIZERDEVICETWIN_02_009: [ IoTHubDeviceTwinCreate_Impl shall locate the model and the metadata for name by calling Schema_GetSchemaForModel/Schema_GetMetadata/Schema_GetModelByName. ]*/ + SCHEMA_HANDLE h = Schema_GetSchemaForModel(name); + if (h == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Schema_GetSchemaForModel."); + result = NULL; + } + else + { + void* metadata = Schema_GetMetadata(h); + SCHEMA_MODEL_TYPE_HANDLE modelType = Schema_GetModelByName(h, name); + if (modelType == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Schema_GetModelByName"); + result = NULL; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_010: [ IoTHubDeviceTwinCreate_Impl shall call CodeFirst_CreateDevice. ]*/ + result = CodeFirst_CreateDevice(modelType, (REFLECTED_DATA_FROM_DATAPROVIDER *)metadata, sizeOfName, true); + if (result == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in CodeFirst_CreateDevice"); + /*return as is*/ + } + else + { + protoHandle->deviceAssigned = result; + if (Generic_IoTHubClient_SetCallbacks(protoHandle, serializer_ingest, result) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Generic_IoTHubClient_SetCallbacks"); + CodeFirst_DestroyDevice(result); + result = NULL; + } + else + { + /*lazily add the protohandle to the array of tracking handles*/ + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_012: [ IoTHubDeviceTwinCreate_Impl shall record the pair of (device, IoTHubClient(_LL)). ]*/ + if (lazilyAddProtohandle(protoHandle) != 0) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("unable to add the protohandle to the collection of handles"); + /*unsubscribe*/ + if (Generic_IoTHubClient_SetCallbacks(protoHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + /*just log the error*/ + LogError("failure in Generic_IoTHubClient_SetCallbacks"); + } + CodeFirst_DestroyDevice(result); + result = NULL; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_013: [ If all operations complete successfully then IoTHubDeviceTwinCreate_Impl shall succeeds and return a non-NULL value. ]*/ + /*return as is*/ + } + } + } + } + } + return result; +} + +static bool protoHandleHasDeviceStartAddress(const void* element, const void* value) +{ + const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle = (const SERIALIZER_DEVICETWIN_PROTOHANDLE*)element; + return protoHandle->deviceAssigned == value; +} + +static void IoTHubDeviceTwin_Destroy_Impl(void* model) +{ + /*Codes_SRS_SERIALIZERDEVICETWIN_02_020: [ If model is NULL then IoTHubDeviceTwin_Destroy_Impl shall return. ]*/ + if (model == NULL) + { + LogError("invalid argument void* model=%p", model); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_015: [ IoTHubDeviceTwin_Destroy_Impl shall locate the saved handle belonging to model. ]*/ + SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle = (SERIALIZER_DEVICETWIN_PROTOHANDLE*)VECTOR_find_if(g_allProtoHandles, protoHandleHasDeviceStartAddress, model); + if (protoHandle == NULL) + { + LogError("failure in VECTOR_find_if [not found]"); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_016: [ IoTHubDeviceTwin_Destroy_Impl shall set the devicetwin callback to NULL. ]*/ + switch (protoHandle->iothubClientHandleVariant.iothubClientHandleType) + { + case IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE: + { + if (IoTHubClient_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_SetDeviceTwinCallback"); + } + /*Codes_SRS_SERIALIZERDEVICETWIN_02_028: [ IoTHubDeviceTwin_Destroy_Impl shall set the method callback to NULL. ]*/ + if (IoTHubClient_SetDeviceMethodCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_SetDeviceMethodCallback"); + } + break; + } + case IOTHUB_CLIENT_LL_HANDLE_TYPE: + { + if (IoTHubClient_LL_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_LL_SetDeviceTwinCallback"); + } + /*Codes_SRS_SERIALIZERDEVICETWIN_02_028: [ IoTHubDeviceTwin_Destroy_Impl shall set the method callback to NULL. ]*/ + if (IoTHubClient_LL_SetDeviceMethodCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_LL_SetDeviceMethodCallback"); + } + break; + } + default: + { + LogError("INTERNAL ERROR"); + } + }/*switch*/ + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_017: [ IoTHubDeviceTwin_Destroy_Impl shall call CodeFirst_DestroyDevice. ]*/ + CodeFirst_DestroyDevice(protoHandle->deviceAssigned); + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_018: [ IoTHubDeviceTwin_Destroy_Impl shall remove the IoTHubClient_Handle and the device handle from the recorded set. ]*/ + VECTOR_erase(g_allProtoHandles, protoHandle, 1); + } + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_019: [ If the recorded set of IoTHubClient handles is zero size, then the set shall be destroyed. ]*/ + if (VECTOR_size(g_allProtoHandles) == 0) /*lazy init means more work @ destroy time*/ + { + VECTOR_destroy(g_allProtoHandles); + g_allProtoHandles = NULL; + } + } +} + +/*the below function sends the reported state of a model previously created by IoTHubDeviceTwin_Create*/ +/*this function serves both the _LL and the convenience layer because of protohandles*/ +static IOTHUB_CLIENT_RESULT IoTHubDeviceTwin_SendReportedState_Impl(void* model, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK deviceTwinCallback, void* context) +{ + unsigned char*buffer; + size_t bufferSize; + + IOTHUB_CLIENT_RESULT result; + + if (SERIALIZE_REPORTED_PROPERTIES_FROM_POINTERS(&buffer, &bufferSize, model) != CODEFIRST_OK) + { + LogError("Failed serializing reported state"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle = (SERIALIZER_DEVICETWIN_PROTOHANDLE*)VECTOR_find_if(g_allProtoHandles, protoHandleHasDeviceStartAddress, model); + if (protoHandle == NULL) + { + LogError("failure in VECTOR_find_if [not found]"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + switch (protoHandle->iothubClientHandleVariant.iothubClientHandleType) + { + case IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE: + { + if (IoTHubClient_SendReportedState(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, buffer, bufferSize, deviceTwinCallback, context) != IOTHUB_CLIENT_OK) + { + LogError("Failure sending data"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + break; + } + case IOTHUB_CLIENT_LL_HANDLE_TYPE: + { + if (IoTHubClient_LL_SendReportedState(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, buffer, bufferSize, deviceTwinCallback, context) != IOTHUB_CLIENT_OK) + { + LogError("Failure sending data"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + result = IOTHUB_CLIENT_OK; + } + break; + } + default: + { + LogError("INTERNAL ERROR: unexpected value for enum (%d)", (int)protoHandle->iothubClientHandleVariant.iothubClientHandleType); + result = IOTHUB_CLIENT_ERROR; + break; + } + } + } + free(buffer); + } + return result; +} + +#define DECLARE_DEVICETWIN_MODEL(name, ...) \ + DECLARE_MODEL(name, __VA_ARGS__) \ + static name* C2(IoTHubDeviceTwin_Create, name)(IOTHUB_CLIENT_HANDLE iotHubClientHandle) \ + { \ + SERIALIZER_DEVICETWIN_PROTOHANDLE protoHandle; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleType = IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle = iotHubClientHandle; \ + return (name*)IoTHubDeviceTwinCreate_Impl(#name, sizeof(name), &protoHandle); \ + } \ + \ + static void C2(IoTHubDeviceTwin_Destroy, name) (name* model) \ + { \ + IoTHubDeviceTwin_Destroy_Impl(model); \ + } \ + \ + static name* C2(IoTHubDeviceTwin_LL_Create, name)(IOTHUB_CLIENT_LL_HANDLE iotHubClientLLHandle) \ + { \ + SERIALIZER_DEVICETWIN_PROTOHANDLE protoHandle; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleType = IOTHUB_CLIENT_LL_HANDLE_TYPE; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle = iotHubClientLLHandle; \ + return (name*)IoTHubDeviceTwinCreate_Impl(#name, sizeof(name), &protoHandle); \ + } \ + \ + static void C2(IoTHubDeviceTwin_LL_Destroy, name) (name* model) \ + { \ + IoTHubDeviceTwin_Destroy_Impl(model); \ + } \ + static IOTHUB_CLIENT_RESULT C2(IoTHubDeviceTwin_LL_SendReportedState, name) (name* model, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK deviceTwinCallback, void* context) \ + { \ + return IoTHubDeviceTwin_SendReportedState_Impl(model, deviceTwinCallback, context); \ + } \ + static IOTHUB_CLIENT_RESULT C2(IoTHubDeviceTwin_SendReportedState, name) (name* model, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK deviceTwinCallback, void* context) \ + { \ + return IoTHubDeviceTwin_SendReportedState_Impl(model, deviceTwinCallback, context); \ + } \ + +#endif /*SERIALIZER_DEVICE_TWIN_H*/ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/agenttypesystem.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,4011 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "agenttypesystem.h" +#include <inttypes.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4756) /* Known warning for INFINITY */ +#endif + +#include <stddef.h> + +#include <float.h> +#include <math.h> +#include <limits.h> +#include <errno.h> + +/*if ULLONG_MAX is defined by limits.h for whatever reasons... */ +#ifndef ULLONG_MAX +#define ULLONG_MAX 18446744073709551615 +#endif + +#include "azure_c_shared_utility/crt_abstractions.h" + +#include "jsonencoder.h" +#include "multitree.h" + +#include "azure_c_shared_utility/xlogging.h" + +#define NaN_STRING "NaN" +#define MINUSINF_STRING "-INF" +#define PLUSINF_STRING "INF" + +#ifndef _HUGE_ENUF +#define _HUGE_ENUF 1e+300 /* _HUGE_ENUF*_HUGE_ENUF must overflow */ +#endif /* _HUGE_ENUF */ + +#ifndef INFINITY +#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF)) /* causes warning C4756: overflow in constant arithmetic (by design) */ +#endif /* INFINITY */ + +#ifndef NAN +#define NAN ((float)(INFINITY * 0.0F)) +#endif /* NAN */ + +#define GUID_STRING_LENGTH 38 + +// This is an artificial upper limit on floating point string length +// (e.g. the size of the string when printing %f). It is set to twice the +// maximum decimal precision plus 2. 1 for the decimal point and 1 for a +// sign (+/-) +// Unfortunately it is quite possible to print a float larger than this. +// An example of this would be printf("%.*f", MAX_FLOATING_POINT_STRING_LENGTH, 1.3); +// But currently no explicit requests for this exist in the file nor are +// any expected to reasonably occur when being used (numbers that hit +// this limit would be experiencing significant precision loss in storage anyway. +#define MAX_FLOATING_POINT_STRING_LENGTH (DECIMAL_DIG *2 + 2) + +// This maximum length is 11 for 32 bit integers (including the sign) +// optionally increase to 21 if longs are 64 bit +#define MAX_LONG_STRING_LENGTH ( 11 + (10 * (sizeof(long)/ 8))) + +// This is the maximum length for the largest 64 bit number (signed) +#define MAX_ULONG_LONG_STRING_LENGTH 20 + +DEFINE_ENUM_STRINGS(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_RESULT_VALUES); + +static int ValidateDate(int year, int month, int day); + +static int NoCloneFunction(void** destination, const void* source) +{ + *destination = (void*)source; + return 0; +} + +static void NoFreeFunction(void* value) +{ + (void)value; +} + + +#define IS_DIGIT(a) (('0'<=(a)) &&((a)<='9')) +#define splitInt(intVal, bytePos) (char)((intVal >> (bytePos << 3)) & 0xFF) +#define joinChars(a, b, c, d) (uint32_t)( (uint32_t)a + ((uint32_t)b << 8) + ((uint32_t)c << 16) + ((uint32_t)d << 24)) + +static char base64char(unsigned char val) +{ + char result; + + if (val < 26) + { + result = 'A' + (char)val; + } + else if (val < 52) + { + result = 'a' + ((char)val - 26); + } + else if (val < 62) + { + result = '0' + ((char)val - 52); + } + else if (val == 62) + { + result = '-'; + } + else + { + result = '_'; + } + + return result; +} + +static char base64b16(unsigned char val) +{ + const uint32_t base64b16values[4] = { + joinChars('A', 'E', 'I', 'M'), + joinChars('Q', 'U', 'Y', 'c'), + joinChars('g', 'k', 'o', 's'), + joinChars('w', '0', '4', '8') + }; + return splitInt(base64b16values[val >> 2], (val & 0x03)); +} + +static char base64b8(unsigned char val) +{ + const uint32_t base64b8values = joinChars('A', 'Q', 'g', 'w'); + return splitInt(base64b8values, val); +}; + +/*creates an AGENT_DATA_TYPE containing a EDM_BOOLEAN from a int*/ +AGENT_DATA_TYPES_RESULT Create_EDM_BOOLEAN_from_int(AGENT_DATA_TYPE* agentData, int v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[All the Create_... functions shall check their parameters for validity.When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned]*/ + if(agentData==NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_031:[ Creates a AGENT_DATA_TYPE representing an EDM_BOOLEAN.]*/ + agentData->type = EDM_BOOLEAN_TYPE; + agentData->value.edmBoolean.value = (v)?(EDM_TRUE):(EDM_FALSE); + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*creates an AGENT_DATA_TYPE containing a UINT8*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_UINT8(AGENT_DATA_TYPE* agentData, uint8_t v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_BYTE_TYPE; + agentData->value.edmByte.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_091:[Creates an AGENT_DATA_TYPE containing an Edm.DateTimeOffset from an EDM_DATE_TIME_OFFSET.]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET(AGENT_DATA_TYPE* agentData, EDM_DATE_TIME_OFFSET v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ]*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (ValidateDate(v.dateTime.tm_year+1900, v.dateTime.tm_mon +1 , v.dateTime.tm_mday) != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_092:[ The structure shall be validated to be conforming to OData specifications (odata-abnf-construction-rules, 2013), and if found invalid, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if ( + (v.dateTime.tm_hour > 23) || + (v.dateTime.tm_hour < 0) || + (v.dateTime.tm_min > 59) || + (v.dateTime.tm_min < 0) || + (v.dateTime.tm_sec > 59) || + (v.dateTime.tm_sec < 0) + ) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_092:[ The structure shall be validated, and if found invalid, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if ((v.hasFractionalSecond) && (v.fractionalSecond > 999999999999)) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_092:[ The structure shall be validated to be conforming to OData specifications (odata-abnf-construction-rules, 2013), and if found invalid, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if ( + (v.hasTimeZone) && + ( + (v.timeZoneHour<-23) || + (v.timeZoneHour>23) || + (v.timeZoneMinute>59) + ) + ) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_092:[ The structure shall be validated to be conforming to OData specifications (odata-abnf-construction-rules, 2013), and if found invalid, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + agentData->type = EDM_DATE_TIME_OFFSET_TYPE; + agentData->value.edmDateTimeOffset = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_094:[ Creates and AGENT_DATA_TYPE containing a EDM_GUID from an EDM_GUID]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_EDM_GUID(AGENT_DATA_TYPE* agentData, EDM_GUID v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the functions shall check their parameters for validity. When an invalid parameter is detected, the value AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("result = %s ", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_094:[ Creates and AGENT_DATA_TYPE containing a EDM_GUID from an EDM_GUID]*/ + agentData->type = EDM_GUID_TYPE; + memmove(agentData->value.edmGuid.GUID, v.GUID, 16); + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_098:[ Creates an AGENT_DATA_TYPE containing a EDM_BINARY from a EDM_BINARY.]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_EDM_BINARY(AGENT_DATA_TYPE* agentData, EDM_BINARY v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the functions shall check their parameters for validity. When an invalid parameter is detected, the value AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("result = %s", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_098:[ Creates an AGENT_DATA_TYPE containing a EDM_BINARY from a EDM_BINARY.]*/ + if (v.data == NULL) + { + if (v.size != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("result = %s ", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_BINARY_TYPE; + agentData->value.edmBinary.size = 0; + agentData->value.edmBinary.data = NULL; + result = AGENT_DATA_TYPES_OK; + } + } + else + { + if (v.size != 0) + { + /*make a copy*/ + if ((agentData->value.edmBinary.data = (unsigned char*)malloc(v.size)) == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("result = %s", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + (void)memcpy(agentData->value.edmBinary.data, v.data, v.size); + agentData->type = EDM_BINARY_TYPE; + agentData->value.edmBinary.size = v.size; + result = AGENT_DATA_TYPES_OK; + } + } + else + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("result = %s", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + } + } + return result; +} + +/*scans sign, if any*/ +/*if no sign, then it will set *sign to = +1*/ +/*if sign, then it will set *sign to = +/-1*/ +static void scanOptionalSign(const char* source, size_t sourceSize, size_t* position, int* sign) +{ + if (*position < sourceSize) + { + if (source[*position] == '-') + { + + *sign = -1; + (*position)++; + } + else if (source[*position] == '+') + { + *sign = +1; + (*position)++; + } + else + { + *sign = +1; + } + } +} + +/*scans a minus sign, if any*/ +/*if no sign, then it will set *sign to = +1*/ +/*if sign, then it will set *sign to = +/-1*/ +static void scanOptionalMinusSign(const char* source, size_t sourceSize, size_t* position, int* sign) +{ + if (*position < sourceSize) + { + if (source[*position] == '-') + { + + *sign = -1; + (*position)++; + } + else + { + *sign = +1; + } + } +} + +/*this function alawys returns 0 if it processed 1 digit*/ +/*return 1 when error (such as wrong parameters)*/ +static int scanMandatoryOneDigit(const char* source, size_t sourceSize, size_t* position) +{ + int result; + if (*position < sourceSize) + { + if (IS_DIGIT(source[*position])) + { + (*position)++; + result = 0; + } + else + { + result = 1; + } + } + else + { + result = 1; + } + return result; +} + +/*scans digits, if any*/ +static void scanOptionalNDigits(const char* source, size_t sourceSize, size_t* position) +{ + while (*position < sourceSize) + { + if (IS_DIGIT(source[*position])) + { + (*position)++; + } + else + { + break; + } + } +} + +/*from the string pointed to by source, having the size sourceSize, starting at initial position *position*/ +/*this function will attempt to read a decimal number having an optional sign(+/-) followed by precisely N digits */ +/*will return 0 if in the string there was a number and that number has been read in the *value parameter*/ +/*will update position parameter to reflect the first character not belonging to the number*/ +static int scanAndReadNDigitsInt(const char* source, size_t* position, int *value, size_t N) +{ + N++; + *value = 0; + while ((IS_DIGIT(source[*position])) && + (N > 0)) + { + *value *= 10; + *value += (source[*position] - '0'); + (*position)++; + N--; + } + + return N != 1; +} + +/*this function alawys returns 0 if it found a dot followed by at least digit*/ +/*return 1 when error (such as wrong parameters)*/ +static int scanOptionalDotAndDigits(const char* source, size_t sourceSize, size_t* position) +{ + int result = 0; + if (*position < sourceSize) + { + if (source[*position] == '.') + { + (*position)++; + if (scanMandatoryOneDigit(source, sourceSize, position) != 0) + { + /* not a digit following the dot... */ + result = 1; + } + else + { + scanOptionalNDigits(source, sourceSize, position); + } + } + else + { + /*not a dot, don't care*/ + } + } + return result; +} + + +static int scanMandatory1CapitalHexDigit(const char* source, uint8_t* value) +{ + int result = 0; + if (('0' <= source[0]) && (source[0] <= '9')) + { + *value = (source[0] - '0'); + } + else if (('A' <= source[0]) && (source[0] <= 'F')) + { + *value = (source[0] - 'A'+10); + } + else + { + result = 1; + } + return result; +} + +/*this function alawys returns 0 if it found 2 hex digits, also updates the *value parameter*/ +/*return 1 when error (such as wrong parameters)*/ +static int scanMandatory2CapitalHexDigits(const char* source, uint8_t* value) +{ + int result; + uint8_t temp; + if (scanMandatory1CapitalHexDigit(source, &temp) == 0) + { + *value = temp*16; + if (scanMandatory1CapitalHexDigit(source + 1, &temp) == 0) + { + *value += temp; + result = 0; + } + else + { + result = 1; + } + } + else + { + result = 2; + } + + return result; +} + +static int ValidateDecimal(const char* v, size_t vlen) +{ + int result; + int sign = 0; + size_t validatePosition = 0; + scanOptionalSign(v, vlen, &validatePosition, &sign); + if (scanMandatoryOneDigit(v, vlen, &validatePosition) != 0) + { + result = 1; + } + else + { + scanOptionalNDigits(v, vlen, &validatePosition); + if (scanOptionalDotAndDigits(v, vlen, &validatePosition) != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_067:[ If the string indicated by the parameter v does not match exactly an ODATA string representation, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + else if (validatePosition != vlen) /*Trailing wrong characters*/ + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_067:[ If the string indicated by the parameter v does not match exactly an ODATA string representation, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + else + { + result = 0; + } + } + + return result; +} + +/*return 0 if everything went ok*/ +/*takes 1 base64char and returns its value*/ +static int base64toValue(char base64charSource, unsigned char* value) +{ + int result; + if (('A' <= base64charSource) && (base64charSource <= 'Z')) + { + *value = base64charSource - 'A'; + result = 0; + } + else if (('a' <= base64charSource) && (base64charSource <= 'z')) + { + *value = ('Z' - 'A') +1+ (base64charSource - 'a'); + result = 0; + } + else if (('0' <= base64charSource) && (base64charSource <= '9')) + { + *value = ('Z' - 'A') +1+('z'-'a')+1 +(base64charSource - '0'); + result = 0; + } + else if ('-' == base64charSource) + { + *value = 62; + result = 0; + } + else if ('_' == base64charSource) + { + *value = 63; + result = 0; + } + else + { + result = 1; + } + return result; +} + +/*returns 0 if everything went ok*/ +/*scans 4 base64 characters and returns 3 usual bytes*/ +static int scan4base64char(const char* source, size_t sourceSize, unsigned char *destination0, unsigned char* destination1, unsigned char* destination2) +{ + int result; + if (sourceSize < 4) + { + result = 1; + } + else + { + unsigned char b0, b1, b2, b3; + if ( + (base64toValue(source[0], &b0) == 0) && + (base64toValue(source[1], &b1) == 0) && + (base64toValue(source[2], &b2) == 0) && + (base64toValue(source[3], &b3) == 0) + ) + { + *destination0 = (b0 << 2) | ((b1 & 0x30) >> 4); + *destination1 = ((b1 & 0x0F)<<4) | ((b2 & 0x3C) >>2 ); + *destination2 = ((b2 & 0x03) << 6) | (b3); + result = 0; + } + else + { + result = 2; + } + } + return result; +} + +/*return 0 if the character is one of ( 'A' / 'E' / 'I' / 'M' / 'Q' / 'U' / 'Y' / 'c' / 'g' / 'k' / 'o' / 's' / 'w' / '0' / '4' / '8' )*/ +static int base64b16toValue(unsigned char source, unsigned char* destination) +{ + unsigned char i; + for (i = 0; i <= 15; i++) + { + if (base64b16(i) == source) + { + *destination = i; + return 0; + } + } + return 1; +} + +/*return 0 if the character is one of ( 'A' / 'Q' / 'g' / 'w' )*/ +static int base64b8toValue(unsigned char source, unsigned char* destination) +{ + unsigned char i; + for (i = 0; i <= 3; i++) + { + if (base64b8(i) == source) + { + *destination = i; + return 0; + } + } + return 1; +} + + +/*returns 0 if everything went ok*/ +/*scans 2 base64 characters + 1 special + 1 optional = and returns 2 usual bytes*/ +int scanbase64b16(const char* source, size_t sourceSize, size_t *consumed, unsigned char* destination0, unsigned char* destination1) +{ + int result; + if (sourceSize < 3) + { + result = 1; + } + else + { + unsigned char c0, c1, c2; + *consumed = 0; + if ( + (base64toValue(source[0], &c0) == 0) && + (base64toValue(source[1], &c1) == 0) && + (base64b16toValue(source[2], &c2) == 0) + ) + { + *consumed = 3 + ((sourceSize>=3)&&(source[3] == '=')); /*== produce 1 or 0 ( in C )*/ + *destination0 = (c0 << 2) | ((c1 & 0x30) >> 4); + *destination1 = ((c1 &0x0F) <<4) | c2; + result = 0; + } + else + { + result = 2; + } + } + return result; +} + +/*return 0 if everything is ok*/ +/*Reads base64b8 = base64char ( 'A' / 'Q' / 'g' / 'w' ) [ "==" ]*/ +int scanbase64b8(const char* source, size_t sourceSize, size_t *consumed, unsigned char* destination0) +{ + int result; + if (sourceSize < 2) + { + result = 1; + } + else + { + unsigned char c0, c1; + if ( + (base64toValue(source[0], &c0) == 0) && + (base64b8toValue(source[1], &c1) == 0) + ) + { + *consumed = 2 + (((sourceSize>=4) && (source[2] == '=') && (source[3] == '='))?2:0); /*== produce 1 or 0 ( in C )*/ + *destination0 = (c0 << 2) | (c1 & 3); + result = 0; + } + else + { + result = 2; + } + } + return result; +} + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_039:[ Creates an AGENT_DATA_TYPE containing an EDM_DECIMAL from a null-terminated string.]*/ +AGENT_DATA_TYPES_RESULT Create_EDM_DECIMAL_from_charz(AGENT_DATA_TYPE* agentData, const char* v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (v == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t vlen = strlen(v); + /*validate that v has the form [SIGN] 1*DIGIT ["." 1*DIGIT]*/ + if (vlen == 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_067:[ If the string indicated by the parameter v does not match exactly an ODATA string representation, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (ValidateDecimal(v, vlen) != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_067:[ If the string indicated by the parameter v does not match exactly an ODATA string representation, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if ((agentData->value.edmDecimal.value = STRING_construct(v)) == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_DECIMAL_TYPE; + result = AGENT_DATA_TYPES_OK; + } + } + } + return result; +} + +/*create an AGENT_DATA_TYPE containing an EDM_DOUBLE from a double*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_DOUBLE(AGENT_DATA_TYPE* agentData, double v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[All the Create_... functions shall check their parameters for validity.When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned]*/ + if(agentData==NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_041:[Creates an AGENT_DATA_TYPE containing an EDM_DOUBLE from double]*/ + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_042:[Values of NaN, -INF, +INF are accepted]*/ + agentData->type = EDM_DOUBLE_TYPE; + agentData->value.edmDouble.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*create an AGENT_DATA_TYPE from INT16_T*/ +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_043:[ Creates an AGENT_DATA_TYPE containing an EDM_INT16 from int16_t]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_SINT16(AGENT_DATA_TYPE* agentData, int16_t v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_INT16_TYPE; + agentData->value.edmInt16.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*create an AGENT_DATA_TYPE from INT32_T*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_SINT32(AGENT_DATA_TYPE* agentData, int32_t v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_INT32_TYPE; + agentData->value.edmInt32.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*create an AGENT_DATA_TYPE from INT64_T*/ +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_045:[ Creates an AGENT_DATA_TYPE containing an EDM_INT64 from int64_t]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_SINT64(AGENT_DATA_TYPE* agentData, int64_t v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + + } + else + { + agentData->type = EDM_INT64_TYPE; + agentData->value.edmInt64.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +/*create an AGENT_DATA_TYPE from int8_t*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_SINT8(AGENT_DATA_TYPE* agentData, int8_t v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + + } + else + { + agentData->type = EDM_SBYTE_TYPE; + agentData->value.edmSbyte.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +static int ValidateDate(int year, int month, int day) +{ + int result; + + if ((year <= -10000) || (year >= 10000)) + { + result = 1; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[ If year-month-date does not indicate a valid day (for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + if (day <= 0) + { + result = 1; + } + else + { + /*the following data will be validated...*/ + /*leap years are those that can be divided by 4. But if the year can be divided by 100, it is not leap. But if they year can be divided by 400 it is leap*/ + if ( + (month == 1) || /*these months have always 31 days*/ + (month == 3) || + (month == 5) || + (month == 7) || + (month == 8) || + (month == 10) || + (month == 12) + ) + { + if (day <= 31) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + else if ( + (month == 4) || + (month == 6) || + (month == 9) || + (month == 11) + ) + { + if (day <= 30) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = -1; + } + } + else if (month == 2)/*february*/ + { + if ((year % 400) == 0) + { + /*these are leap years, suchs as 2000*/ + if (day <= 29) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + else if ((year % 100) == 0) + { + /*non-leap year, such as 1900*/ + if (day <= 28) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + else if ((year % 4) == 0) + { + /*these are leap years, such as 2104*/ + if (day <= 29) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + else + { + /*certainly not a leap year*/ + if (day <= 28) + { + result = 0; + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_070:[If year - month - date does not indicate a valid day(for example 31 Feb 2013), then AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + result = 1; + } + } + } + + return result; +} + +/*Codes_SRS_AGENT_TYPE_SYSTEM_99_069:[ Creates an AGENT_DATA_TYPE containing an EDM_DATE from a year, a month and a day of the month.]*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_date(AGENT_DATA_TYPE* agentData, int16_t year, uint8_t month, uint8_t day) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*ODATA-ABNF: year = [ "-" ] ( "0" 3DIGIT / oneToNine 3*DIGIT )*/ + else if (ValidateDate(year, month, day) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_069:[ Creates an AGENT_DATA_TYPE containing an EDM_DATE from a year, a month and a day of the month.]*/ + agentData->type = EDM_DATE_TYPE; + agentData->value.edmDate.year = year; + agentData->value.edmDate.month = month; + agentData->value.edmDate.day = day; + result = AGENT_DATA_TYPES_OK; + } + + return result; +} + + +/*create an AGENT_DATA_TYPE from SINGLE*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_FLOAT(AGENT_DATA_TYPE* agentData, float v) +{ + AGENT_DATA_TYPES_RESULT result; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[All the Create_... functions shall check their parameters for validity.When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned]*/ + if(agentData==NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_SINGLE_TYPE; + agentData->value.edmSingle.value = v; + result = AGENT_DATA_TYPES_OK; + } + return result; +} + +const char hexToASCII[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +/*create an AGENT_DATA_TYPE from ANSI zero terminated string*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_charz(AGENT_DATA_TYPE* agentData, const char* v) +{ + AGENT_DATA_TYPES_RESULT result = AGENT_DATA_TYPES_OK; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (v == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_049:[ Creates an AGENT_DATA_TYPE containing an EDM_STRING from an ASCII zero terminated string.]*/ + agentData->type = EDM_STRING_TYPE; + if (mallocAndStrcpy_s(&agentData->value.edmString.chars, v) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->value.edmString.length = strlen(agentData->value.edmString.chars); + result = AGENT_DATA_TYPES_OK; + } + } + return result; +} + +/*create an AGENT_DATA_TYPE from ANSI zero terminated string (without quotes) */ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_charz_no_quotes(AGENT_DATA_TYPE* agentData, const char* v) +{ + AGENT_DATA_TYPES_RESULT result = AGENT_DATA_TYPES_OK; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the Create_... functions shall check their parameters for validity. When an invalid parameter is detected, a code of AGENT_DATA_TYPES_INVALID_ARG shall be returned ].*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (v == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_01_001: [Creates an AGENT_DATA_TYPE containing an EDM_STRING from an ASCII zero terminated string.] */ + agentData->type = EDM_STRING_NO_QUOTES_TYPE; + if (mallocAndStrcpy_s(&agentData->value.edmStringNoQuotes.chars, v) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->value.edmStringNoQuotes.length = strlen(agentData->value.edmStringNoQuotes.chars); + result = AGENT_DATA_TYPES_OK; + } + } + return result; +} + +void Destroy_AGENT_DATA_TYPE(AGENT_DATA_TYPE* agentData) +{ + if(agentData==NULL) + { + LogError("agentData was NULL."); + } + else + { + switch(agentData->type) + { + default: + { + LogError("invalid agentData"); + break; + } + case(EDM_NO_TYPE): + { + LogError("invalid agentData"); + break; + } + case(EDM_BINARY_TYPE): + { + /*destroying means maybe deallocating some memory*/ + free(agentData->value.edmBinary.data); /* If ptr is a null pointer, no action occurs*/ + agentData->value.edmBinary.data = NULL; + break; + } + case(EDM_BOOLEAN_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_BYTE_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_DATE_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_DATE_TIME_OFFSET_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_DECIMAL_TYPE): + { + STRING_delete(agentData->value.edmDecimal.value); + agentData->value.edmDecimal.value = NULL; + break; + } + case(EDM_DURATION_TYPE): + { + LogError("EDM_DURATION_TYPE not implemented"); + break; + } + case(EDM_GUID_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_INT16_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_INT32_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_INT64_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_SBYTE_TYPE): + { + /*nothing to do*/ + break; + } + case(EDM_STREAM): + { + LogError("EDM_STREAM not implemented"); + break; + } + case(EDM_STRING_TYPE): + { + if (agentData->value.edmString.chars != NULL) + { + free(agentData->value.edmString.chars); + agentData->value.edmString.chars = NULL; + } + break; + } + case(EDM_STRING_NO_QUOTES_TYPE) : + { + if (agentData->value.edmStringNoQuotes.chars != NULL) + { + free(agentData->value.edmStringNoQuotes.chars); + agentData->value.edmStringNoQuotes.chars = NULL; + } + break; + } + case(EDM_TIME_OF_DAY_TYPE) : + { + LogError("EDM_TIME_OF_DAY_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_TYPE): + { + LogError("EDM_GEOGRAPHY_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_POINT_TYPE): + { + LogError("EDM_GEOGRAPHY_POINT_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_LINE_STRING_TYPE): + { + LogError("EDM_GEOGRAPHY_LINE_STRING_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_POLYGON_TYPE): + { + LogError("EDM_GEOGRAPHY_POLYGON_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_MULTI_POINT_TYPE): + { + LogError("EDM_GEOGRAPHY_MULTI_POINT_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_MULTI_LINE_STRING_TYPE): + { + LogError("EDM_GEOGRAPHY_MULTI_LINE_STRING_TYPE not implemented"); + break; + } + case(EDM_GEOGRAPHY_MULTI_POLYGON_TYPE): + { + LogError("EDM_GEOGRAPHY_MULTI_POLYGON_TYPE"); + break; + } + case(EDM_GEOGRAPHY_COLLECTION_TYPE): + { + LogError("EDM_GEOGRAPHY_COLLECTION_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_TYPE): + { + LogError("EDM_GEOMETRY_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_POINT_TYPE): + { + LogError("EDM_GEOMETRY_POINT_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_LINE_STRING_TYPE): + { + LogError("EDM_GEOMETRY_LINE_STRING_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_POLYGON_TYPE): + { + LogError("EDM_GEOMETRY_POLYGON_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_MULTI_POINT_TYPE): + { + LogError("EDM_GEOMETRY_MULTI_POINT_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_MULTI_LINE_STRING_TYPE): + { + LogError("EDM_GEOMETRY_MULTI_LINE_STRING_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_MULTI_POLYGON_TYPE): + { + LogError("EDM_GEOMETRY_MULTI_POLYGON_TYPE not implemented"); + break; + } + case(EDM_GEOMETRY_COLLECTION_TYPE): + { + LogError("EDM_GEOMETRY_COLLECTION_TYPE not implemented"); + break; + } + case(EDM_SINGLE_TYPE): + { + break; + } + case(EDM_DOUBLE_TYPE): + { + break; + } + case (EDM_COMPLEX_TYPE_TYPE): + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_050:[Destroy_AGENT_DATA_TYPE shall deallocate all allocated resources used to represent the type.]*/ + size_t i; + for (i = 0; i < agentData->value.edmComplexType.nMembers; i++) + { + free((void*)(agentData->value.edmComplexType.fields[i].fieldName)); + agentData->value.edmComplexType.fields[i].fieldName = NULL; + Destroy_AGENT_DATA_TYPE(agentData->value.edmComplexType.fields[i].value); + free(agentData->value.edmComplexType.fields[i].value); + agentData->value.edmComplexType.fields[i].value = NULL; + } + free(agentData->value.edmComplexType.fields); + break; + } + } + agentData->type = EDM_NO_TYPE; /*mark as detroyed*/ + } +} + +static char hexDigitToChar(uint8_t hexDigit) +{ + if (hexDigit < 10) return '0' + hexDigit; + else return ('A' - 10) + hexDigit; +} + +AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const AGENT_DATA_TYPE* value) +{ + AGENT_DATA_TYPES_RESULT result; + + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_015:[If destination parameter is NULL, AgentDataTypes_ToString shall return AGENT_DATA_TYPES_INVALID_ARG.]*/ + if(destination == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_053:[ If value is NULL or has been destroyed or otherwise doesn't contain valid data, AGENT_DATA_TYPES_INVALID_ARG shall be returned.]*/ + else if (value == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + switch(value->type) + { + default: + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_NULL_TYPE) : + { + /*SRS_AGENT_TYPE_SYSTEM_99_101:[ EDM_NULL_TYPE shall return the unquoted string null.]*/ + if (STRING_concat(destination, "null") != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_014:[ All functions shall return AGENT_DATA_TYPES_OK when the processing is successful.]*/ + result = AGENT_DATA_TYPES_OK; + } + break; + } + case(EDM_BOOLEAN_TYPE) : + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_017:[EDM_BOOLEAN:as in(odata - abnf - construction - rules, 2013), booleanValue = "true" / "false"]*/ + if (value->value.edmBoolean.value == EDM_TRUE) + { + /*SRS_AGENT_TYPE_SYSTEM_99_030:[If v is different than 0 then the AGENT_DATA_TYPE shall have the value "true".]*/ + if (STRING_concat(destination, "true") != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_014:[ All functions shall return AGENT_DATA_TYPES_OK when the processing is successful.]*/ + result = AGENT_DATA_TYPES_OK; + } + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_017:[EDM_BOOLEAN:as in(odata - abnf - construction - rules, 2013), booleanValue = "true" / "false"]*/ + else if (value->value.edmBoolean.value == EDM_FALSE) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_029:[ If v 0 then the AGENT_DATA_TYPE shall have the value "false" Boolean.]*/ + if (STRING_concat(destination, "false") != 0) + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_014:[ All functions shall return AGENT_DATA_TYPES_OK when the processing is successful.]*/ + result = AGENT_DATA_TYPES_OK; + } + } + else + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_053:[ If value contains invalid data, AgentDataTypes_ToString shall return AGENT_DATA_TYPES_INVALID_ARG.]*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + + break; + } + case(EDM_BYTE_TYPE) : + { + char tempbuffer2[4]; /*because bytes can at most be 255 and that is 3 characters + 1 for '\0'*/ + size_t pos = 0; + if (value->value.edmByte.value >= 100) tempbuffer2[pos++] = '0' + (value->value.edmByte.value / 100); + if (value->value.edmByte.value >= 10) tempbuffer2[pos++] = '0' + (value->value.edmByte.value % 100) / 10; + tempbuffer2[pos++] = '0' + (value->value.edmByte.value % 10); + tempbuffer2[pos++] = '\0'; + + if (STRING_concat(destination, tempbuffer2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case(EDM_DATE_TYPE) : + { + size_t pos = 0; + char tempBuffer2[1 + 5 + 1 + 2 + 1 + 2 + 1+1]; + int16_t year = value->value.edmDate.year; + tempBuffer2[pos++] = '\"'; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_068:[ EDM_DATE: dateValue = year "-" month "-" day.]*/ + if (year < 0) + { + tempBuffer2[pos++] = '-'; + year = -year; + } + + tempBuffer2[pos++] = '0' + (char)(year / 1000); + tempBuffer2[pos++] = '0' + (char)((year % 1000) / 100); + tempBuffer2[pos++] = '0' + (char)((year % 100) / 10); + tempBuffer2[pos++] = '0' + (char)(year % 10); + tempBuffer2[pos++] = '-'; + tempBuffer2[pos++] = '0' + value->value.edmDate.month / 10; + tempBuffer2[pos++] = '0' + value->value.edmDate.month % 10; + tempBuffer2[pos++] = '-'; + tempBuffer2[pos++] = '0' + value->value.edmDate.day / 10; + tempBuffer2[pos++] = '0' + value->value.edmDate.day % 10; + tempBuffer2[pos++] = '\"'; + tempBuffer2[pos++] = '\0'; + + if (STRING_concat(destination, tempBuffer2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_DATE_TIME_OFFSET_TYPE): + { + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_019:[ EDM_DATETIMEOFFSET: dateTimeOffsetValue = year "-" month "-" day "T" hour ":" minute [ ":" second [ "." fractionalSeconds ] ] ( "Z" / sign hour ":" minute )]*/ + /*from ABNF seems like these numbers HAVE to be padded with zeroes*/ + if (value->value.edmDateTimeOffset.hasTimeZone) + { + if (value->value.edmDateTimeOffset.hasFractionalSecond) + { + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // . + MAX_ULONG_LONG_STRING_LENGTH + // %.12llu + 1 + MAX_LONG_STRING_LENGTH + // %+.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + //\" + 1; // " (terminating NULL); + + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12llu%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year+1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.fractionalSecond, + value->value.edmDateTimeOffset.timeZoneHour, + value->value.edmDateTimeOffset.timeZoneMinute) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + // Clean up temp buffer if allocated + free(tempBuffer); + } + } + else + { + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + MAX_LONG_STRING_LENGTH + // %+.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // \" + 1; // " terminating NULL + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.timeZoneHour, + value->value.edmDateTimeOffset.timeZoneMinute) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + // Clean up temp buffer if allocated + free(tempBuffer); + } + } + } + else + { + if (value->value.edmDateTimeOffset.hasFractionalSecond) + { + size_t tempBufferSize = 1 + //\" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // . + MAX_ULONG_LONG_STRING_LENGTH + // %.12llu + 1 + // Z + 1 + // \" + 1; // " (terminating NULL) + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12lluZ\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.fractionalSecond) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } + } + else + { + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // Z + 1 + // \" + 1; // " (terminating null); + + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ\"", + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } + } + } + break; + } + case(EDM_DECIMAL_TYPE) : + { + if (STRING_concat_with_STRING(destination, value->value.edmDecimal.value) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_INT16_TYPE) : + { + /*-32768 to +32767*/ + char buffertemp2[7]; /*because 5 digits and sign and '\0'*/ + uint16_t positiveValue; + size_t pos = 0; + uint16_t rank = (uint16_t)10000; + bool foundFirstDigit = false; + + if (value->value.edmInt16.value < 0) + { + buffertemp2[pos++] = '-'; + positiveValue = -value->value.edmInt16.value; + } + else + { + positiveValue = value->value.edmInt16.value; + } + + while (rank >= 10) + { + if ((foundFirstDigit == true) || (positiveValue / rank) > 0) + { + buffertemp2[pos++] = '0' + (char)(positiveValue / rank); + foundFirstDigit = true; + } + positiveValue %= rank; + rank /= 10; + } + buffertemp2[pos++] = '0' + (char)(positiveValue); + buffertemp2[pos++] = '\0'; + + if (STRING_concat(destination, buffertemp2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_INT32_TYPE) : + { + /*-2147483648 to +2147483647*/ + char buffertemp2[12]; /*because 10 digits and sign and '\0'*/ + uint32_t positiveValue; + size_t pos = 0; + uint32_t rank = 1000000000UL; + bool foundFirstDigit = false; + + if (value->value.edmInt32.value < 0) + { + buffertemp2[pos++] = '-'; + positiveValue = - value->value.edmInt32.value; + } + else + { + positiveValue = value->value.edmInt32.value; + } + + while (rank >= 10) + { + if ((foundFirstDigit == true) || (positiveValue / rank) > 0) + { + buffertemp2[pos++] = '0' + (char)(positiveValue / rank); + foundFirstDigit = true; + } + positiveValue %= rank; + rank /= 10; + } + buffertemp2[pos++] = '0' + (char)(positiveValue); + buffertemp2[pos++] = '\0'; + + if (STRING_concat(destination, buffertemp2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_INT64_TYPE): + { + char buffertemp2[21]; /*because 19 digits and sign and '\0'*/ + uint64_t positiveValue; + size_t pos = 0; + uint64_t rank = 10000000000000000000ULL; + bool foundFirstDigit = false; + + if (value->value.edmInt64.value < 0) + { + buffertemp2[pos++] = '-'; + positiveValue = -value->value.edmInt64.value; + } + else + { + positiveValue = value->value.edmInt64.value; + } + + while (rank >= 10) + { + if ((foundFirstDigit == true) || (positiveValue / rank) > 0) + { + buffertemp2[pos++] = '0' + (char)(positiveValue / rank); + foundFirstDigit = true; + } + positiveValue %= rank; + rank /= 10; + } + buffertemp2[pos++] = '0' + (char)(positiveValue); + buffertemp2[pos++] = '\0'; + + if (STRING_concat(destination, buffertemp2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_SBYTE_TYPE) : + { + char tempbuffer2[5]; /* because '-' and 3 characters for 127 let's say and '\0'*/ + int absValue = value->value.edmSbyte.value; + size_t pos=0; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_026:[ EDM_SBYTE: sbyteValue = [ sign ] 1*3DIGIT ; numbers in the range from -128 to 127]*/ + + if (value->value.edmSbyte.value < 0) + { + tempbuffer2[pos++] = '-'; + absValue = -absValue; + } + + if (absValue >= 100 ) tempbuffer2[pos++] = '0' + (char)(absValue / 100); + if (absValue >=10) tempbuffer2[pos++] = '0' + (absValue % 100) / 10; + tempbuffer2[pos++] = '0' + (absValue % 10); + tempbuffer2[pos++] = '\0'; + + + if (STRING_concat(destination, tempbuffer2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case (EDM_STRING_TYPE): + { + size_t i; + size_t nControlCharacters = 0; /*counts how many characters are to be expanded from 1 character to \uxxxx (6 characters)*/ + size_t nEscapeCharacters = 0; + size_t vlen = value->value.edmString.length; + char* v = value->value.edmString.chars; + + for (i = 0; i < vlen; i++) + { + if ((unsigned char)v[i] >= 128) /*this be a UNICODE character begin*/ + { + break; + } + else + { + if (v[i] <= 0x1F) + { + nControlCharacters++; + } + else if ( + (v[i] == '"') || + (v[i] == '\\') || + (v[i] == '/') + ) + { + nEscapeCharacters++; + } + } + } + + if (i < vlen) + { + result = AGENT_DATA_TYPES_INVALID_ARG; /*don't handle those who do not copy bit by bit to UTF8*/ + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*forward parse the string to scan for " and for \ that in JSON are \" respectively \\*/ + size_t tempBufferSize = vlen + 5 * nControlCharacters + nEscapeCharacters + 3 + 1; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t w = 0; + tempBuffer[w++] = '"'; + for (i = 0; i < vlen; i++) + { + if (v[i] <= 0x1F) + { + tempBuffer[w++] = '\\'; + tempBuffer[w++] = 'u'; + tempBuffer[w++] = '0'; + tempBuffer[w++] = '0'; + tempBuffer[w++] = hexToASCII[(v[i] & 0xF0) >> 4]; /*high nibble*/ + tempBuffer[w++] = hexToASCII[v[i] & 0x0F]; /*lowNibble nibble*/ + } + else if (v[i] == '"') + { + tempBuffer[w++] = '\\'; + tempBuffer[w++] = '"'; + } + else if (v[i] == '\\') + { + tempBuffer[w++] = '\\'; + tempBuffer[w++] = '\\'; + } + else if (v[i] == '/') + { + tempBuffer[w++] = '\\'; + tempBuffer[w++] = '/'; + } + else + { + tempBuffer[w++] = v[i]; + } + } + +#ifdef _MSC_VER +#pragma warning(suppress: 6386) /* The test Create_AGENT_DATA_TYPE_from_charz_With_2_Slashes_Succeeds verifies that Code Analysis is wrong here */ +#endif + tempBuffer[w] = '"'; + /*zero terminating it*/ + tempBuffer[vlen + 5 * nControlCharacters + nEscapeCharacters + 3 - 1] = '\0'; + + if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_01_003: [EDM_STRING_no_quotes: the string is copied as given when the AGENT_DATA_TYPE was created.] */ + case (EDM_STRING_NO_QUOTES_TYPE) : + { + /* this is a special case where we just want to copy/paste the contents, no encoding, no quotes */ + /* Codes_SRS_AGENT_TYPE_SYSTEM_01_002: [When serialized, this type is not enclosed with quotes.] */ + if (STRING_concat(destination, value->value.edmStringNoQuotes.chars) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + break; + } + +#ifndef NO_FLOATS + case(EDM_SINGLE_TYPE): + { + /*C89 standard says: When a float is promoted to double or long double, or a double is promoted to long double, its value is unchanged*/ + /*I read that as : when a float is NaN or Inf, it will stay NaN or INF in double representation*/ + + /*The sprintf function returns the number of characters written in the array, not counting the terminating null character.*/ + + if(ISNAN(value->value.edmSingle.value)) + { + if (STRING_concat(destination, NaN_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + else if (ISNEGATIVEINFINITY(value->value.edmSingle.value)) + { + if (STRING_concat(destination, MINUSINF_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + else if (ISPOSITIVEINFINITY(value->value.edmSingle.value)) + { + if (STRING_concat(destination, PLUSINF_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + else + { + size_t tempBufferSize = MAX_FLOATING_POINT_STRING_LENGTH; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "%.*f", FLT_DIG, (double)(value->value.edmSingle.value)) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } + } + break; + } + case(EDM_DOUBLE_TYPE): + { + /*The sprintf function returns the number of characters written in the array, not counting the terminating null character.*/ + /*OData-ABNF says these can be used: nanInfinity = 'NaN' / '-INF' / 'INF'*/ + /*C90 doesn't declare a NaN or Inf in the standard, however, values might be NaN or Inf...*/ + /*C99 ... does*/ + /*C11 is same as C99*/ + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_022:[ EDM_DOUBLE: doubleValue = decimalValue [ "e" [SIGN] 1*DIGIT ] / nanInfinity ; IEEE 754 binary64 floating-point number (15-17 decimal digits). The representation shall use DBL_DIG C #define*/ + if(ISNAN(value->value.edmDouble.value)) + { + if (STRING_concat(destination, NaN_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_022:[ EDM_DOUBLE: doubleValue = decimalValue [ "e" [SIGN] 1*DIGIT ] / nanInfinity ; IEEE 754 binary64 floating-point number (15-17 decimal digits). The representation shall use DBL_DIG C #define*/ + else if (ISNEGATIVEINFINITY(value->value.edmDouble.value)) + { + if (STRING_concat(destination, MINUSINF_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_022:[ EDM_DOUBLE: doubleValue = decimalValue [ "e" [SIGN] 1*DIGIT ] / nanInfinity ; IEEE 754 binary64 floating-point number (15-17 decimal digits). The representation shall use DBL_DIG C #define*/ + else if (ISPOSITIVEINFINITY(value->value.edmDouble.value)) + { + if (STRING_concat(destination, PLUSINF_STRING) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_022:[ EDM_DOUBLE: doubleValue = decimalValue [ "e" [SIGN] 1*DIGIT ] / nanInfinity ; IEEE 754 binary64 floating-point number (15-17 decimal digits). The representation shall use DBL_DIG C #define*/ + else + { + size_t tempBufferSize = DECIMAL_DIG * 2; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "%.*f", DBL_DIG, value->value.edmDouble.value) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } + } + break; + } +#endif + + case(EDM_COMPLEX_TYPE_TYPE) : + { + /*to produce an EDM_COMPLEX_TYPE is a recursive process*/ + /*uses JSON encoder*/ + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_062:[ If the AGENT_DATA_TYPE represents a "complex type", then the JSON marshaller shall produce the following JSON value:[...]*/ + MULTITREE_HANDLE treeHandle; + result = AGENT_DATA_TYPES_OK; + /*SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + treeHandle = MultiTree_Create(NoCloneFunction, NoFreeFunction); + if (treeHandle == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t i; + for (i = 0; i < value->value.edmComplexType.nMembers; i++) + { + if (MultiTree_AddLeaf(treeHandle, value->value.edmComplexType.fields[i].fieldName, value->value.edmComplexType.fields[i].value) != MULTITREE_OK) + { + /*SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*all is fine*/ + } + } + + if (result == AGENT_DATA_TYPES_OK) + { + /*SRS_AGENT_TYPE_SYSTEM_99_016:[ When the value cannot be converted to a string AgentDataTypes_ToString shall return AGENT_DATA_TYPES_ERROR.]*/ + if (JSONEncoder_EncodeTree(treeHandle, destination, (JSON_ENCODER_TOSTRING_FUNC)AgentDataTypes_ToString) != JSON_ENCODER_OK) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /*all is fine*/ + } + + } + + MultiTree_Destroy(treeHandle); + } + break; + } + case EDM_GUID_TYPE: + { + char tempBuffer2[1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1+ 1]; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_093:[ EDM_GUID: 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG]*/ + tempBuffer2[0] = '\"'; + tempBuffer2[1] = hexDigitToChar(value->value.edmGuid.GUID[0] / 16); + tempBuffer2[2] = hexDigitToChar(value->value.edmGuid.GUID[0] % 16); + tempBuffer2[3] = hexDigitToChar(value->value.edmGuid.GUID[1] / 16); + tempBuffer2[4] = hexDigitToChar(value->value.edmGuid.GUID[1] % 16); + tempBuffer2[5] = hexDigitToChar(value->value.edmGuid.GUID[2] / 16); + tempBuffer2[6] = hexDigitToChar(value->value.edmGuid.GUID[2] % 16); + tempBuffer2[7] = hexDigitToChar(value->value.edmGuid.GUID[3] / 16); + tempBuffer2[8] = hexDigitToChar(value->value.edmGuid.GUID[3] % 16); + + tempBuffer2[9] = '-'; + tempBuffer2[10] = hexDigitToChar(value->value.edmGuid.GUID[4] / 16); + tempBuffer2[11] = hexDigitToChar(value->value.edmGuid.GUID[4] % 16); + tempBuffer2[12] = hexDigitToChar(value->value.edmGuid.GUID[5] / 16); + tempBuffer2[13] = hexDigitToChar(value->value.edmGuid.GUID[5] % 16); + + tempBuffer2[14] = '-'; + tempBuffer2[15] = hexDigitToChar(value->value.edmGuid.GUID[6] / 16); + tempBuffer2[16] = hexDigitToChar(value->value.edmGuid.GUID[6] % 16); + tempBuffer2[17] = hexDigitToChar(value->value.edmGuid.GUID[7] / 16); + tempBuffer2[18] = hexDigitToChar(value->value.edmGuid.GUID[7] % 16); + + tempBuffer2[19] = '-'; + tempBuffer2[20] = hexDigitToChar(value->value.edmGuid.GUID[8] / 16); + tempBuffer2[21] = hexDigitToChar(value->value.edmGuid.GUID[8] % 16); + tempBuffer2[22] = hexDigitToChar(value->value.edmGuid.GUID[9] / 16); + tempBuffer2[23] = hexDigitToChar(value->value.edmGuid.GUID[9] % 16); + + tempBuffer2[24] = '-'; + tempBuffer2[25] = hexDigitToChar(value->value.edmGuid.GUID[10] / 16); + tempBuffer2[26] = hexDigitToChar(value->value.edmGuid.GUID[10] % 16); + tempBuffer2[27] = hexDigitToChar(value->value.edmGuid.GUID[11] / 16); + tempBuffer2[28] = hexDigitToChar(value->value.edmGuid.GUID[11] % 16); + tempBuffer2[29] = hexDigitToChar(value->value.edmGuid.GUID[12] / 16); + tempBuffer2[30] = hexDigitToChar(value->value.edmGuid.GUID[12] % 16); + tempBuffer2[31] = hexDigitToChar(value->value.edmGuid.GUID[13] / 16); + tempBuffer2[32] = hexDigitToChar(value->value.edmGuid.GUID[13] % 16); + tempBuffer2[33] = hexDigitToChar(value->value.edmGuid.GUID[14] / 16); + tempBuffer2[34] = hexDigitToChar(value->value.edmGuid.GUID[14] % 16); + tempBuffer2[35] = hexDigitToChar(value->value.edmGuid.GUID[15] / 16); + tempBuffer2[36] = hexDigitToChar(value->value.edmGuid.GUID[15] % 16); + + tempBuffer2[37] = '\"'; + tempBuffer2[38] = '\0'; + + if (STRING_concat(destination, tempBuffer2) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + break; + } + case EDM_BINARY_TYPE: + { + size_t currentPosition = 0; + char* temp; + /*binary types */ + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_099:[EDM_BINARY:= *(4base64char)[base64b16 / base64b8]]*/ + /*the following will happen*/ + /*1. the "data" of the binary shall be "eaten" 3 characters at a time and produce 4 base64 encoded characters for as long as there are more than 3 characters still to process*/ + /*2. the remaining characters (1 or 2) shall be encoded.*/ + /*there's a level of assumption that 'a' corresponds to 0b000000 and that '_' corresponds to 0b111111*/ + /*the encoding will use the optional [=] or [==] at the end of the encoded string, so that other less standard aware libraries can do their work*/ + /*these are the bits of the 3 normal bytes to be encoded*/ + size_t neededSize = 2; /*2 because starting and ending quotes */ + neededSize += (value->value.edmBinary.size == 0) ? (0) : ((((value->value.edmBinary.size-1) / 3) + 1) * 4); + neededSize += 1; /*+1 because \0 at the end of the string*/ + if ((temp = (char*)malloc(neededSize))==NULL) + { + result = AGENT_DATA_TYPES_ERROR; + } + else + { + /*b0 b1(+1) b2(+2) + 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + |----c1---| |----c2---| |----c3---| |----c4---| + */ + + size_t destinationPointer = 0; + temp[destinationPointer++] = '"'; + while (value->value.edmBinary.size - currentPosition >= 3) + { + char c1 = base64char(value->value.edmBinary.data[currentPosition] >> 2); + char c2 = base64char( + ((value->value.edmBinary.data[currentPosition] & 3) << 4) | + (value->value.edmBinary.data[currentPosition + 1] >> 4) + ); + char c3 = base64char( + ((value->value.edmBinary.data[currentPosition + 1] & 0x0F) << 2) | + ((value->value.edmBinary.data[currentPosition + 2] >> 6) & 3) + ); + char c4 = base64char( + value->value.edmBinary.data[currentPosition + 2] & 0x3F + ); + currentPosition += 3; + temp[destinationPointer++] = c1; + temp[destinationPointer++] = c2; + temp[destinationPointer++] = c3; + temp[destinationPointer++] = c4; + + } + if (value->value.edmBinary.size - currentPosition == 2) + { + char c1 = base64char(value->value.edmBinary.data[currentPosition] >> 2); + char c2 = base64char( + ((value->value.edmBinary.data[currentPosition] & 0x03) << 4) | + (value->value.edmBinary.data[currentPosition + 1] >> 4) + ); + char c3 = base64b16(value->value.edmBinary.data[currentPosition + 1] & 0x0F); + temp[destinationPointer++] = c1; + temp[destinationPointer++] = c2; + temp[destinationPointer++] = c3; + temp[destinationPointer++] = '='; + } + else if (value->value.edmBinary.size - currentPosition == 1) + { + char c1 = base64char(value->value.edmBinary.data[currentPosition] >> 2); + char c2 = base64b8(value->value.edmBinary.data[currentPosition] & 0x03); + temp[destinationPointer++] = c1; + temp[destinationPointer++] = c2; + temp[destinationPointer++] = '='; + temp[destinationPointer++] = '='; + } + + /*closing quote*/ + temp[destinationPointer++] = '"'; + /*null terminating the string*/ + temp[destinationPointer] = '\0'; + + if (STRING_concat(destination, temp) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + free(temp); + } + break; + } + } + } + return result; +} + +/*return 0 if all names are different than NULL*/ +static int isOneNameNULL(size_t nMemberNames, const char* const * memberNames) +{ + size_t i; + int result = 0; + for (i = 0; i < nMemberNames; i++) + { + if (memberNames[i] == NULL) + { + result = 1; + break; + } + } + return result; +} + +/*return 0 if all names are different than NULL*/ +static int areThereTwoSameNames(size_t nMemberNames, const char* const * memberNames) +{ + size_t i, j; + int result = 0; + for (i = 0; i < nMemberNames-1; i++) + { + for (j = i + 1; j < nMemberNames; j++) + { + if (strcmp(memberNames[i], memberNames[j]) == 0) + { + result = 1; + goto out; + } + } + } +out: + return result; +} + +static void DestroyHalfBakedComplexType(AGENT_DATA_TYPE* agentData) +{ + size_t i; + if (agentData != NULL) + { + if (agentData->type == EDM_COMPLEX_TYPE_TYPE) + { + if (agentData->value.edmComplexType.fields != NULL) + { + for (i = 0; i < agentData->value.edmComplexType.nMembers; i++) + { + if (agentData->value.edmComplexType.fields[i].fieldName != NULL) + { + free((void*)(agentData->value.edmComplexType.fields[i].fieldName)); + agentData->value.edmComplexType.fields[i].fieldName = NULL; + } + if (agentData->value.edmComplexType.fields[i].value != NULL) + { + Destroy_AGENT_DATA_TYPE(agentData->value.edmComplexType.fields[i].value); + free(agentData->value.edmComplexType.fields[i].value); + agentData->value.edmComplexType.fields[i].value = NULL; + } + } + free(agentData->value.edmComplexType.fields); + agentData->value.edmComplexType.fields = NULL; + } + agentData->type = EDM_NO_TYPE; + } + } +} + +/* Creates an object of AGENT_DATA_TYPE_TYPE EDM_NULL_TYPE*/ +AGENT_DATA_TYPES_RESULT Create_NULL_AGENT_DATA_TYPE(AGENT_DATA_TYPE* agentData) +{ + AGENT_DATA_TYPES_RESULT result; + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_NULL_TYPE; + result = AGENT_DATA_TYPES_OK; + } + return result; +} +/*creates a copy of the AGENT_DATA_TYPE*/ +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(AGENT_DATA_TYPE* dest, const AGENT_DATA_TYPE* src) +{ + AGENT_DATA_TYPES_RESULT result; + size_t i; + if ((dest == NULL) || (src == NULL)) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + switch (src->type) + { + default: + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_NO_TYPE) : + case(EDM_NULL_TYPE) : + { + dest->type = src->type; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_BOOLEAN_TYPE) : + { + dest->type = src->type; + dest->value.edmBoolean= src->value.edmBoolean; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_BYTE_TYPE) : + { + dest->type = src->type; + dest->value.edmByte= src->value.edmByte; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_DATE_TYPE) : + { + dest->type = src->type; + dest->value.edmDate = src->value.edmDate; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_DATE_TIME_OFFSET_TYPE) : + { + dest->type = src->type; + dest->value.edmDateTimeOffset = src->value.edmDateTimeOffset; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_DECIMAL_TYPE) : + { + if ((dest->value.edmDecimal.value = STRING_clone(src->value.edmDecimal.value)) == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + dest->type = src->type; + result = AGENT_DATA_TYPES_OK; + /*all is fine*/ + } + break; + } + case(EDM_DOUBLE_TYPE) : + { + dest->type = src->type; + dest->value.edmDouble = src->value.edmDouble; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_DURATION_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GUID_TYPE) : + { + dest->type = src->type; + dest->value.edmGuid = src->value.edmGuid; + result = AGENT_DATA_TYPES_OK; + break; + } + case EDM_BINARY_TYPE: + { + if (src->value.edmBinary.size == 0) + { + dest->value.edmBinary.size = 0; + dest->value.edmBinary.data = NULL; + dest->type = EDM_BINARY_TYPE; + result = AGENT_DATA_TYPES_OK; + } + else + { + if ((dest->value.edmBinary.data = (unsigned char*)malloc(src->value.edmBinary.size)) == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + } + else + { + dest->value.edmBinary.size = src->value.edmBinary.size; + (void)memcpy(dest->value.edmBinary.data, src->value.edmBinary.data, src->value.edmBinary.size); + dest->type = EDM_BINARY_TYPE; + result = AGENT_DATA_TYPES_OK; + } + } + break; + } + case(EDM_INT16_TYPE) : + { + dest->type = src->type; + dest->value.edmInt16 = src->value.edmInt16; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_INT32_TYPE) : + { + dest->type = src->type; + dest->value.edmInt32 = src->value.edmInt32; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_INT64_TYPE) : + { + dest->type = src->type; + dest->value.edmInt64 = src->value.edmInt64; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_SBYTE_TYPE) : + { + dest->type = src->type; + dest->value.edmSbyte = src->value.edmSbyte; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_SINGLE_TYPE) : + { + dest->type = src->type; + dest->value.edmSingle = src->value.edmSingle; + result = AGENT_DATA_TYPES_OK; + break; + } + case(EDM_STREAM) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } /*not supported, because what is stream?*/ + case(EDM_STRING_TYPE) : + { + dest->type = src->type; + dest->value.edmString.length = src->value.edmString.length; + if (mallocAndStrcpy_s((char**)&(dest->value.edmString.chars), (char*)src->value.edmString.chars) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + /*all is fine*/ + } + break; + } + case(EDM_STRING_NO_QUOTES_TYPE) : + { + dest->type = src->type; + dest->value.edmStringNoQuotes.length = src->value.edmStringNoQuotes.length; + if (mallocAndStrcpy_s((char**)&(dest->value.edmStringNoQuotes.chars), (char*)src->value.edmStringNoQuotes.chars) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + /*all is fine*/ + } + break; + } + case(EDM_TIME_OF_DAY_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } /*not supported because what is "abstract base type"*/ + case(EDM_GEOGRAPHY_POINT_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_LINE_STRING_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_POLYGON_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_MULTI_POINT_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_MULTI_LINE_STRING_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_MULTI_POLYGON_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOGRAPHY_COLLECTION_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } /*not supported because what is "abstract base type"*/ + case(EDM_GEOMETRY_POINT_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_LINE_STRING_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_POLYGON_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_MULTI_POINT_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_MULTI_LINE_STRING_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_MULTI_POLYGON_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_GEOMETRY_COLLECTION_TYPE) : + { + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + case(EDM_COMPLEX_TYPE_TYPE) : + { + result = AGENT_DATA_TYPES_OK; + /*copying a COMPLEX_TYPE means to copy all its member names and all its fields*/ + dest->type = src->type; + if (src->value.edmComplexType.nMembers == 0) + { + /*error*/ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + dest->value.edmComplexType.nMembers = src->value.edmComplexType.nMembers; + dest->value.edmComplexType.fields = (COMPLEX_TYPE_FIELD_TYPE*)malloc(dest->value.edmComplexType.nMembers * sizeof(COMPLEX_TYPE_FIELD_TYPE)); + if (dest->value.edmComplexType.fields == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + for (i = 0; i < dest->value.edmComplexType.nMembers; i++) + { + dest->value.edmComplexType.fields[i].fieldName = NULL; + dest->value.edmComplexType.fields[i].value = NULL; + } + + for (i = 0; i < dest->value.edmComplexType.nMembers; i++) + { + /*copy the name of this field*/ + if (mallocAndStrcpy_s((char**)(&(dest->value.edmComplexType.fields[i].fieldName)), src->value.edmComplexType.fields[i].fieldName) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*field name copied success*/ + /*field value copy follows*/ + dest->value.edmComplexType.fields[i].value = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)); + if (dest->value.edmComplexType.fields[i].value == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(dest->value.edmComplexType.fields[i].value, src->value.edmComplexType.fields[i].value) != AGENT_DATA_TYPES_OK) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*all is fine*/ + } + } + } + } + + if (result != AGENT_DATA_TYPES_OK) + { + /*unbuild*/ + DestroyHalfBakedComplexType(dest); + } + } + } + break; + } /*ANY CHANGE (?!) here must be reflected in the tool providing the binary file (XML2BINARY) */ + } + + if (result != AGENT_DATA_TYPES_OK) + { + dest->type = EDM_NO_TYPE; + } + } + + return result; +} + +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_MemberPointers(AGENT_DATA_TYPE* agentData, const char* typeName, size_t nMembers, const char* const * memberNames, const AGENT_DATA_TYPE** memberPointerValues) +{ + AGENT_DATA_TYPES_RESULT result; + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_109:[ AGENT_DATA_TYPES_INVALID_ARG shall be returned if memberPointerValues parameter is NULL.] */ + if (memberPointerValues == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result: AGENT_DATA_TYPES_INVALID_ARG)"); + } + else + { + AGENT_DATA_TYPE* values = (AGENT_DATA_TYPE*)malloc(nMembers* sizeof(AGENT_DATA_TYPE)); + if (values == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t i; + for (i = 0; i < nMembers; i++) + { + if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(values + i, memberPointerValues[i]) != AGENT_DATA_TYPES_OK) + { + size_t j; + for (j = 0; j < i; j++) + { + Destroy_AGENT_DATA_TYPE(values + j); + } + break; + } + } + + if (i != nMembers) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /* SRS_AGENT_TYPE_SYSTEM_99_111:[ AGENT_DATA_TYPES_OK shall be returned upon success.] */ + result = Create_AGENT_DATA_TYPE_from_Members(agentData, typeName, nMembers, memberNames, values); + if (result != AGENT_DATA_TYPES_OK) + { + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + for (i = 0; i < nMembers; i++) + { + Destroy_AGENT_DATA_TYPE(&values[i]); + } + } + free(values); + } + } + return result; +} + +AGENT_DATA_TYPES_RESULT Create_AGENT_DATA_TYPE_from_Members(AGENT_DATA_TYPE* agentData, const char* typeName, size_t nMembers, const char* const * memberNames, const AGENT_DATA_TYPE* memberValues) +{ + AGENT_DATA_TYPES_RESULT result; + size_t i; + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_013:[ All the functions shall check their parameters for validity. When an invalid parameter is detected, the value AGENT_DATA_TYPES_INVALID_ARG shall be returned ]*/ + if (agentData == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result: AGENT_DATA_TYPES_INVALID_ARG)"); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_055:[ If typeName is NULL, the function shall return AGENT_DATA_TYPES_INVALID_ARG .]*/ + else if (typeName==NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result: AGENT_DATA_TYPES_INVALID_ARG)"); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_056:[If nMembers is 0, the function shall return AGENT_DATA_TYPES_INVALID_ARG .]*/ + else if (nMembers == 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_057:[ If memberNames is NULL, the function shall return AGENT_DATA_TYPES_INVALID_ARG .]*/ + else if (memberNames == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_058:[ If any of the memberNames[i] is NULL, the function shall return AGENT_DATA_TYPES_INVALID_ARG .]*/ + else if (isOneNameNULL(nMembers, memberNames)!=0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_059:[ If memberValues is NULL, the function shall return AGENT_DATA_TYPES_INVALID_ARG .]*/ + else if (memberValues == NULL) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_063:[ If there are two memberNames with the same name, then the function shall return AGENT_DATA_TYPES_INVALID_ARG.]*/ + else if (areThereTwoSameNames(nMembers, memberNames) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->value.edmComplexType.nMembers = nMembers; + agentData->value.edmComplexType.fields = (COMPLEX_TYPE_FIELD_TYPE*)malloc(nMembers *sizeof(COMPLEX_TYPE_FIELD_TYPE)); + if (agentData->value.edmComplexType.fields == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; /*not liking this, solution might be to use a temp variable*/ + + /*initialize the fields*/ + for (i = 0; i < nMembers; i++) + { + agentData->value.edmComplexType.fields[i].fieldName = NULL; + agentData->value.edmComplexType.fields[i].value = NULL; + } + + for (i = 0; i < nMembers; i++) + { + /*copy the name*/ + if (mallocAndStrcpy_s((char**)(&(agentData->value.edmComplexType.fields[i].fieldName)), memberNames[i]) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*field name was transferred successfully*/ + /*copy the rest*/ + agentData->value.edmComplexType.fields[i].value = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)); + if (agentData->value.edmComplexType.fields[i].value == NULL) + { + /*deallocate the name*/ + free((void*)(agentData->value.edmComplexType.fields[i].fieldName)); + agentData->value.edmComplexType.fields[i].fieldName = NULL; + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*copy the values*/ + if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(agentData->value.edmComplexType.fields[i].value, &(memberValues[i])) != AGENT_DATA_TYPES_OK) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + } + else + { + /*all is fine*/ + } + } + } + + } + } + + if (result != AGENT_DATA_TYPES_OK) + { + /*dealloc, something went bad*/ + DestroyHalfBakedComplexType(agentData); + } + else + { + agentData->type = EDM_COMPLEX_TYPE_TYPE; + } + } + return result; +} + +#define isLeapYear(y) ((((y) % 400) == 0) || (((y)%4==0)&&(!((y)%100==0)))) + +static int daysInAllPreviousMonths(int month) +{ + int result = 0; + for (int i = 0; i < month; i++) + { + switch (i) + { + case 0: + case 2: + case 4: + case 6: + case 7: + case 9: + result += 31; + break; + case 1: + result += 28; + break; + default: + result += 30; + } + } + return result; +} + +/*this function assumes a correctly filled in tm_year, tm_mon and tm_mday and will fill in tm_yday and tm_wday*/ +static void fill_tm_yday_and_tm_wday(struct tm* source) +{ + /*to fill in tm_yday the function shall add the number of days in every month, not including the current one*/ + /*and then it will add the number of days in the current month*/ + /*1st of Jan is day "0" in a year*/ + int year = source->tm_year + 1900 + 10000; + int nLeapYearsSinceYearMinus9999 = ((year - 1) / 4) - ((year - 1) / 100) + ((year - 1) / 400); + source->tm_yday = (daysInAllPreviousMonths(source->tm_mon)) + (source->tm_mday - 1) + ((source->tm_mon > 1 /*1 is Feb*/) && isLeapYear(year)); + source->tm_wday = ((365 * year + nLeapYearsSinceYearMinus9999) + source->tm_yday) % 7; + /*day "0" is 1st jan -9999 (this is how much odata can span*/ + /*the function shall count days */ +} + +/*the following function does the same as sscanf(pos2, ":%02d", &sec)*/ +/*this function only exists because of optimizing valgrind time, otherwise sscanf would be just as good*/ +static int sscanf2d(const char *pos2, int* sec) +{ + int result; + size_t position = 1; + if ( + (pos2[0] == ':') && + (scanAndReadNDigitsInt(pos2, &position, sec, 2) == 0) + ) + { + result = 1; + } + else + { + result = EOF; + } + + return result; + +} + +/*the following function does the same as sscanf(pos2, "%d", &sec)*/ +/*this function only exists because of optimizing valgrind time, otherwise sscanf would be just as good*/ +static int sscanfd(const char *src, int* dst) +{ + int result; + char* next; + long int temp = strtol(src, &next, 10); + if ((src == next) || (((temp == LONG_MAX) || (temp == LONG_MIN)) && (errno != 0))) + { + result = EOF; + } + else + { + (*dst) = (int)temp; + result = 1; + } + return result; +} + +/*the following function does the same as sscanf(src, "%llu", &dst), but, it changes the src pointer.*/ +static int sscanfllu(const char** src, unsigned long long* dst) +{ + int result = 1; + char* next; + (*dst) = strtoull((*src), &next, 10); + if (((*src) == (const char*)next) || (((*dst) == ULLONG_MAX) && (errno != 0))) + { + result = EOF; + } + (*src) = (const char*)next; + return result; +} + +/*the following function does the same as sscanf(src, ".%llu", &dst)*/ +static int sscanfdotllu(const char*src, unsigned long long* dst) +{ + int result; + + if ((*src) != '.') + { + /*doesn't start with '.' error out*/ + result = EOF; + } + else + { + src++; + result = sscanfllu(&src, dst); + } + + return result; +} + +/*the following function does the same as sscanf(src, "%u", &dst)*/ +static int sscanfu(const char* src, uint32_t* dst) +{ + int result; + char* next; + unsigned long int temp = strtoul(src, &next, 10); + if ((src == next) || ((temp == ULONG_MAX) && (errno != 0))) + { + result = EOF; + } + else + { + result = 1; + (*dst) = (uint32_t)temp; + } + return result; +} + +/*the following function does the same as sscanf(src, "%f", &dst)*/ +static int sscanff(const char*src, float* dst) +{ + int result = 1; + char* next; + (*dst) = strtof(src, &next); + if ((src == next) || (((*dst) == HUGE_VALF) && (errno != 0))) + { + result = EOF; + } + return result; +} + +/*the following function does the same as sscanf(src, "%lf", &dst)*/ +static int sscanflf(const char*src, double* dst) +{ + int result = 1; + char* next; + (*dst) = strtod(src, &next); + if ((src == next) || (((*dst) == HUGE_VALL) && (errno != 0))) + { + result = EOF; + } + return result; +} + + +/*the below function replaces sscanf(src, "%03d:%02d\"", &hourOffset, &minOffset)*/ +/*return 2 if success*/ + +static int sscanf3d2d(const char* src, int* hourOffset, int* minOffset) +{ + size_t position = 0; + int result = EOF; + bool isNegative = false; + + if (*src == '+') + { + position++; + } + else if (*src == '-') + { + isNegative = true; + position++; + } + + if ( + (scanAndReadNDigitsInt(src, &position, hourOffset, (3-position)) == 0) && + (src[position++] == ':') && + (scanAndReadNDigitsInt(src, &position, minOffset, 2) == 0) + ) + { + result = 2; + } + else + { + result = 0; + } + if (isNegative) + *hourOffset *= -1; + + return result; +} + +AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGENT_DATA_TYPE_TYPE type, AGENT_DATA_TYPE* agentData) +{ + + AGENT_DATA_TYPES_RESULT result; + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_073:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is NULL.] */ + if ((source == NULL) || + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_074:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if agentData is NULL.] */ + (agentData == NULL)) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_071:[ CreateAgentDataType_From_String shall create an AGENT_DATA_TYPE from a char* representation of the type indicated by type parameter.] */ + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_072:[ The implementation for the transformation of the char* source into AGENT_DATA_TYPE shall be type specific.] */ + switch (type) + { + default: + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_075:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_NOT_IMPLEMENTED if type is not a supported type.] */ + result = AGENT_DATA_TYPES_NOT_IMPLEMENTED; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + break; + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_086:[ EDM_STRING] */ + case EDM_BOOLEAN_TYPE: + { + if (strcmp(source, "true") == 0) + { + agentData->type = EDM_BOOLEAN_TYPE; + agentData->value.edmBoolean.value = EDM_TRUE; + result = AGENT_DATA_TYPES_OK; + } + else if (strcmp(source, "false") == 0) + { + agentData->type = EDM_BOOLEAN_TYPE; + agentData->value.edmBoolean.value = EDM_FALSE; + result = AGENT_DATA_TYPES_OK; + } + else + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + + break; + } + case EDM_NULL_TYPE: + { + if (strcmp(source, "null") == 0) + { + agentData->type = EDM_NULL_TYPE; + result = AGENT_DATA_TYPES_OK; + } + else + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_084:[ EDM_SBYTE] */ + case EDM_SBYTE_TYPE: + { + int sByteValue; + if ((sscanfd(source, &sByteValue) != 1) || + (sByteValue < -128) || + (sByteValue > 127)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_SBYTE_TYPE; + agentData->value.edmSbyte.value = (int8_t)sByteValue; + result = AGENT_DATA_TYPES_OK; + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_077:[ EDM_BYTE] */ + case EDM_BYTE_TYPE: + { + int byteValue; + if ((sscanfd(source, &byteValue) != 1) || + (byteValue < 0) || + (byteValue > 255)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_BYTE_TYPE; + agentData->value.edmByte.value = (uint8_t)byteValue; + result = AGENT_DATA_TYPES_OK; + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_081:[ EDM_INT16] */ + case EDM_INT16_TYPE: + { + int int16Value; + if ((sscanfd(source, &int16Value) != 1) || + (int16Value < -32768) || + (int16Value > 32767)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_INT16_TYPE; + agentData->value.edmInt16.value = (int16_t)int16Value; + result = AGENT_DATA_TYPES_OK; + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_082:[ EDM_INT32] */ + case EDM_INT32_TYPE: + { + int32_t int32Value; + unsigned char isNegative; + uint32_t uint32Value; + const char* pos; + size_t strLength; + + if (source[0] == '-') + { + isNegative = 1; + pos = &source[1]; + } + else + { + isNegative = 0; + pos = &source[0]; + } + + strLength = strlen(source); + + if ((sscanfu(pos, &uint32Value) != 1) || + (strLength > 11) || + ((uint32Value > 2147483648UL) && isNegative) || + ((uint32Value > 2147483647UL) && (!isNegative))) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (isNegative) + { + if (uint32Value == 2147483648UL) + { + int32Value = -2147483647L - 1L; + } + else + { + int32Value = -(int32_t)uint32Value; + } + } + else + { + int32Value = uint32Value; + } + + agentData->type = EDM_INT32_TYPE; + agentData->value.edmInt32.value = (int32_t)int32Value; + result = AGENT_DATA_TYPES_OK; + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_083:[ EDM_INT64] */ + case EDM_INT64_TYPE: + { + long long int64Value; + unsigned char isNegative; + unsigned long long ullValue; + const char* pos; + size_t strLength; + + if (source[0] == '-') + { + isNegative = 1; + pos = &source[1]; + } + else + { + isNegative = 0; + pos = &source[0]; + } + + strLength = strlen(source); + + if ((sscanfllu(&pos, &ullValue) != 1) || + (strLength > 20) || + ((ullValue > 9223372036854775808ULL) && isNegative) || + ((ullValue > 9223372036854775807ULL) && (!isNegative))) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (isNegative) + { + if (ullValue == 9223372036854775808ULL) + { + int64Value = -9223372036854775807LL - 1LL; + } + else + { + int64Value = -(long long)ullValue; + } + } + else + { + int64Value = ullValue; + } + agentData->type = EDM_INT64_TYPE; + agentData->value.edmInt64.value = (int64_t)int64Value; + result = AGENT_DATA_TYPES_OK; + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_085:[ EDM_DATE] */ + case EDM_DATE_TYPE: + { + int year; + int month; + int day; + size_t strLength = strlen(source); + + if ((strLength < 2) || + (source[0] != '"') || + (source[strLength - 1] != '"')) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t pos = 1; + int sign; + scanOptionalMinusSign(source, 2, &pos, &sign); + + if ((scanAndReadNDigitsInt(source, &pos, &year, 4) != 0) || + (source[pos++] != '-') || + (scanAndReadNDigitsInt(source, &pos, &month, 2) != 0) || + (source[pos++] != '-') || + (scanAndReadNDigitsInt(source, &pos, &day, 2) != 0) || + (Create_AGENT_DATA_TYPE_from_date(agentData, (int16_t)(sign*year), (uint8_t)month, (uint8_t)day) != AGENT_DATA_TYPES_OK)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_078:[ EDM_DATETIMEOFFSET] */ + case EDM_DATE_TIME_OFFSET_TYPE: + { + int year; + int month; + int day; + int hour; + int min; + int sec = 0; + int hourOffset; + int minOffset; + unsigned long long fractionalSeconds = 0; + size_t strLength = strlen(source); + + agentData->value.edmDateTimeOffset.hasFractionalSecond = 0; + agentData->value.edmDateTimeOffset.hasTimeZone = 0; + /* The value of tm_isdst is positive if Daylight Saving Time is in effect, zero if Daylight + Saving Time is not in effect, and negative if the information is not available.*/ + agentData->value.edmDateTimeOffset.dateTime.tm_isdst = -1; + + if ((strLength < 2) || + (source[0] != '"') || + (source[strLength - 1] != '"')) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + size_t pos = 1; + int sign; + scanOptionalMinusSign(source, 2, &pos, &sign); + + if ((scanAndReadNDigitsInt(source, &pos, &year, 4) != 0) || + (source[pos++] != '-') || + (scanAndReadNDigitsInt(source, &pos, &month, 2) != 0) || + (source[pos++] != '-') || + (scanAndReadNDigitsInt(source, &pos, &day, 2) != 0) || + (source[pos++] != 'T') || + (scanAndReadNDigitsInt(source, &pos, &hour, 2) != 0) || + (source[pos++] != ':') || + (scanAndReadNDigitsInt(source, &pos, &min, 2) != 0)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + const char* pos2; + year = year*sign; + if ((pos2 = strchr(source, ':')) == NULL) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + pos2 += 3; + if (*pos2 == ':') + { + if (sscanf2d(pos2, &sec) != 1) + { + pos2 = NULL; + } + else + { + pos2 += 3; + } + } + + if ((pos2 != NULL) && + (*pos2 == '.')) + { + if (sscanfdotllu(pos2, &fractionalSeconds) != 1) + { + pos2 = NULL; + } + else + { + pos2++; + + agentData->value.edmDateTimeOffset.hasFractionalSecond = 1; + + while ((*pos2 != '\0') && + (IS_DIGIT(*pos2))) + { + pos2++; + } + + if (*pos2 == '\0') + { + pos2 = NULL; + } + } + } + + if (pos2 == NULL) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + hourOffset = 0; + minOffset = 0; + + if (sscanf3d2d(pos2, &hourOffset, &minOffset) == 2) + { + agentData->value.edmDateTimeOffset.hasTimeZone = 1; + } + + if ((strcmp(pos2, "Z\"") == 0) || + agentData->value.edmDateTimeOffset.hasTimeZone) + { + if ((ValidateDate(year, month, day) != 0) || + (hour < 0) || + (hour > 23) || + (min < 0) || + (min > 59) || + (sec < 0) || + (sec > 59) || + (fractionalSeconds > 999999999999) || + (hourOffset < -23) || + (hourOffset > 23) || + (minOffset < 0) || + (minOffset > 59)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_DATE_TIME_OFFSET_TYPE; + agentData->value.edmDateTimeOffset.dateTime.tm_year= year-1900; + agentData->value.edmDateTimeOffset.dateTime.tm_mon = month-1; + agentData->value.edmDateTimeOffset.dateTime.tm_mday = day; + agentData->value.edmDateTimeOffset.dateTime.tm_hour = hour; + agentData->value.edmDateTimeOffset.dateTime.tm_min = min; + agentData->value.edmDateTimeOffset.dateTime.tm_sec = sec; + /*fill in tm_wday and tm_yday*/ + fill_tm_yday_and_tm_wday(&agentData->value.edmDateTimeOffset.dateTime); + agentData->value.edmDateTimeOffset.fractionalSecond = (uint64_t)fractionalSeconds; + agentData->value.edmDateTimeOffset.timeZoneHour = (int8_t)hourOffset; + agentData->value.edmDateTimeOffset.timeZoneMinute = (uint8_t)minOffset; + result = AGENT_DATA_TYPES_OK; + } + } + else + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + } + } + } + } + + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_080:[ EDM_DOUBLE] */ + case EDM_DOUBLE_TYPE: + { + if (strcmp(source, "\"NaN\"") == 0) + { + agentData->type = EDM_DOUBLE_TYPE; + agentData->value.edmDouble.value = NAN; + result = AGENT_DATA_TYPES_OK; + } + else if (strcmp(source, "\"INF\"") == 0) + { + agentData->type = EDM_DOUBLE_TYPE; + agentData->value.edmDouble.value = INFINITY; + result = AGENT_DATA_TYPES_OK; + } + else if (strcmp(source, "\"-INF\"") == 0) + { + agentData->type = EDM_DOUBLE_TYPE; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4056) /* Known warning for INIFNITY */ +#endif + agentData->value.edmDouble.value = -INFINITY; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + result = AGENT_DATA_TYPES_OK; + } + else if (sscanflf(source, &(agentData->value.edmDouble.value)) != 1) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + agentData->type = EDM_DOUBLE_TYPE; + result = AGENT_DATA_TYPES_OK; + } + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_089:[EDM_SINGLE] */ + case EDM_SINGLE_TYPE: + { + if (strcmp(source, "\"NaN\"") == 0) + { + agentData->type = EDM_SINGLE_TYPE; + agentData->value.edmSingle.value = NAN; + result = AGENT_DATA_TYPES_OK; + } + else if (strcmp(source, "\"INF\"") == 0) + { + agentData->type = EDM_SINGLE_TYPE; + agentData->value.edmSingle.value = INFINITY; + result = AGENT_DATA_TYPES_OK; + } + else if (strcmp(source, "\"-INF\"") == 0) + { + agentData->type = EDM_SINGLE_TYPE; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4056) /* Known warning for INIFNITY */ +#endif + agentData->value.edmSingle.value = -INFINITY; +#ifdef _MSC_VER +#pragma warning(pop) +#endif +result = AGENT_DATA_TYPES_OK; + } + else if (sscanff(source, &agentData->value.edmSingle.value) != 1) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_SINGLE_TYPE; + result = AGENT_DATA_TYPES_OK; + } + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_079:[ EDM_DECIMAL] */ + case EDM_DECIMAL_TYPE: + { + size_t strLength = strlen(source); + if ((strLength < 2) || + (source[0] != '"') || + (source[strLength - 1] != '"') || + (ValidateDecimal(source + 1, strLength - 2) != 0)) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_DECIMAL_TYPE; + agentData->value.edmDecimal.value = STRING_construct_n(source + 1, strLength-2); + if (agentData->value.edmDecimal.value == NULL) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_088:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_ERROR if any other error occurs.] */ + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + } + break; + } + + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_086:[ EDM_STRING] */ + case EDM_STRING_TYPE: + { + size_t strLength = strlen(source); + if ((strLength < 2) || + (source[0] != '"') || + (source[strLength - 1] != '"')) + { + /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ + result = AGENT_DATA_TYPES_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + char* temp; + if ((temp = (char*)malloc(strLength - 1)) == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (strncpy_s(temp, strLength - 1, source + 1, strLength - 2) != 0) + { + free(temp); + + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + temp[strLength - 2] = 0; + + agentData->type = EDM_STRING_TYPE; + agentData->value.edmString.chars = temp; + agentData->value.edmString.length = strLength - 2; + result = AGENT_DATA_TYPES_OK; + } + } + break; + } + /* Codes_SRS_AGENT_TYPE_SYSTEM_01_004: [EDM_STRING_NO_QUOTES] */ + case EDM_STRING_NO_QUOTES_TYPE: + { + char* temp; + size_t strLength = strlen(source); + if (mallocAndStrcpy_s(&temp, source) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + agentData->type = EDM_STRING_NO_QUOTES_TYPE; + agentData->value.edmStringNoQuotes.chars = temp; + agentData->value.edmStringNoQuotes.length = strLength; + result = AGENT_DATA_TYPES_OK; + } + break; + } + /*Codes_SRS_AGENT_TYPE_SYSTEM_99_097:[ EDM_GUID]*/ + case EDM_GUID_TYPE: + { + if (strlen(source) != GUID_STRING_LENGTH) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + if (source[0] != '"') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 1, &(agentData->value.edmGuid.GUID[0])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 3, &(agentData->value.edmGuid.GUID[1])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 5, &(agentData->value.edmGuid.GUID[2])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 7, &(agentData->value.edmGuid.GUID[3])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (source[9] != '-') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 10, &(agentData->value.edmGuid.GUID[4])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 12, &(agentData->value.edmGuid.GUID[5])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (source[14] != '-') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 15, &(agentData->value.edmGuid.GUID[6])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 17, &(agentData->value.edmGuid.GUID[7])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (source[19] != '-') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 20, &(agentData->value.edmGuid.GUID[8])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 22, &(agentData->value.edmGuid.GUID[9])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (source[24] != '-') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 25, &(agentData->value.edmGuid.GUID[10])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 27, &(agentData->value.edmGuid.GUID[11])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 29, &(agentData->value.edmGuid.GUID[12])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 31, &(agentData->value.edmGuid.GUID[13])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 33, &(agentData->value.edmGuid.GUID[14])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (scanMandatory2CapitalHexDigits(source + 35, &(agentData->value.edmGuid.GUID[15])) != 0) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (source[37] != '"') + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + agentData->type = EDM_GUID_TYPE; + result = AGENT_DATA_TYPES_OK; + } + } + break; + } + case EDM_BINARY_TYPE: + { + size_t sourceLength = strlen(source); + if (sourceLength < 2) + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (sourceLength == 2) + { + agentData->type = EDM_BINARY_TYPE; + agentData->value.edmBinary.data = NULL; + agentData->value.edmBinary.size = 0; + result = AGENT_DATA_TYPES_OK; + } + else + { + size_t sourcePosition = 0; + if (source[sourcePosition++] != '"') /*if it doesn't start with a quote then... */ + { + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + /*1. read groups of 4 base64 character and transfer those into groups of 3 "normal" characters. + 2. read the end of the string and produce from that the ending characters*/ + + /*compute the amount of memory to allocate*/ + agentData->value.edmBinary.size = (((sourceLength - 2) + 4) / 4) * 3; /*this is overallocation, shall be trimmed later*/ + agentData->value.edmBinary.data = (unsigned char*)malloc(agentData->value.edmBinary.size); /*this is overallocation, shall be trimmed later*/ + if (agentData->value.edmBinary.data == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + } + else + { + + size_t destinationPosition = 0; + size_t consumed; + /*read and store "solid" groups of 4 base64 chars*/ + while (scan4base64char(source + sourcePosition, sourceLength - sourcePosition, agentData->value.edmBinary.data + destinationPosition, agentData->value.edmBinary.data + destinationPosition + 1, agentData->value.edmBinary.data + destinationPosition + 2) == 0) + { + sourcePosition += 4; + destinationPosition += 3; + } + + if (scanbase64b16(source + sourcePosition, sourceLength - sourcePosition, &consumed, agentData->value.edmBinary.data + destinationPosition, agentData->value.edmBinary.data + destinationPosition + 1) == 0) + { + sourcePosition += consumed; + destinationPosition += 2; + + } + else if (scanbase64b8(source + sourcePosition, sourceLength - sourcePosition, &consumed, agentData->value.edmBinary.data + destinationPosition) == 0) + { + sourcePosition += consumed; + destinationPosition += 1; + } + + if (source[sourcePosition++] != '"') /*if it doesn't end with " then bail out*/ + { + free(agentData->value.edmBinary.data); + agentData->value.edmBinary.data = NULL; + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else if (sourcePosition != sourceLength) + { + free(agentData->value.edmBinary.data); + agentData->value.edmBinary.data = NULL; + result = AGENT_DATA_TYPES_INVALID_ARG; + } + else + { + /*trim the result*/ + void* temp = realloc(agentData->value.edmBinary.data, destinationPosition); + if (temp == NULL) /*this is extremely unlikely to happen, but whatever*/ + { + free(agentData->value.edmBinary.data); + agentData->value.edmBinary.data = NULL; + result = AGENT_DATA_TYPES_ERROR; + } + else + { + agentData->type = EDM_BINARY_TYPE; + agentData->value.edmBinary.data = (unsigned char*)temp; + agentData->value.edmBinary.size = destinationPosition; + result = AGENT_DATA_TYPES_OK; + } + } + } + } + } + break; + } + } + } + + return result; +} + +// extern AGENT_DATA_TYPES_RESULT AgentDataType_GetComplexTypeField(AGENT_DATA_TYPE* agentData, size_t index, COMPLEX_TYPE_FIELD_TYPE* complexField); +COMPLEX_TYPE_FIELD_TYPE* AgentDataType_GetComplexTypeField(AGENT_DATA_TYPE* agentData, size_t index) +{ + COMPLEX_TYPE_FIELD_TYPE* complexField = NULL; + if (agentData == NULL) + { + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); + } + else + { + if (agentData->type != EDM_COMPLEX_TYPE_TYPE) + { + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); + } + + else + { + if (index >= agentData->value.edmComplexType.nMembers) + { + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); + } + else + { + complexField = (COMPLEX_TYPE_FIELD_TYPE*)malloc(sizeof(COMPLEX_TYPE_FIELD_TYPE)); + if (complexField == NULL) + { + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_ERROR)); + } + else + { + *complexField = agentData->value.edmComplexType.fields[index]; + } + } + } + } + return complexField; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/codefirst.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1625 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdarg.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "codefirst.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include <stddef.h> +#include "azure_c_shared_utility/crt_abstractions.h" +#include "iotdevice.h" + +DEFINE_ENUM_STRINGS(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES) +DEFINE_ENUM_STRINGS(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_RESULT_VALUES) + +#define LOG_CODEFIRST_ERROR \ + LogError("(result = %s)", ENUM_TO_STRING(CODEFIRST_RESULT, result)) + +typedef struct DEVICE_HEADER_DATA_TAG +{ + DEVICE_HANDLE DeviceHandle; + const REFLECTED_DATA_FROM_DATAPROVIDER* ReflectedData; + SCHEMA_MODEL_TYPE_HANDLE ModelHandle; + size_t DataSize; + unsigned char* data; +} DEVICE_HEADER_DATA; + +#define COUNT_OF(A) (sizeof(A) / sizeof((A)[0])) + +/*design considerations for lazy init of CodeFirst: + There are 2 main states: either CodeFirst is in an initialized state, or it is not initialized. + The initialized state means there's a g_OverrideSchemaNamespace set (even when it is set to NULL). + The uninitialized state means g_OverrideSchemaNamespace is not set. + + To switch to Init state, either call CodeFirst_Init (that sets g_OverrideSchemaNamespace to something) + or call directly an API (that will set automatically g_OverrideSchemaNamespace to NULL). + + To switch to NOT INIT state, depending on the method used to initialize: + - if CodeFirst_Init was called, then only by a call to CodeFirst_Deinit the switch will take place + (note how in this case g_OverrideSchemaNamespace survives destruction of all devices). + - if the init has been done "lazily" by an API call then the module returns to uninitialized state + when the number of devices reaches zero. + + +-----------------------------+ + Start +---------------->| | + | NOT INIT | + +---------------->| | + | +------------+----------------+ + | | + | | + | | _Init | APIs + | | + | v + | +---------------------------------------------------+ + | | Init State | + | | +-----------------+ +-----------------+ | + | | | | | | | + | | | init by _Init | | init by API | | + | | +------+----------+ +---------+-------+ | + | | | | | + | | |_DeInit | nDevices==0 | + | | | | | + | +--------v----------------+-----------v-------------+ + | | + +-------------------------------+ + +*/ + + +#define CODEFIRST_STATE_VALUES \ + CODEFIRST_STATE_NOT_INIT, \ + CODEFIRST_STATE_INIT_BY_INIT, \ + CODEFIRST_STATE_INIT_BY_API + +DEFINE_ENUM(CODEFIRST_STATE, CODEFIRST_STATE_VALUES) + +static CODEFIRST_STATE g_state = CODEFIRST_STATE_NOT_INIT; +static const char* g_OverrideSchemaNamespace; +static size_t g_DeviceCount = 0; +static DEVICE_HEADER_DATA** g_Devices = NULL; + +static void deinitializeDesiredProperties(SCHEMA_MODEL_TYPE_HANDLE model, void* destination) +{ + size_t nDesiredProperties; + if (Schema_GetModelDesiredPropertyCount(model, &nDesiredProperties) != SCHEMA_OK) + { + LogError("unexpected error in Schema_GetModelDesiredPropertyCount"); + } + else + { + size_t nProcessedDesiredProperties = 0; + for (size_t i = 0;i < nDesiredProperties;i++) + { + SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = Schema_GetModelDesiredPropertyByIndex(model, i); + if (desiredPropertyHandle == NULL) + { + LogError("unexpected error in Schema_GetModelDesiredPropertyByIndex"); + i = nDesiredProperties; + } + else + { + pfDesiredPropertyDeinitialize desiredPropertyDeinitialize = Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize(desiredPropertyHandle); + if (desiredPropertyDeinitialize == NULL) + { + LogError("unexpected error in Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize"); + i = nDesiredProperties; + } + else + { + size_t offset = Schema_GetModelDesiredProperty_offset(desiredPropertyHandle); + desiredPropertyDeinitialize((char*)destination + offset); + nProcessedDesiredProperties++; + } + } + } + + if (nDesiredProperties == nProcessedDesiredProperties) + { + /*recursively go in the model and initialize the other fields*/ + size_t nModelInModel; + if (Schema_GetModelModelCount(model, &nModelInModel) != SCHEMA_OK) + { + LogError("unexpected error in Schema_GetModelModelCount"); + } + else + { + size_t nProcessedModelInModel = 0; + for (size_t i = 0; i < nModelInModel;i++) + { + SCHEMA_MODEL_TYPE_HANDLE modelInModel = Schema_GetModelModelyByIndex(model, i); + if (modelInModel == NULL) + { + LogError("unexpected failure in Schema_GetModelModelyByIndex"); + i = nModelInModel; + } + else + { + size_t offset = Schema_GetModelModelByIndex_Offset(model, i); + deinitializeDesiredProperties(modelInModel, (char*)destination + offset); + nProcessedModelInModel++; + } + } + + if (nProcessedModelInModel == nModelInModel) + { + /*all is fine... */ + } + } + } + } +} + +static void DestroyDevice(DEVICE_HEADER_DATA* deviceHeader) +{ + /* Codes_SRS_CODEFIRST_99_085:[CodeFirst_DestroyDevice shall free all resources associated with a device.] */ + /* Codes_SRS_CODEFIRST_99_087:[In order to release the device handle, CodeFirst_DestroyDevice shall call Device_Destroy.] */ + + Device_Destroy(deviceHeader->DeviceHandle); + free(deviceHeader->data); + free(deviceHeader); +} + +static CODEFIRST_RESULT buildStructTypes(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData) +{ + CODEFIRST_RESULT result = CODEFIRST_OK; + + const REFLECTED_SOMETHING* something; + for (something = reflectedData->reflectedData; something != NULL; something = something->next) + { + if (something->type == REFLECTION_STRUCT_TYPE) + { + SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle; + structTypeHandle = Schema_CreateStructType(schemaHandle, something->what.structure.name); + + if (structTypeHandle == NULL) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("create struct failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + break; + } + else + { + const REFLECTED_SOMETHING* maybeField; + /*look for the field... */ + for (maybeField = reflectedData->reflectedData; maybeField != NULL; maybeField = maybeField->next) + { + if (maybeField->type == REFLECTION_FIELD_TYPE) + { + if (strcmp(maybeField->what.field.structName, something->what.structure.name) == 0) + { + if (Schema_AddStructTypeProperty(structTypeHandle, maybeField->what.field.fieldName, maybeField->what.field.fieldType) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add struct property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + break; + } + } + } + } + } + } + } + + return result; +} + +static CODEFIRST_RESULT buildModel(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData, const REFLECTED_SOMETHING* modelReflectedData) +{ + CODEFIRST_RESULT result = CODEFIRST_OK; + const REFLECTED_SOMETHING* something; + SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle; + + modelTypeHandle = Schema_GetModelByName(schemaHandle, modelReflectedData->what.model.name); + if (modelTypeHandle == NULL) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("cannot get model %s %s", modelReflectedData->what.model.name, ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + + for (something = reflectedData->reflectedData; something != NULL; something = something->next) + { + /* looking for all elements that belong to a model: properties and actions */ + if ((something->type == REFLECTION_PROPERTY_TYPE) && + (strcmp(something->what.property.modelName, modelReflectedData->what.model.name) == 0)) + { + SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.property.type); + + /* if this is a model type use the appropriate APIs for it */ + if (childModelHande != NULL) + { + if (Schema_AddModelModel(modelTypeHandle, something->what.property.name, childModelHande, something->what.property.offset, NULL) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + else + { + if (Schema_AddModelProperty(modelTypeHandle, something->what.property.name, something->what.property.type) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + + if ((something->type == REFLECTION_REPORTED_PROPERTY_TYPE) && + (strcmp(something->what.reportedProperty.modelName, modelReflectedData->what.model.name) == 0)) + { + SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.reportedProperty.type); + + /* if this is a model type use the appropriate APIs for it */ + if (childModelHande != NULL) + { + if (Schema_AddModelModel(modelTypeHandle, something->what.reportedProperty.name, childModelHande, something->what.reportedProperty.offset, NULL) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + else + { + if (Schema_AddModelReportedProperty(modelTypeHandle, something->what.reportedProperty.name, something->what.reportedProperty.type) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add reported property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + + if ((something->type == REFLECTION_DESIRED_PROPERTY_TYPE) && + (strcmp(something->what.desiredProperty.modelName, modelReflectedData->what.model.name) == 0)) + { + SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.desiredProperty.type); + + /* if this is a model type use the appropriate APIs for it */ + if (childModelHande != NULL) + { + if (Schema_AddModelModel(modelTypeHandle, something->what.desiredProperty.name, childModelHande, something->what.desiredProperty.offset, something->what.desiredProperty.onDesiredProperty) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + else + { + if (Schema_AddModelDesiredProperty(modelTypeHandle, + something->what.desiredProperty.name, + something->what.desiredProperty.type, + something->what.desiredProperty.FromAGENT_DATA_TYPE, + something->what.desiredProperty.desiredPropertInitialize, + something->what.desiredProperty.desiredPropertDeinitialize, + something->what.desiredProperty.offset, + something->what.desiredProperty.onDesiredProperty) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add desired property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + + if ((something->type == REFLECTION_ACTION_TYPE) && + (strcmp(something->what.action.modelName, modelReflectedData->what.model.name) == 0)) + { + SCHEMA_ACTION_HANDLE actionHandle; + size_t i; + + if ((actionHandle = Schema_CreateModelAction(modelTypeHandle, something->what.action.name)) == NULL) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model action failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + + for (i = 0; i < something->what.action.nArguments; i++) + { + if (Schema_AddModelActionArgument(actionHandle, something->what.action.arguments[i].name, something->what.action.arguments[i].type) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model action argument failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + + if ((something->type == REFLECTION_METHOD_TYPE) && + (strcmp(something->what.method.modelName, modelReflectedData->what.model.name) == 0)) + { + SCHEMA_METHOD_HANDLE methodHandle; + size_t i; + + if ((methodHandle = Schema_CreateModelMethod(modelTypeHandle, something->what.method.name)) == NULL) + { + /*Codes_SRS_CODEFIRST_99_076: [ If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL. ]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model method failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + + for (i = 0; i < something->what.method.nArguments; i++) + { + if (Schema_AddModelMethodArgument(methodHandle, something->what.method.arguments[i].name, something->what.method.arguments[i].type) != SCHEMA_OK) + { + /*Codes_SRS_CODEFIRST_99_076: [ If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL. ]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("add model method argument failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + } + +out: + return result; +} + +static CODEFIRST_RESULT buildModelTypes(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData) +{ + CODEFIRST_RESULT result = CODEFIRST_OK; + const REFLECTED_SOMETHING* something; + + /* first have a pass and add all the model types */ + for (something = reflectedData->reflectedData; something != NULL; something = something->next) + { + if (something->type == REFLECTION_MODEL_TYPE) + { + if (Schema_CreateModelType(schemaHandle, something->what.model.name) == NULL) + { + /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/ + result = CODEFIRST_SCHEMA_ERROR; + LogError("create model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + goto out; + } + } + } + + for (something = reflectedData->reflectedData; something != NULL; something = something->next) + { + if (something->type == REFLECTION_MODEL_TYPE) + { + result = buildModel(schemaHandle, reflectedData, something); + if (result != CODEFIRST_OK) + { + break; + } + } + } + +out: + return result; +} + +static CODEFIRST_RESULT CodeFirst_Init_impl(const char* overrideSchemaNamespace, bool calledFromCodeFirst_Init) +{ + /*shall build the default EntityContainer*/ + CODEFIRST_RESULT result; + + if (g_state != CODEFIRST_STATE_NOT_INIT) + { + /*Codes_SRS_CODEFIRST_99_003:[ If the module is already initialized, the initialization shall fail and the return value shall be CODEFIRST_ALREADY_INIT.]*/ + result = CODEFIRST_ALREADY_INIT; + if(calledFromCodeFirst_Init) /*do not log this error when APIs attempt lazy init*/ + { + LogError("CodeFirst was already init %s", ENUM_TO_STRING(CODEFIRST_RESULT, result)); + } + } + else + { + g_DeviceCount = 0; + g_OverrideSchemaNamespace = overrideSchemaNamespace; + g_Devices = NULL; + + /*Codes_SRS_CODEFIRST_99_002:[ CodeFirst_Init shall initialize the CodeFirst module. If initialization is successful, it shall return CODEFIRST_OK.]*/ + g_state = calledFromCodeFirst_Init? CODEFIRST_STATE_INIT_BY_INIT: CODEFIRST_STATE_INIT_BY_API; + result = CODEFIRST_OK; + } + return result; +} + +/*Codes_SRS_CODEFIRST_99_002:[ CodeFirst_Init shall initialize the CodeFirst module. If initialization is successful, it shall return CODEFIRST_OK.]*/ +CODEFIRST_RESULT CodeFirst_Init(const char* overrideSchemaNamespace) +{ + return CodeFirst_Init_impl(overrideSchemaNamespace, true); +} + +void CodeFirst_Deinit(void) +{ + /*Codes_SRS_CODEFIRST_99_006:[If the module is not previously initialed, CodeFirst_Deinit shall do nothing.]*/ + if (g_state != CODEFIRST_STATE_INIT_BY_INIT) + { + LogError("CodeFirst_Deinit called when CodeFirst was not initialized by CodeFirst_Init"); + } + else + { + size_t i; + + /*Codes_SRS_CODEFIRST_99_005:[ CodeFirst_Deinit shall deinitialize the module, freeing all the resources and placing the module in an uninitialized state.]*/ + for (i = 0; i < g_DeviceCount; i++) + { + DestroyDevice(g_Devices[i]); + } + + free(g_Devices); + g_Devices = NULL; + g_DeviceCount = 0; + + g_state = CODEFIRST_STATE_NOT_INIT; + } +} + +static const REFLECTED_SOMETHING* FindModelInCodeFirstMetadata(const REFLECTED_SOMETHING* reflectedData, const char* modelName) +{ + const REFLECTED_SOMETHING* result; + + for (result = reflectedData; result != NULL; result = result->next) + { + if ((result->type == REFLECTION_MODEL_TYPE) && + (strcmp(result->what.model.name, modelName) == 0)) + { + /* found model type */ + break; + } + } + + return result; +} + +static const REFLECTED_SOMETHING* FindChildModelInCodeFirstMetadata(const REFLECTED_SOMETHING* reflectedData, const REFLECTED_SOMETHING* startModel, const char* relativePath, size_t* offset) +{ + const REFLECTED_SOMETHING* result = startModel; + *offset = 0; + + /* Codes_SRS_CODEFIRST_99_139:[If the relativeActionPath is empty then the action shall be looked up in the device model.] */ + while ((*relativePath != 0) && (result != NULL)) + { + /* Codes_SRS_CODEFIRST_99_142:[The relativeActionPath argument shall be in the format "childModel1/childModel2/.../childModelN".] */ + const REFLECTED_SOMETHING* childModelProperty; + size_t propertyNameLength; + const char* slashPos = strchr(relativePath, '/'); + if (slashPos == NULL) + { + slashPos = &relativePath[strlen(relativePath)]; + } + + propertyNameLength = slashPos - relativePath; + + for (childModelProperty = reflectedData; childModelProperty != NULL; childModelProperty = childModelProperty->next) + { + if ((childModelProperty->type == REFLECTION_PROPERTY_TYPE) && + (strcmp(childModelProperty->what.property.modelName, result->what.model.name) == 0) && + (strncmp(childModelProperty->what.property.name, relativePath, propertyNameLength) == 0) && + (strlen(childModelProperty->what.property.name) == propertyNameLength)) + { + /* property found, now let's find the model */ + /* Codes_SRS_CODEFIRST_99_140:[CodeFirst_InvokeAction shall pass to the action wrapper that it calls a pointer to the model where the action is defined.] */ + *offset += childModelProperty->what.property.offset; + break; + } + } + + if (childModelProperty == NULL) + { + /* not found */ + result = NULL; + } + else + { + result = FindModelInCodeFirstMetadata(reflectedData, childModelProperty->what.property.type); + } + + relativePath = slashPos; + } + + return result; +} + +EXECUTE_COMMAND_RESULT CodeFirst_InvokeAction(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeActionPath, const char* actionName, size_t parameterCount, const AGENT_DATA_TYPE* parameterValues) +{ + EXECUTE_COMMAND_RESULT result; + DEVICE_HEADER_DATA* deviceHeader = (DEVICE_HEADER_DATA*)callbackUserContext; + + /*Codes_SRS_CODEFIRST_99_068:[ If the function is called before CodeFirst is initialized then EXECUTE_COMMAND_ERROR shall be returned.] */ + if (g_state == CODEFIRST_STATE_NOT_INIT) + { + result = EXECUTE_COMMAND_ERROR; + LogError("CodeFirst_InvokeAction called before init has an error %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result)); + } + /*Codes_SRS_CODEFIRST_99_066:[ If actionName, relativeActionPath or deviceHandle is NULL then EXECUTE_COMMAND_ERROR shall be returned*/ + else if ((actionName == NULL) || + (deviceHandle == NULL) || + (relativeActionPath == NULL)) + { + result = EXECUTE_COMMAND_ERROR; + LogError("action Name is NULL %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result)); + } + /*Codes_SRS_CODEFIRST_99_067:[ If parameterCount is greater than zero and parameterValues is NULL then EXECUTE_COMMAND_ERROR shall be returned.]*/ + else if ((parameterCount > 0) && (parameterValues == NULL)) + { + result = EXECUTE_COMMAND_ERROR; + LogError("parameterValues error %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result)); + } + else + { + const REFLECTED_SOMETHING* something; + const REFLECTED_SOMETHING* childModel; + const char* modelName; + size_t offset; + + modelName = Schema_GetModelName(deviceHeader->ModelHandle); + + if (((childModel = FindModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, modelName)) == NULL) || + /* Codes_SRS_CODEFIRST_99_138:[The relativeActionPath argument shall be used by CodeFirst_InvokeAction to find the child model where the action is declared.] */ + ((childModel = FindChildModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, childModel, relativeActionPath, &offset)) == NULL)) + { + /*Codes_SRS_CODEFIRST_99_141:[If a child model specified in the relativeActionPath argument cannot be found by CodeFirst_InvokeAction, it shall return EXECUTE_COMMAND_ERROR.] */ + result = EXECUTE_COMMAND_ERROR; + LogError("action %s was not found %s ", actionName, ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result)); + } + else + { + /* Codes_SRS_CODEFIRST_99_062:[ When CodeFirst_InvokeAction is called it shall look through the codefirst metadata associated with a specific device for a previously declared action (function) named actionName.]*/ + /* Codes_SRS_CODEFIRST_99_078:[If such a function is not found then the function shall return EXECUTE_COMMAND_ERROR.]*/ + result = EXECUTE_COMMAND_ERROR; + for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next) + { + if ((something->type == REFLECTION_ACTION_TYPE) && + (strcmp(actionName, something->what.action.name) == 0) && + (strcmp(childModel->what.model.name, something->what.action.modelName) == 0)) + { + /*Codes_SRS_CODEFIRST_99_063:[ If the function is found, then CodeFirst shall call the wrapper of the found function inside the data provider. The wrapper is linked in the reflected data to the function name. The wrapper shall be called with the same arguments as CodeFirst_InvokeAction has been called.]*/ + /*Codes_SRS_CODEFIRST_99_064:[ If the wrapper call succeeds then CODEFIRST_OK shall be returned. ]*/ + /*Codes_SRS_CODEFIRST_99_065:[ For all the other return values CODEFIRST_ACTION_EXECUTION_ERROR shall be returned.]*/ + /* Codes_SRS_CODEFIRST_99_140:[CodeFirst_InvokeAction shall pass to the action wrapper that it calls a pointer to the model where the action is defined.] */ + /*Codes_SRS_CODEFIRST_02_013: [The wrapper's return value shall be returned.]*/ + result = something->what.action.wrapper(deviceHeader->data + offset, parameterCount, parameterValues); + break; + } + } + } + } + + if (result == EXECUTE_COMMAND_ERROR) + { + LogError(" %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result)); + } + return result; +} + +METHODRETURN_HANDLE CodeFirst_InvokeMethod(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeMethodPath, const char* methodName, size_t parameterCount, const AGENT_DATA_TYPE* parameterValues) +{ + METHODRETURN_HANDLE result; + DEVICE_HEADER_DATA* deviceHeader = (DEVICE_HEADER_DATA*)callbackUserContext; + + if (g_state == CODEFIRST_STATE_NOT_INIT) + { + result = NULL; + LogError("CodeFirst_InvokeMethod called before CodeFirst_Init"); + } + else if ((methodName == NULL) || + (deviceHandle == NULL) || + (relativeMethodPath == NULL)) + { + result = NULL; + LogError("invalid args: DEVICE_HANDLE deviceHandle=%p, void* callbackUserContext=%p, const char* relativeMethodPath=%p, const char* methodName=%p, size_t parameterCount=%lu, const AGENT_DATA_TYPE* parameterValues=%p", + deviceHandle, callbackUserContext, relativeMethodPath, methodName, parameterCount, parameterValues); + } + else if ((parameterCount > 0) && (parameterValues == NULL)) + { + result = NULL; + LogError("parameterValues error "); + } + else + { + const REFLECTED_SOMETHING* something; + const REFLECTED_SOMETHING* childModel; + const char* modelName; + size_t offset; + + modelName = Schema_GetModelName(deviceHeader->ModelHandle); + + if (((childModel = FindModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, modelName)) == NULL) || + ((childModel = FindChildModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, childModel, relativeMethodPath, &offset)) == NULL)) + { + result = NULL; + LogError("method %s was not found", methodName); + } + else + { + result = NULL; + for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next) + { + if ((something->type == REFLECTION_METHOD_TYPE) && + (strcmp(methodName, something->what.method.name) == 0) && + (strcmp(childModel->what.model.name, something->what.method.modelName) == 0)) + { + break; /*found...*/ + } + } + + if (something == NULL) + { + LogError("method \"%s\" not found", methodName); + result = NULL; + } + else + { + result = something->what.method.wrapper(deviceHeader->data + offset, parameterCount, parameterValues); + if (result == NULL) + { + LogError("method \"%s\" execution error (returned NULL)", methodName); + } + } + } + } + return result; +} + + +/* Codes_SRS_CODEFIRST_99_002:[ CodeFirst_RegisterSchema shall create the schema information and give it to the Schema module for one schema, identified by the metadata argument. On success, it shall return a handle to the schema.] */ +SCHEMA_HANDLE CodeFirst_RegisterSchema(const char* schemaNamespace, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata) +{ + SCHEMA_HANDLE result; + /*Codes_SRS_CODEFIRST_02_048: [ If schemaNamespace is NULL then CodeFirst_RegisterSchema shall fail and return NULL. ]*/ + /*Codes_SRS_CODEFIRST_02_049: [ If metadata is NULL then CodeFirst_RegisterSchema shall fail and return NULL. ]*/ + if ( + (schemaNamespace == NULL) || + (metadata == NULL) + ) + { + LogError("invalid arg const char* schemaNamespace=%p, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata=%p", schemaNamespace, metadata); + result = NULL; + } + else + { + if (g_OverrideSchemaNamespace != NULL) + { + schemaNamespace = g_OverrideSchemaNamespace; + } + + /* Codes_SRS_CODEFIRST_99_121:[If the schema has already been registered, CodeFirst_RegisterSchema shall return its handle.] */ + result = Schema_GetSchemaByNamespace(schemaNamespace); + if (result == NULL) + { + if ((result = Schema_Create(schemaNamespace, (void*)metadata)) == NULL) + { + /* Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL.] */ + result = NULL; + LogError("schema init failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_SCHEMA_ERROR)); + } + else + { + if ((buildStructTypes(result, metadata) != CODEFIRST_OK) || + (buildModelTypes(result, metadata) != CODEFIRST_OK)) + { + Schema_Destroy(result); + result = NULL; + } + else + { + /* do nothing, everything is OK */ + } + } + } + } + + return result; +} + +AGENT_DATA_TYPE_TYPE CodeFirst_GetPrimitiveType(const char* typeName) +{ +#ifndef NO_FLOATS + if (strcmp(typeName, "double") == 0) + { + return EDM_DOUBLE_TYPE; + } + else if (strcmp(typeName, "float") == 0) + { + return EDM_SINGLE_TYPE; + } + else +#endif + if (strcmp(typeName, "int") == 0) + { + return EDM_INT32_TYPE; + } + else if (strcmp(typeName, "long") == 0) + { + return EDM_INT64_TYPE; + } + else if (strcmp(typeName, "int8_t") == 0) + { + return EDM_SBYTE_TYPE; + } + else if (strcmp(typeName, "uint8_t") == 0) + { + return EDM_BYTE_TYPE; + } + else if (strcmp(typeName, "int16_t") == 0) + { + return EDM_INT16_TYPE; + } + else if (strcmp(typeName, "int32_t") == 0) + { + return EDM_INT32_TYPE; + } + else if (strcmp(typeName, "int64_t") == 0) + { + return EDM_INT64_TYPE; + } + else if ( + (strcmp(typeName, "_Bool") == 0) || + (strcmp(typeName, "bool") == 0) + ) + { + return EDM_BOOLEAN_TYPE; + } + else if (strcmp(typeName, "ascii_char_ptr") == 0) + { + return EDM_STRING_TYPE; + } + else if (strcmp(typeName, "ascii_char_ptr_no_quotes") == 0) + { + return EDM_STRING_NO_QUOTES_TYPE; + } + else if (strcmp(typeName, "EDM_DATE_TIME_OFFSET") == 0) + { + return EDM_DATE_TIME_OFFSET_TYPE; + } + else if (strcmp(typeName, "EDM_GUID") == 0) + { + return EDM_GUID_TYPE; + } + else if (strcmp(typeName, "EDM_BINARY") == 0) + { + return EDM_BINARY_TYPE; + } + else + { + return EDM_NO_TYPE; + } +} + +static void initializeDesiredProperties(SCHEMA_MODEL_TYPE_HANDLE model, void* destination) +{ + /*this function assumes that at the address given by destination there is an instance of a model*/ + /*some constituents of the model need to be initialized - ascii_char_ptr for example should be set to NULL*/ + + size_t nDesiredProperties; + if (Schema_GetModelDesiredPropertyCount(model, &nDesiredProperties) != SCHEMA_OK) + { + LogError("unexpected error in Schema_GetModelDesiredPropertyCount"); + } + else + { + size_t nProcessedDesiredProperties = 0; + for (size_t i = 0;i < nDesiredProperties;i++) + { + SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = Schema_GetModelDesiredPropertyByIndex(model, i); + if (desiredPropertyHandle == NULL) + { + LogError("unexpected error in Schema_GetModelDesiredPropertyByIndex"); + i = nDesiredProperties; + } + else + { + pfDesiredPropertyInitialize desiredPropertyInitialize = Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize(desiredPropertyHandle); + if (desiredPropertyInitialize == NULL) + { + LogError("unexpected error in Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize"); + i = nDesiredProperties; + } + else + { + /*Codes_SRS_CODEFIRST_02_036: [ CodeFirst_CreateDevice shall initialize all the desired properties to their default values. ]*/ + size_t offset = Schema_GetModelDesiredProperty_offset(desiredPropertyHandle); + desiredPropertyInitialize((char*)destination + offset); + nProcessedDesiredProperties++; + } + } + } + + if (nDesiredProperties == nProcessedDesiredProperties) + { + /*recursively go in the model and initialize the other fields*/ + size_t nModelInModel; + if (Schema_GetModelModelCount(model, &nModelInModel) != SCHEMA_OK) + { + LogError("unexpected error in Schema_GetModelModelCount"); + } + else + { + size_t nProcessedModelInModel = 0; + for (size_t i = 0; i < nModelInModel;i++) + { + SCHEMA_MODEL_TYPE_HANDLE modelInModel = Schema_GetModelModelyByIndex(model, i); + if (modelInModel == NULL) + { + LogError("unexpected failure in Schema_GetModelModelyByIndex"); + i = nModelInModel; + } + else + { + size_t offset = Schema_GetModelModelByIndex_Offset(model, i); + initializeDesiredProperties(modelInModel, (char*)destination + offset); + nProcessedModelInModel++; + } + } + + if (nProcessedModelInModel == nModelInModel) + { + /*all is fine... */ + } + } + } + } +} + +/* Codes_SRS_CODEFIRST_99_079:[CodeFirst_CreateDevice shall create a device and allocate a memory block that should hold the device data.] */ +void* CodeFirst_CreateDevice(SCHEMA_MODEL_TYPE_HANDLE model, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata, size_t dataSize, bool includePropertyPath) +{ + void* result; + DEVICE_HEADER_DATA* deviceHeader; + + /* Codes_SRS_CODEFIRST_99_080:[If CodeFirst_CreateDevice is invoked with a NULL model, it shall return NULL.]*/ + if (model == NULL) + { + result = NULL; + LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_INVALID_ARG)); + } + else + { + /*Codes_SRS_CODEFIRST_02_037: [ CodeFirst_CreateDevice shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/ + (void)CodeFirst_Init_impl(NULL, false); /*lazy init*/ + + if ((deviceHeader = (DEVICE_HEADER_DATA*)malloc(sizeof(DEVICE_HEADER_DATA))) == NULL) + { + /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */ + result = NULL; + LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR)); + } + /* Codes_SRS_CODEFIRST_99_081:[CodeFirst_CreateDevice shall use Device_Create to create a device handle.] */ + /* Codes_SRS_CODEFIRST_99_082: [ CodeFirst_CreateDevice shall pass to Device_Create the function CodeFirst_InvokeAction, action callback argument and the CodeFirst_InvokeMethod ] */ + else + { + if ((deviceHeader->data = malloc(dataSize)) == NULL) + { + free(deviceHeader); + deviceHeader = NULL; + result = NULL; + LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR)); + } + else + { + DEVICE_HEADER_DATA** newDevices; + + initializeDesiredProperties(model, deviceHeader->data); + + if (Device_Create(model, CodeFirst_InvokeAction, deviceHeader, CodeFirst_InvokeMethod, deviceHeader, + includePropertyPath, &deviceHeader->DeviceHandle) != DEVICE_OK) + { + free(deviceHeader->data); + free(deviceHeader); + + /* Codes_SRS_CODEFIRST_99_084:[If Device_Create fails, CodeFirst_CreateDevice shall return NULL.] */ + result = NULL; + LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_DEVICE_FAILED)); + } + else if ((newDevices = (DEVICE_HEADER_DATA**)realloc(g_Devices, sizeof(DEVICE_HEADER_DATA*) * (g_DeviceCount + 1))) == NULL) + { + Device_Destroy(deviceHeader->DeviceHandle); + free(deviceHeader->data); + free(deviceHeader); + + /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */ + result = NULL; + LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR)); + } + else + { + SCHEMA_RESULT schemaResult; + deviceHeader->ReflectedData = metadata; + deviceHeader->DataSize = dataSize; + deviceHeader->ModelHandle = model; + schemaResult = Schema_AddDeviceRef(model); + if (schemaResult != SCHEMA_OK) + { + Device_Destroy(deviceHeader->DeviceHandle); + free(deviceHeader->data); + free(deviceHeader); + + /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */ + result = NULL; + } + else + { + g_Devices = newDevices; + g_Devices[g_DeviceCount] = deviceHeader; + g_DeviceCount++; + + /* Codes_SRS_CODEFIRST_99_101:[On success, CodeFirst_CreateDevice shall return a non NULL pointer to the device data.] */ + result = deviceHeader->data; + } + } + } + } + + } + + return result; +} + +void CodeFirst_DestroyDevice(void* device) +{ + /* Codes_SRS_CODEFIRST_99_086:[If the argument is NULL, CodeFirst_DestroyDevice shall do nothing.] */ + if (device != NULL) + { + size_t i; + + for (i = 0; i < g_DeviceCount; i++) + { + if (g_Devices[i]->data == device) + { + deinitializeDesiredProperties(g_Devices[i]->ModelHandle, g_Devices[i]->data); + Schema_ReleaseDeviceRef(g_Devices[i]->ModelHandle); + + // Delete the Created Schema if all the devices are unassociated + Schema_DestroyIfUnused(g_Devices[i]->ModelHandle); + + DestroyDevice(g_Devices[i]); + (void)memcpy(&g_Devices[i], &g_Devices[i + 1], (g_DeviceCount - i - 1) * sizeof(DEVICE_HEADER_DATA*)); + g_DeviceCount--; + break; + } + } + + /*Codes_SRS_CODEFIRST_02_039: [ If the current device count is zero then CodeFirst_DestroyDevice shall deallocate all other used resources. ]*/ + if ((g_state == CODEFIRST_STATE_INIT_BY_API) && (g_DeviceCount == 0)) + { + free(g_Devices); + g_Devices = NULL; + g_state = CODEFIRST_STATE_NOT_INIT; + } + } +} + +static DEVICE_HEADER_DATA* FindDevice(void* value) +{ + size_t i; + DEVICE_HEADER_DATA* result = NULL; + + for (i = 0; i < g_DeviceCount; i++) + { + if ((g_Devices[i]->data <= (unsigned char*)value) && + (g_Devices[i]->data + g_Devices[i]->DataSize > (unsigned char*)value)) + { + result = g_Devices[i]; + break; + } + } + + return result; +} + +static const REFLECTED_SOMETHING* FindValue(DEVICE_HEADER_DATA* deviceHeader, void* value, const char* modelName, size_t startOffset, STRING_HANDLE valuePath) +{ + const REFLECTED_SOMETHING* result; + size_t valueOffset = (size_t)((unsigned char*)value - (unsigned char*)deviceHeader->data) - startOffset; + + for (result = deviceHeader->ReflectedData->reflectedData; result != NULL; result = result->next) + { + if (result->type == REFLECTION_PROPERTY_TYPE && + (strcmp(result->what.property.modelName, modelName) == 0) && + (result->what.property.offset <= valueOffset) && + (result->what.property.offset + result->what.property.size > valueOffset)) + { + if (startOffset != 0) + { + STRING_concat(valuePath, "/"); + } + + STRING_concat(valuePath, result->what.property.name); + break; + } + } + + if (result != NULL) + { + /* Codes_SRS_CODEFIRST_99_133:[CodeFirst_SendAsync shall allow sending of properties that are part of a child model.] */ + if (result->what.property.offset < valueOffset) + { + /* find recursively the property in the inner model, if there is one */ + result = FindValue(deviceHeader, value, result->what.property.type, startOffset + result->what.property.offset, valuePath); + } + } + + return result; +} + +static const REFLECTED_SOMETHING* FindReportedProperty(DEVICE_HEADER_DATA* deviceHeader, void* value, const char* modelName, size_t startOffset, STRING_HANDLE valuePath) +{ + const REFLECTED_SOMETHING* result; + size_t valueOffset = (size_t)((unsigned char*)value - (unsigned char*)deviceHeader->data) - startOffset; + + for (result = deviceHeader->ReflectedData->reflectedData; result != NULL; result = result->next) + { + if (result->type == REFLECTION_REPORTED_PROPERTY_TYPE && + (strcmp(result->what.reportedProperty.modelName, modelName) == 0) && + (result->what.reportedProperty.offset <= valueOffset) && + (result->what.reportedProperty.offset + result->what.reportedProperty.size > valueOffset)) + { + if (startOffset != 0) + { + if (STRING_concat(valuePath, "/") != 0) + { + LogError("unable to STRING_concat"); + result = NULL; + break; + } + } + + if (STRING_concat(valuePath, result->what.reportedProperty.name) != 0) + { + LogError("unable to STRING_concat"); + result = NULL; + break; + } + break; + } + } + + if (result != NULL) + { + /* Codes_SRS_CODEFIRST_99_133:[CodeFirst_SendAsync shall allow sending of properties that are part of a child model.] */ + if (result->what.reportedProperty.offset < valueOffset) + { + /* find recursively the property in the inner model, if there is one */ + result = FindReportedProperty(deviceHeader, value, result->what.reportedProperty.type, startOffset + result->what.reportedProperty.offset, valuePath); + } + } + + return result; +} + +/* Codes_SRS_CODEFIRST_99_130:[If a pointer to the beginning of a device block is passed to CodeFirst_SendAsync instead of a pointer to a property, CodeFirst_SendAsync shall send all the properties that belong to that device.] */ +/* Codes_SRS_CODEFIRST_99_131:[The properties shall be given to Device as one transaction, as if they were all passed as individual arguments to Code_First.] */ +static CODEFIRST_RESULT SendAllDeviceProperties(DEVICE_HEADER_DATA* deviceHeader, TRANSACTION_HANDLE transaction) +{ + const char* modelName = Schema_GetModelName(deviceHeader->ModelHandle); + const REFLECTED_SOMETHING* something; + unsigned char* deviceAddress = (unsigned char*)deviceHeader->data; + CODEFIRST_RESULT result = CODEFIRST_OK; + + for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next) + { + if ((something->type == REFLECTION_PROPERTY_TYPE) && + (strcmp(something->what.property.modelName, modelName) == 0)) + { + AGENT_DATA_TYPE agentDataType; + + /* Codes_SRS_CODEFIRST_99_097:[For each value marshalling to AGENT_DATA_TYPE shall be performed.] */ + /* Codes_SRS_CODEFIRST_99_098:[The marshalling shall be done by calling the Create_AGENT_DATA_TYPE_from_Ptr function associated with the property.] */ + if (something->what.property.Create_AGENT_DATA_TYPE_from_Ptr(deviceAddress + something->what.property.offset, &agentDataType) != AGENT_DATA_TYPES_OK) + { + /* Codes_SRS_CODEFIRST_99_099:[If Create_AGENT_DATA_TYPE_from_Ptr fails, CodeFirst_SendAsync shall return CODEFIRST_AGENT_DATA_TYPE_ERROR.] */ + result = CODEFIRST_AGENT_DATA_TYPE_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + else + { + /* Codes_SRS_CODEFIRST_99_092:[CodeFirst shall publish each value by using Device_PublishTransacted.] */ + if (Device_PublishTransacted(transaction, something->what.property.name, &agentDataType) != DEVICE_OK) + { + Destroy_AGENT_DATA_TYPE(&agentDataType); + + /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */ + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + break; + } + + Destroy_AGENT_DATA_TYPE(&agentDataType); + } + } + } + + return result; +} + +static CODEFIRST_RESULT SendAllDeviceReportedProperties(DEVICE_HEADER_DATA* deviceHeader, REPORTED_PROPERTIES_TRANSACTION_HANDLE transaction) +{ + const char* modelName = Schema_GetModelName(deviceHeader->ModelHandle); + const REFLECTED_SOMETHING* something; + unsigned char* deviceAddress = (unsigned char*)deviceHeader->data; + CODEFIRST_RESULT result = CODEFIRST_OK; + + for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next) + { + if ((something->type == REFLECTION_REPORTED_PROPERTY_TYPE) && + (strcmp(something->what.reportedProperty.modelName, modelName) == 0)) + { + AGENT_DATA_TYPE agentDataType; + + if (something->what.reportedProperty.Create_AGENT_DATA_TYPE_from_Ptr(deviceAddress + something->what.reportedProperty.offset, &agentDataType) != AGENT_DATA_TYPES_OK) + { + result = CODEFIRST_AGENT_DATA_TYPE_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + else + { + if (Device_PublishTransacted_ReportedProperty(transaction, something->what.reportedProperty.name, &agentDataType) != DEVICE_OK) + { + Destroy_AGENT_DATA_TYPE(&agentDataType); + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + break; + } + + Destroy_AGENT_DATA_TYPE(&agentDataType); + } + } + } + + return result; +} + + +/* Codes_SRS_CODEFIRST_99_088:[CodeFirst_SendAsync shall send to the Device module a set of properties, a destination and a destinationSize.]*/ +CODEFIRST_RESULT CodeFirst_SendAsync(unsigned char** destination, size_t* destinationSize, size_t numProperties, ...) +{ + CODEFIRST_RESULT result; + va_list ap; + + if ( + (numProperties == 0) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + /* Codes_SRS_CODEFIRST_04_002: [If CodeFirst_SendAsync receives destination or destinationSize NULL, CodeFirst_SendAsync shall return Invalid Argument.]*/ + /* Codes_SRS_CODEFIRST_99_103:[If CodeFirst_SendAsync is called with numProperties being zero, CODEFIRST_INVALID_ARG shall be returned.] */ + result = CODEFIRST_INVALID_ARG; + LOG_CODEFIRST_ERROR; + } + else + { + /*Codes_SRS_CODEFIRST_02_040: [ CodeFirst_SendAsync shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/ + (void)CodeFirst_Init_impl(NULL, false); /*lazy init*/ + + DEVICE_HEADER_DATA* deviceHeader = NULL; + size_t i; + TRANSACTION_HANDLE transaction = NULL; + result = CODEFIRST_OK; + + /* Codes_SRS_CODEFIRST_99_105:[The properties are passed as pointers to the memory locations where the data exists in the device block allocated by CodeFirst_CreateDevice.] */ + va_start(ap, numProperties); + + /* Codes_SRS_CODEFIRST_99_089:[The numProperties argument shall indicate how many properties are to be sent.] */ + for (i = 0; i < numProperties; i++) + { + void* value = (void*)va_arg(ap, void*); + + /* Codes_SRS_CODEFIRST_99_095:[For each value passed to it, CodeFirst_SendAsync shall look up to which device the value belongs.] */ + DEVICE_HEADER_DATA* currentValueDeviceHeader = FindDevice(value); + if (currentValueDeviceHeader == NULL) + { + /* Codes_SRS_CODEFIRST_99_104:[If a property cannot be associated with a device, CodeFirst_SendAsync shall return CODEFIRST_INVALID_ARG.] */ + result = CODEFIRST_INVALID_ARG; + LOG_CODEFIRST_ERROR; + break; + } + else if ((deviceHeader != NULL) && + (currentValueDeviceHeader != deviceHeader)) + { + /* Codes_SRS_CODEFIRST_99_096:[All values have to belong to the same device, otherwise CodeFirst_SendAsync shall return CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR.] */ + result = CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + /* Codes_SRS_CODEFIRST_99_090:[All the properties shall be sent together by using the transacted APIs of the device.] */ + /* Codes_SRS_CODEFIRST_99_091:[CodeFirst_SendAsync shall start a transaction by calling Device_StartTransaction.] */ + else if ((deviceHeader == NULL) && + ((transaction = Device_StartTransaction(currentValueDeviceHeader->DeviceHandle)) == NULL)) + { + /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */ + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + break; + } + else + { + deviceHeader = currentValueDeviceHeader; + + if (value == ((unsigned char*)deviceHeader->data)) + { + /* we got a full device, send all its state data */ + result = SendAllDeviceProperties(deviceHeader, transaction); + if (result != CODEFIRST_OK) + { + LOG_CODEFIRST_ERROR; + break; + } + } + else + { + const REFLECTED_SOMETHING* propertyReflectedData; + const char* modelName; + STRING_HANDLE valuePath; + + if ((valuePath = STRING_new()) == NULL) + { + /* Codes_SRS_CODEFIRST_99_134:[If CodeFirst_Notify fails for any other reason it shall return CODEFIRST_ERROR.] */ + result = CODEFIRST_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + else + { + if ((modelName = Schema_GetModelName(deviceHeader->ModelHandle)) == NULL) + { + /* Codes_SRS_CODEFIRST_99_134:[If CodeFirst_Notify fails for any other reason it shall return CODEFIRST_ERROR.] */ + result = CODEFIRST_ERROR; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else if ((propertyReflectedData = FindValue(deviceHeader, value, modelName, 0, valuePath)) == NULL) + { + /* Codes_SRS_CODEFIRST_99_104:[If a property cannot be associated with a device, CodeFirst_SendAsync shall return CODEFIRST_INVALID_ARG.] */ + result = CODEFIRST_INVALID_ARG; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + AGENT_DATA_TYPE agentDataType; + + /* Codes_SRS_CODEFIRST_99_097:[For each value marshalling to AGENT_DATA_TYPE shall be performed.] */ + /* Codes_SRS_CODEFIRST_99_098:[The marshalling shall be done by calling the Create_AGENT_DATA_TYPE_from_Ptr function associated with the property.] */ + if (propertyReflectedData->what.property.Create_AGENT_DATA_TYPE_from_Ptr(value, &agentDataType) != AGENT_DATA_TYPES_OK) + { + /* Codes_SRS_CODEFIRST_99_099:[If Create_AGENT_DATA_TYPE_from_Ptr fails, CodeFirst_SendAsync shall return CODEFIRST_AGENT_DATA_TYPE_ERROR.] */ + result = CODEFIRST_AGENT_DATA_TYPE_ERROR; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + /* Codes_SRS_CODEFIRST_99_092:[CodeFirst shall publish each value by using Device_PublishTransacted.] */ + /* Codes_SRS_CODEFIRST_99_136:[CodeFirst_SendAsync shall build the full path for each property and then pass it to Device_PublishTransacted.] */ + if (Device_PublishTransacted(transaction, STRING_c_str(valuePath), &agentDataType) != DEVICE_OK) + { + Destroy_AGENT_DATA_TYPE(&agentDataType); + + /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */ + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + STRING_delete(valuePath); /*anyway*/ + } + + Destroy_AGENT_DATA_TYPE(&agentDataType); + } + } + } + } + } + } + + if (i < numProperties) + { + if (transaction != NULL) + { + (void)Device_CancelTransaction(transaction); + } + } + /* Codes_SRS_CODEFIRST_99_093:[After all values have been published, Device_EndTransaction shall be called.] */ + else if (Device_EndTransaction(transaction, destination, destinationSize) != DEVICE_OK) + { + /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */ + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + } + else + { + /* Codes_SRS_CODEFIRST_99_117:[On success, CodeFirst_SendAsync shall return CODEFIRST_OK.] */ + result = CODEFIRST_OK; + } + + va_end(ap); + + } + + return result; +} + +CODEFIRST_RESULT CodeFirst_SendAsyncReported(unsigned char** destination, size_t* destinationSize, size_t numReportedProperties, ...) +{ + CODEFIRST_RESULT result; + if ((destination == NULL) || (destinationSize == NULL) || numReportedProperties == 0) + { + /*Codes_SRS_CODEFIRST_02_018: [ If parameter destination, destinationSize or any of the values passed through va_args is NULL then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/ + LogError("invalid argument unsigned char** destination=%p, size_t* destinationSize=%p, size_t numReportedProperties=%lu", destination, destinationSize, numReportedProperties); + result = CODEFIRST_INVALID_ARG; + } + else + { + /*Codes_SRS_CODEFIRST_02_046: [ CodeFirst_SendAsyncReported shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/ + (void)CodeFirst_Init_impl(NULL, false);/*lazy init*/ + + DEVICE_HEADER_DATA* deviceHeader = NULL; + size_t i; + REPORTED_PROPERTIES_TRANSACTION_HANDLE transaction = NULL; + va_list ap; + result = CODEFIRST_ACTION_EXECUTION_ERROR; /*this initialization squelches a false warning about result not being initialized*/ + + va_start(ap, numReportedProperties); + + for (i = 0; i < numReportedProperties; i++) + { + void* value = (void*)va_arg(ap, void*); + /*Codes_SRS_CODEFIRST_02_018: [ If parameter destination, destinationSize or any of the values passed through va_args is NULL then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/ + if (value == NULL) + { + LogError("argument number %lu passed through variable arguments is NULL", i); + result = CODEFIRST_INVALID_ARG; + break; + } + else + { + DEVICE_HEADER_DATA* currentValueDeviceHeader = FindDevice(value); + if (currentValueDeviceHeader == NULL) + { + result = CODEFIRST_INVALID_ARG; + LOG_CODEFIRST_ERROR; + break; + } + /*Codes_SRS_CODEFIRST_02_019: [ If values passed through va_args do not belong to the same device then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR. ]*/ + else if ((deviceHeader != NULL) && + (currentValueDeviceHeader != deviceHeader)) + { + result = CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + /*Codes_SRS_CODEFIRST_02_022: [ CodeFirst_SendAsyncReported shall start a transaction by calling Device_CreateTransaction_ReportedProperties. ]*/ + else if ((deviceHeader == NULL) && + ((transaction = Device_CreateTransaction_ReportedProperties(currentValueDeviceHeader->DeviceHandle)) == NULL)) + { + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + break; + } + else + { + deviceHeader = currentValueDeviceHeader; + if (value == ((unsigned char*)deviceHeader->data)) + { + /*Codes_SRS_CODEFIRST_02_021: [ If the value passed through va_args is a complete model instance, then CodeFirst_SendAsyncReported shall send all the reported properties of that device. ]*/ + result = SendAllDeviceReportedProperties(deviceHeader, transaction); + if (result != CODEFIRST_OK) + { + LOG_CODEFIRST_ERROR; + break; + } + } + else + { + /*Codes_SRS_CODEFIRST_02_020: [ If values passed through va_args are not all of type REFLECTED_REPORTED_PROPERTY then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/ + const REFLECTED_SOMETHING* propertyReflectedData; + const char* modelName; + + STRING_HANDLE valuePath; + if ((valuePath = STRING_new()) == NULL) + { + result = CODEFIRST_ERROR; + LOG_CODEFIRST_ERROR; + break; + } + else + { + modelName = Schema_GetModelName(deviceHeader->ModelHandle); + + /*Codes_SRS_CODEFIRST_02_025: [ CodeFirst_SendAsyncReported shall compute for every AGENT_DATA_TYPE the valuePath. ]*/ + if ((propertyReflectedData = FindReportedProperty(deviceHeader, value, modelName, 0, valuePath)) == NULL) + { + result = CODEFIRST_INVALID_ARG; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + AGENT_DATA_TYPE agentDataType; + /*Codes_SRS_CODEFIRST_02_023: [ CodeFirst_SendAsyncReported shall convert all REPORTED_PROPERTY model components to AGENT_DATA_TYPE. ]*/ + if (propertyReflectedData->what.reportedProperty.Create_AGENT_DATA_TYPE_from_Ptr(value, &agentDataType) != AGENT_DATA_TYPES_OK) + { + result = CODEFIRST_AGENT_DATA_TYPE_ERROR; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + /*Codes_SRS_CODEFIRST_02_024: [ CodeFirst_SendAsyncReported shall call Device_PublishTransacted_ReportedProperty for every AGENT_DATA_TYPE converted from REPORTED_PROPERTY. ]*/ + if (Device_PublishTransacted_ReportedProperty(transaction, STRING_c_str(valuePath), &agentDataType) != DEVICE_OK) + { + Destroy_AGENT_DATA_TYPE(&agentDataType); + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + STRING_delete(valuePath); + break; + } + else + { + STRING_delete(valuePath); + } + Destroy_AGENT_DATA_TYPE(&agentDataType); + } + } + } + } + } + } + } + + /*Codes_SRS_CODEFIRST_02_027: [ If any error occurs, CodeFirst_SendAsyncReported shall fail and return CODEFIRST_ERROR. ]*/ + if (i < numReportedProperties) + { + if (transaction != NULL) + { + Device_DestroyTransaction_ReportedProperties(transaction); + } + } + /*Codes_SRS_CODEFIRST_02_026: [ CodeFirst_SendAsyncReported shall call Device_CommitTransaction_ReportedProperties to commit the transaction. ]*/ + else + { + if (Device_CommitTransaction_ReportedProperties(transaction, destination, destinationSize) != DEVICE_OK) + { + result = CODEFIRST_DEVICE_PUBLISH_FAILED; + LOG_CODEFIRST_ERROR; + } + else + { + /*Codes_SRS_CODEFIRST_02_028: [ CodeFirst_SendAsyncReported shall return CODEFIRST_OK when it succeeds. ]*/ + result = CODEFIRST_OK; + } + + /*Codes_SRS_CODEFIRST_02_029: [ CodeFirst_SendAsyncReported shall call Device_DestroyTransaction_ReportedProperties to destroy the transaction. ]*/ + Device_DestroyTransaction_ReportedProperties(transaction); + } + + va_end(ap); + } + return result; +} + +EXECUTE_COMMAND_RESULT CodeFirst_ExecuteCommand(void* device, const char* command) +{ + EXECUTE_COMMAND_RESULT result; + /*Codes_SRS_CODEFIRST_02_014: [If parameter device or command is NULL then CodeFirst_ExecuteCommand shall return EXECUTE_COMMAND_ERROR.] */ + if ( + (device == NULL) || + (command == NULL) + ) + { + result = EXECUTE_COMMAND_ERROR; + LogError("invalid argument (NULL) passed to CodeFirst_ExecuteCommand void* device = %p, const char* command = %p", device, command); + } + else + { + /*Codes_SRS_CODEFIRST_02_015: [CodeFirst_ExecuteCommand shall find the device.]*/ + DEVICE_HEADER_DATA* deviceHeader = FindDevice(device); + if(deviceHeader == NULL) + { + /*Codes_SRS_CODEFIRST_02_016: [If finding the device fails, then CodeFirst_ExecuteCommand shall return EXECUTE_COMMAND_ERROR.]*/ + result = EXECUTE_COMMAND_ERROR; + LogError("unable to find the device given by address %p", device); + } + else + { + /*Codes_SRS_CODEFIRST_02_017: [Otherwise CodeFirst_ExecuteCommand shall call Device_ExecuteCommand and return what Device_ExecuteCommand is returning.] */ + result = Device_ExecuteCommand(deviceHeader->DeviceHandle, command); + } + } + return result; +} + +METHODRETURN_HANDLE CodeFirst_ExecuteMethod(void* device, const char* methodName, const char* methodPayload) +{ + METHODRETURN_HANDLE result; + if ( + (device == NULL) || + (methodName== NULL) /*methodPayload can be NULL*/ + ) + { + result = NULL; + LogError("invalid argument (NULL) passed to CodeFirst_ExecuteMethod void* device = %p, const char* methodName = %p", device, methodName); + } + else + { + DEVICE_HEADER_DATA* deviceHeader = FindDevice(device); + if (deviceHeader == NULL) + { + result = NULL; + LogError("unable to find the device given by address %p", device); + } + else + { + result = Device_ExecuteMethod(deviceHeader->DeviceHandle, methodName, methodPayload); + } + } + return result; +} + +CODEFIRST_RESULT CodeFirst_IngestDesiredProperties(void* device, const char* jsonPayload, bool parseDesiredNode) +{ + CODEFIRST_RESULT result; + /*Codes_SRS_CODEFIRST_02_030: [ If argument device is NULL then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_INVALID_ARG. ]*/ + /*Codes_SRS_CODEFIRST_02_031: [ If argument jsonPayload is NULL then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_INVALID_ARG. ]*/ + if ( + (device == NULL) || + (jsonPayload == NULL) + ) + { + LogError("invalid argument void* device=%p, const char* jsonPayload=%p", device, jsonPayload); + result = CODEFIRST_INVALID_ARG; + } + else + { + /*Codes_SRS_CODEFIRST_02_032: [ CodeFirst_IngestDesiredProperties shall locate the device associated with device. ]*/ + DEVICE_HEADER_DATA* deviceHeader = FindDevice(device); + if (deviceHeader == NULL) + { + /*Codes_SRS_CODEFIRST_02_034: [ If there is any failure, then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_ERROR. ]*/ + LogError("unable to find a device having this memory address %p", device); + result = CODEFIRST_ERROR; + } + else + { + /*Codes_SRS_CODEFIRST_02_033: [ CodeFirst_IngestDesiredProperties shall call Device_IngestDesiredProperties. ]*/ + if (Device_IngestDesiredProperties(device, deviceHeader->DeviceHandle, jsonPayload, parseDesiredNode) != DEVICE_OK) + { + LogError("failure in Device_IngestDesiredProperties"); + result = CODEFIRST_ERROR; + } + else + { + /*Codes_SRS_CODEFIRST_02_035: [ Otherwise, CodeFirst_IngestDesiredProperties shall return CODEFIRST_OK. ]*/ + result = CODEFIRST_OK; + } + } + } + return result; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/commanddecoder.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1038 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" + +#include <stddef.h> + +#include "commanddecoder.h" +#include "multitree.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "schema.h" +#include "codefirst.h" +#include "jsondecoder.h" + +DEFINE_ENUM_STRINGS(COMMANDDECODER_RESULT, COMMANDDECODER_RESULT_VALUES); + +typedef struct COMMAND_DECODER_HANDLE_DATA_TAG +{ + METHOD_CALLBACK_FUNC methodCallback; + void* methodCallbackContext; + SCHEMA_MODEL_TYPE_HANDLE ModelHandle; + ACTION_CALLBACK_FUNC ActionCallback; + void* ActionCallbackContext; +} COMMAND_DECODER_HANDLE_DATA; + +static int DecodeValueFromNode(SCHEMA_HANDLE schemaHandle, AGENT_DATA_TYPE* agentDataType, MULTITREE_HANDLE node, const char* edmTypeName) +{ + /* because "pottentially uninitialized variable on MS compiler" */ + int result = 0; + const char* argStringValue; + AGENT_DATA_TYPE_TYPE primitiveType; + + /* Codes_SRS_COMMAND_DECODER_99_029:[ If the argument type is complex then a complex type value shall be built from the child nodes.] */ + if ((primitiveType = CodeFirst_GetPrimitiveType(edmTypeName)) == EDM_NO_TYPE) + { + SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle; + size_t propertyCount; + + /* Codes_SRS_COMMAND_DECODER_99_033:[ In order to determine which are the members of a complex types, Schema APIs for structure types shall be used.] */ + if (((structTypeHandle = Schema_GetStructTypeByName(schemaHandle, edmTypeName)) == NULL) || + (Schema_GetStructTypePropertyCount(structTypeHandle, &propertyCount) != SCHEMA_OK)) + { + /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + result = __FAILURE__; + LogError("Getting Struct information failed."); + } + else + { + if (propertyCount == 0) + { + /* Codes_SRS_COMMAND_DECODER_99_034:[ If Schema APIs indicate that a complex type has 0 members then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + result = __FAILURE__; + LogError("Struct type with 0 members is not allowed"); + } + else + { + AGENT_DATA_TYPE* memberValues = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* propertyCount); + if (memberValues == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + result = __FAILURE__; + LogError("Failed allocating member values for command argument"); + } + else + { + const char** memberNames = (const char**)malloc(sizeof(const char*)* propertyCount); + if (memberNames == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + result = __FAILURE__; + LogError("Failed allocating member names for command argument."); + } + else + { + size_t j; + size_t k; + + for (j = 0; j < propertyCount; j++) + { + SCHEMA_PROPERTY_HANDLE propertyHandle; + MULTITREE_HANDLE memberNode; + const char* propertyName; + const char* propertyType; + + if ((propertyHandle = Schema_GetStructTypePropertyByIndex(structTypeHandle, j)) == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + result = __FAILURE__; + LogError("Getting struct member failed."); + break; + } + else if (((propertyName = Schema_GetPropertyName(propertyHandle)) == NULL) || + ((propertyType = Schema_GetPropertyType(propertyHandle)) == NULL)) + { + /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + result = __FAILURE__; + LogError("Getting the struct member information failed."); + break; + } + else + { + memberNames[j] = propertyName; + + /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */ + if (MultiTree_GetChildByName(node, memberNames[j], &memberNode) != MULTITREE_OK) + { + /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + result = __FAILURE__; + LogError("Getting child %s failed", propertyName); + break; + } + /* Codes_SRS_COMMAND_DECODER_99_032:[ Nesting shall be supported for complex type.] */ + else if ((result = DecodeValueFromNode(schemaHandle, &memberValues[j], memberNode, propertyType)) != 0) + { + break; + } + } + } + + if (j == propertyCount) + { + /* Codes_SRS_COMMAND_DECODER_99_031:[ The complex type value that aggregates the children shall be built by using the Create_AGENT_DATA_TYPE_from_Members.] */ + if (Create_AGENT_DATA_TYPE_from_Members(agentDataType, edmTypeName, propertyCount, (const char* const*)memberNames, memberValues) != AGENT_DATA_TYPES_OK) + { + /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + result = __FAILURE__; + LogError("Creating the agent data type from members failed."); + } + else + { + result = 0; + } + } + + for (k = 0; k < j; k++) + { + Destroy_AGENT_DATA_TYPE(&memberValues[k]); + } + + free((void*)memberNames); + } + + free(memberValues); + } + } + } + } + else + { + /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */ + if (MultiTree_GetValue(node, (const void **)&argStringValue) != MULTITREE_OK) + { + /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + result = __FAILURE__; + LogError("Getting the string from the multitree failed."); + } + /* Codes_SRS_COMMAND_DECODER_99_027:[ The value for an argument of primitive type shall be decoded by using the CreateAgentDataType_From_String API.] */ + else if (CreateAgentDataType_From_String(argStringValue, primitiveType, agentDataType) != AGENT_DATA_TYPES_OK) + { + /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + result = __FAILURE__; + LogError("Failed parsing node %s.", argStringValue); + } + } + + return result; +} + +static EXECUTE_COMMAND_RESULT DecodeAndExecuteModelAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeActionPath, const char* actionName, MULTITREE_HANDLE commandNode) +{ + EXECUTE_COMMAND_RESULT result; + char tempStr[128]; + size_t strLength = strlen(actionName); + + if (strLength <= 1) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Invalid action name"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "Name" of the command JSON.] */ + SCHEMA_ACTION_HANDLE modelActionHandle; + size_t argCount; + MULTITREE_HANDLE parametersTreeNode; + +#ifdef _MSC_VER +#pragma warning(suppress: 6324) /* We intentionally use here strncpy */ +#endif + if (strncpy(tempStr, actionName, strLength - 1) == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Invalid action name."); + result = EXECUTE_COMMAND_ERROR; + } + /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */ + else if (MultiTree_GetChildByName(commandNode, "Parameters", ¶metersTreeNode) != MULTITREE_OK) + { + /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + LogError("Error getting Parameters node."); + result = EXECUTE_COMMAND_ERROR; + } + else + { + tempStr[strLength - 1] = 0; + + /* Codes_SRS_COMMAND_DECODER_99_009:[ CommandDecoder shall call Schema_GetModelActionByName to obtain the information about a specific action.] */ + if (((modelActionHandle = Schema_GetModelActionByName(modelHandle, tempStr)) == NULL) || + (Schema_GetModelActionArgumentCount(modelActionHandle, &argCount) != SCHEMA_OK)) + { + /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + LogError("Failed reading action %s from the schema", tempStr); + result = EXECUTE_COMMAND_ERROR; + } + else + { + AGENT_DATA_TYPE* arguments = NULL; + + if (argCount > 0) + { + arguments = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* argCount); + } + + if ((argCount > 0) && + (arguments == NULL)) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Failed allocating arguments array"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + size_t i; + size_t j; + result = EXECUTE_COMMAND_ERROR; + + /* Codes_SRS_COMMAND_DECODER_99_011:[ CommandDecoder shall attempt to extract from the command text the value for each action argument.] */ + for (i = 0; i < argCount; i++) + { + SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle; + MULTITREE_HANDLE argumentNode; + const char* argName; + const char* argType; + + if (((actionArgumentHandle = Schema_GetModelActionArgumentByIndex(modelActionHandle, i)) == NULL) || + ((argName = Schema_GetActionArgumentName(actionArgumentHandle)) == NULL) || + ((argType = Schema_GetActionArgumentType(actionArgumentHandle)) == NULL)) + { + LogError("Failed getting the argument information from the schema"); + result = EXECUTE_COMMAND_ERROR; + break; + } + /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */ + /* Codes_SRS_COMMAND_DECODER_01_008: [Each argument shall be looked up as a field, member of the "Parameters" node.] */ + else if (MultiTree_GetChildByName(parametersTreeNode, argName, &argumentNode) != MULTITREE_OK) + { + /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + LogError("Missing argument %s", argName); + result = EXECUTE_COMMAND_ERROR; + break; + } + else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0) + { + result = EXECUTE_COMMAND_ERROR; + break; + } + } + + if (i == argCount) + { + /* Codes_SRS_COMMAND_DECODER_99_005:[ If an Invoke Action is decoded successfully then the callback actionCallback shall be called, passing to it the callback action context, decoded name and arguments.] */ + result = commandDecoderInstance->ActionCallback(commandDecoderInstance->ActionCallbackContext, relativeActionPath, tempStr, argCount, arguments); + } + + for (j = 0; j < i; j++) + { + Destroy_AGENT_DATA_TYPE(&arguments[j]); + } + + if (arguments != NULL) + { + free(arguments); + } + } + } + } + } + return result; +} + +static METHODRETURN_HANDLE DecodeAndExecuteModelMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeMethodPath, const char* methodName, MULTITREE_HANDLE methodTree) +{ + METHODRETURN_HANDLE result; + size_t strLength = strlen(methodName); + + if (strLength == 0) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Invalid method name"); + result = NULL; + } + else + { + SCHEMA_METHOD_HANDLE modelMethodHandle; + size_t argCount; + +#ifdef _MSC_VER +#pragma warning(suppress: 6324) /* We intentionally use here strncpy */ +#endif + + /*Codes_SRS_COMMAND_DECODER_02_020: [ CommandDecoder_ExecuteMethod shall verify that the model has a method called methodName. ]*/ + if (((modelMethodHandle = Schema_GetModelMethodByName(modelHandle, methodName)) == NULL) || + (Schema_GetModelMethodArgumentCount(modelMethodHandle, &argCount) != SCHEMA_OK)) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Failed reading method %s from the schema", methodName); + result = NULL; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_021: [ For every argument of methodName, CommandDecoder_ExecuteMethod shall build an AGENT_DATA_TYPE from the node with the same name from the MULTITREE_HANDLE. ]*/ + + if (argCount == 0) + { + /*no need for any parameters*/ + result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, 0, NULL); + } + else + { + AGENT_DATA_TYPE* arguments; + arguments = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* argCount); + if (arguments == NULL) + { + LogError("Failed allocating arguments array"); + result = NULL; + } + else + { + size_t i; + size_t j; + result = NULL; + + for (i = 0; i < argCount; i++) + { + SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle; + MULTITREE_HANDLE argumentNode; + const char* argName; + const char* argType; + + if (((methodArgumentHandle = Schema_GetModelMethodArgumentByIndex(modelMethodHandle, i)) == NULL) || + ((argName = Schema_GetMethodArgumentName(methodArgumentHandle)) == NULL) || + ((argType = Schema_GetMethodArgumentType(methodArgumentHandle)) == NULL)) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Failed getting the argument information from the schema"); + result = NULL; + break; + } + else if (MultiTree_GetChildByName(methodTree, argName, &argumentNode) != MULTITREE_OK) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Missing argument %s", argName); + result = NULL; + break; + } + else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("failure in DecodeValueFromNode"); + result = NULL; + break; + } + } + + if (i == argCount) + { + /*Codes_SRS_COMMAND_DECODER_02_022: [ CommandDecoder_ExecuteMethod shall call methodCallback passing the context, the methodName, number of arguments and the AGENT_DATA_TYPE. ]*/ + /*Codes_SRS_COMMAND_DECODER_02_024: [ Otherwise, CommandDecoder_ExecuteMethod shall return what methodCallback returns. ]*/ + result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, argCount, arguments); + } + + for (j = 0; j < i; j++) + { + Destroy_AGENT_DATA_TYPE(&arguments[j]); + } + + free(arguments); + } + + } + } + + } + return result; +} + + +static EXECUTE_COMMAND_RESULT ScanActionPathAndExecuteAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* actionPath, MULTITREE_HANDLE commandNode) +{ + EXECUTE_COMMAND_RESULT result; + char* relativeActionPath; + const char* actionName = actionPath; + SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle; + + /* Codes_SRS_COMMAND_DECODER_99_035:[ CommandDecoder_ExecuteCommand shall support paths to actions that are in child models (i.e. ChildModel/SomeAction.] */ + do + { + /* find the slash */ + const char* slashPos = strchr(actionName, '/'); + if (slashPos == NULL) + { + size_t relativeActionPathLength; + + /* Codes_SRS_COMMAND_DECODER_99_037:[ The relative path passed to the actionCallback shall be in the format "childModel1/childModel2/.../childModelN".] */ + if (actionName == actionPath) + { + relativeActionPathLength = 0; + } + else + { + relativeActionPathLength = actionName - actionPath - 1; + } + + relativeActionPath = (char*)malloc(relativeActionPathLength + 1); + if (relativeActionPath == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Failed allocating relative action path"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + strncpy(relativeActionPath, actionPath, relativeActionPathLength); + relativeActionPath[relativeActionPathLength] = 0; + + /* no slash found, this must be an action */ + result = DecodeAndExecuteModelAction(commandDecoderInstance, schemaHandle, modelHandle, relativeActionPath, actionName, commandNode); + + free(relativeActionPath); + actionName = NULL; + } + break; + } + else + { + /* found a slash, get the child model name */ + size_t modelLength = slashPos - actionName; + char* childModelName = (char*)malloc(modelLength + 1); + if (childModelName == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Failed allocating child model name"); + result = EXECUTE_COMMAND_ERROR; + break; + } + else + { + strncpy(childModelName, actionName, modelLength); + childModelName[modelLength] = 0; + + /* find the model */ + modelHandle = Schema_GetModelModelByName(modelHandle, childModelName); + if (modelHandle == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_036:[ If a child model cannot be found by using Schema APIs then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + LogError("Getting the model %s failed", childModelName); + free(childModelName); + result = EXECUTE_COMMAND_ERROR; + break; + } + else + { + free(childModelName); + actionName = slashPos + 1; + result = EXECUTE_COMMAND_ERROR; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/ + } + } + } + } while (actionName != NULL); + return result; +} + +static METHODRETURN_HANDLE ScanMethodPathAndExecuteMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* fullMethodName, MULTITREE_HANDLE methodTree) +{ + METHODRETURN_HANDLE result; + char* relativeMethodPath; + const char* methodName = fullMethodName; + SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle; + + /*Codes_SRS_COMMAND_DECODER_02_018: [ CommandDecoder_ExecuteMethod shall validate that consecutive segments of the fullMethodName exist in the model. ]*/ + /*Codes_SRS_COMMAND_DECODER_02_019: [ CommandDecoder_ExecuteMethod shall locate the final model to which the methodName applies. ]*/ + do + { + /* find the slash */ + const char* slashPos = strchr(methodName, '/'); + if (slashPos == NULL) + { + size_t relativeMethodPathLength; + + if (methodName == fullMethodName) + { + relativeMethodPathLength = 0; + } + else + { + relativeMethodPathLength = methodName - fullMethodName - 1; + } + + relativeMethodPath = (char*)malloc(relativeMethodPathLength + 1); + if (relativeMethodPath == NULL) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Failed allocating relative method path"); + result = NULL; + } + else + { + strncpy(relativeMethodPath, fullMethodName, relativeMethodPathLength); + relativeMethodPath[relativeMethodPathLength] = 0; + + /* no slash found, this must be an method */ + result = DecodeAndExecuteModelMethod(commandDecoderInstance, schemaHandle, modelHandle, relativeMethodPath, methodName, methodTree); + + free(relativeMethodPath); + methodName = NULL; + } + break; + } + else + { + /* found a slash, get the child model name */ + size_t modelLength = slashPos - methodName; + char* childModelName = (char*)malloc(modelLength + 1); + if (childModelName == NULL) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Failed allocating child model name"); + result = NULL; + break; + } + else + { + strncpy(childModelName, methodName, modelLength); + childModelName[modelLength] = 0; + + /* find the model */ + modelHandle = Schema_GetModelModelByName(modelHandle, childModelName); + if (modelHandle == NULL) + { + /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/ + LogError("Getting the model %s failed", childModelName); + free(childModelName); + result = NULL; + break; + } + else + { + free(childModelName); + methodName = slashPos + 1; + result = NULL; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/ + } + } + } + } while (methodName != NULL); + return result; +} + +static EXECUTE_COMMAND_RESULT DecodeCommand(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, MULTITREE_HANDLE commandNode) +{ + EXECUTE_COMMAND_RESULT result; + SCHEMA_HANDLE schemaHandle; + + /* Codes_SRS_COMMAND_DECODER_99_022:[ CommandDecoder shall use the Schema APIs to obtain the information about the entity set name and namespace] */ + if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL) + { + /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + LogError("Getting schema information failed"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + const char* actionName; + MULTITREE_HANDLE nameTreeNode; + + /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */ + /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "name" of the command JSON.] */ + if ((MultiTree_GetChildByName(commandNode, "Name", &nameTreeNode) != MULTITREE_OK) || + (MultiTree_GetValue(nameTreeNode, (const void **)&actionName) != MULTITREE_OK)) + { + /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + LogError("Getting action name failed."); + result = EXECUTE_COMMAND_ERROR; + } + else if (strlen(actionName) < 2) + { + /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */ + LogError("Invalid action name."); + result = EXECUTE_COMMAND_ERROR; + } + else + { + actionName++; + result = ScanActionPathAndExecuteAction(commandDecoderInstance, schemaHandle, actionName, commandNode); + } + } + return result; +} + +static METHODRETURN_HANDLE DecodeMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, const char* fullMethodName, MULTITREE_HANDLE methodTree) +{ + METHODRETURN_HANDLE result; + SCHEMA_HANDLE schemaHandle; + + /*Codes_SRS_COMMAND_DECODER_02_017: [ CommandDecoder_ExecuteMethod shall get the SCHEMA_HANDLE associated with the modelHandle passed at CommandDecoder_Create. ]*/ + if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL) + { + LogError("Getting schema information failed"); + result = NULL; + } + else + { + result = ScanMethodPathAndExecuteMethod(commandDecoderInstance, schemaHandle, fullMethodName, methodTree); + + } + return result; +} + +/*Codes_SRS_COMMAND_DECODER_01_009: [Whenever CommandDecoder_ExecuteCommand is the command shall be decoded and further dispatched to the actionCallback passed in CommandDecoder_Create.]*/ +EXECUTE_COMMAND_RESULT CommandDecoder_ExecuteCommand(COMMAND_DECODER_HANDLE handle, const char* command) +{ + EXECUTE_COMMAND_RESULT result; + COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle; + /*Codes_SRS_COMMAND_DECODER_01_010: [If either the buffer or the receiveCallbackContext argument is NULL, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + if ( + (command == NULL) || + (commandDecoderInstance == NULL) + ) + { + LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* command=%p", handle, command); + result = EXECUTE_COMMAND_ERROR; + } + else + { + size_t size = strlen(command); + char* commandJSON; + + /* Codes_SRS_COMMAND_DECODER_01_011: [If the size of the command is 0 then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + if (size == 0) + { + LogError("Failed because command size is zero"); + result = EXECUTE_COMMAND_ERROR; + } + /*Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/ + else if ((commandJSON = (char*)malloc(size + 1)) == NULL) + { + LogError("Failed to allocate temporary storage for the commands JSON"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + MULTITREE_HANDLE commandsTree; + + (void)memcpy(commandJSON, command, size); + commandJSON[size] = '\0'; + + /* Codes_SRS_COMMAND_DECODER_01_012: [CommandDecoder shall decode the command JSON contained in buffer to a multi-tree by using JSONDecoder_JSON_To_MultiTree.] */ + if (JSONDecoder_JSON_To_MultiTree(commandJSON, &commandsTree) != JSON_DECODER_OK) + { + /* Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */ + LogError("Decoding JSON to a multi tree failed"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + result = DecodeCommand(commandDecoderInstance, commandsTree); + + /* Codes_SRS_COMMAND_DECODER_01_016: [CommandDecoder shall ensure that the multi-tree resulting from JSONDecoder_JSON_To_MultiTree is freed after the commands are executed.] */ + MultiTree_Destroy(commandsTree); + } + + free(commandJSON); + } + } + return result; +} + +METHODRETURN_HANDLE CommandDecoder_ExecuteMethod(COMMAND_DECODER_HANDLE handle, const char* fullMethodName, const char* methodPayload) +{ + METHODRETURN_HANDLE result; + /*Codes_SRS_COMMAND_DECODER_02_014: [ If handle is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/ + /*Codes_SRS_COMMAND_DECODER_02_015: [ If fulMethodName is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/ + if ( + (handle == NULL) || + (fullMethodName == NULL) /*methodPayload can be NULL*/ + ) + { + LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* fullMethodName=%p", handle, fullMethodName); + result = NULL; + } + else + { + COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle; + /*Codes_SRS_COMMAND_DECODER_02_025: [ If methodCallback is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/ + if (commandDecoderInstance->methodCallback == NULL) + { + LogError("unable to execute a method when the methodCallback passed in CommandDecoder_Create is NULL"); + result = NULL; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_016: [ If methodPayload is not NULL then CommandDecoder_ExecuteMethod shall build a MULTITREE_HANDLE out of methodPayload. ]*/ + if (methodPayload == NULL) + { + result = DecodeMethod(commandDecoderInstance, fullMethodName, NULL); + } + else + { + char* methodJSON; + + if (mallocAndStrcpy_s(&methodJSON, methodPayload) != 0) + { + LogError("Failed to allocate temporary storage for the method JSON"); + result = NULL; + } + else + { + MULTITREE_HANDLE methodTree; + if (JSONDecoder_JSON_To_MultiTree(methodJSON, &methodTree) != JSON_DECODER_OK) + { + LogError("Decoding JSON to a multi tree failed"); + result = NULL; + } + else + { + result = DecodeMethod(commandDecoderInstance, fullMethodName, methodTree); + MultiTree_Destroy(methodTree); + } + free(methodJSON); + } + } + } + } + return result; +} + + +COMMAND_DECODER_HANDLE CommandDecoder_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, ACTION_CALLBACK_FUNC actionCallback, void* actionCallbackContext, METHOD_CALLBACK_FUNC methodCallback, void* methodCallbackContext) +{ + COMMAND_DECODER_HANDLE_DATA* result; + /* Codes_SRS_COMMAND_DECODER_99_019:[ For all exposed APIs argument validity checks shall precede other checks.] */ + /* Codes_SRS_COMMAND_DECODER_01_003: [ If modelHandle is NULL, CommandDecoder_Create shall return NULL. ]*/ + if (modelHandle == NULL) + { + LogError("Invalid arguments: SCHEMA_MODEL_TYPE_HANDLE modelHandle=%p, ACTION_CALLBACK_FUNC actionCallback=%p, void* actionCallbackContext=%p, METHOD_CALLBACK_FUNC methodCallback=%p, void* methodCallbackContext=%p", + modelHandle, actionCallback, actionCallbackContext, methodCallback, methodCallbackContext); + result = NULL; + } + else + { + /* Codes_SRS_COMMAND_DECODER_01_001: [CommandDecoder_Create shall create a new instance of a CommandDecoder.] */ + result = malloc(sizeof(COMMAND_DECODER_HANDLE_DATA)); + if (result == NULL) + { + /* Codes_SRS_COMMAND_DECODER_01_004: [If any error is encountered during CommandDecoder_Create CommandDecoder_Create shall return NULL.] */ + /*return as is*/ + } + else + { + result->ModelHandle = modelHandle; + result->ActionCallback = actionCallback; + result->ActionCallbackContext = actionCallbackContext; + result->methodCallback = methodCallback; + result->methodCallbackContext = methodCallbackContext; + } + } + + return result; +} + +void CommandDecoder_Destroy(COMMAND_DECODER_HANDLE commandDecoderHandle) +{ + /* Codes_SRS_COMMAND_DECODER_01_007: [If CommandDecoder_Destroy is called with a NULL handle, CommandDecoder_Destroy shall do nothing.] */ + if (commandDecoderHandle != NULL) + { + COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)commandDecoderHandle; + + /* Codes_SRS_COMMAND_DECODER_01_005: [CommandDecoder_Destroy shall free all resources associated with the commandDecoderHandle instance.] */ + free(commandDecoderInstance); + } +} + +DEFINE_ENUM_STRINGS(AGENT_DATA_TYPE_TYPE, AGENT_DATA_TYPE_TYPE_VALUES); + +/*validates that the multitree (coming from a JSON) is actually a serialization of the model (complete or incomplete)*/ +/*if the serialization contains more than the model, then it fails.*/ +/*if the serialization does not contain mandatory items from the model, it fails*/ +static bool validateModel_vs_Multitree(void* startAddress, SCHEMA_MODEL_TYPE_HANDLE modelHandle, MULTITREE_HANDLE desiredPropertiesTree, size_t offset) +{ + + bool result; + size_t nChildren; + size_t nProcessedChildren = 0; + (void)MultiTree_GetChildCount(desiredPropertiesTree, &nChildren); + for (size_t i = 0;i < nChildren;i++) + { + MULTITREE_HANDLE child; + if (MultiTree_GetChild(desiredPropertiesTree, i, &child) != MULTITREE_OK) + { + LogError("failure in MultiTree_GetChild"); + i = nChildren; + } + else + { + STRING_HANDLE childName = STRING_new(); + if (childName == NULL) + { + LogError("failure to STRING_new"); + i = nChildren; + } + else + { + if (MultiTree_GetName(child, childName) != MULTITREE_OK) + { + LogError("failure to MultiTree_GetName"); + i = nChildren; + } + else + { + const char *childName_str = STRING_c_str(childName); + SCHEMA_MODEL_ELEMENT elementType = Schema_GetModelElementByName(modelHandle, childName_str); + switch (elementType.elementType) + { + default: + { + LogError("INTERNAL ERROR: unexpected function return"); + i = nChildren; + break; + } + case (SCHEMA_PROPERTY): + { + LogError("cannot ingest name (WITH_DATA instead of WITH_DESIRED_PROPERTY): %s", STRING_c_str); + i = nChildren; + break; + } + case (SCHEMA_REPORTED_PROPERTY): + { + LogError("cannot ingest name (WITH_REPORTED_PROPERTY instead of WITH_DESIRED_PROPERTY): %s", STRING_c_str); + i = nChildren; + break; + } + case (SCHEMA_DESIRED_PROPERTY): + { + /*Codes_SRS_COMMAND_DECODER_02_007: [ If the child name corresponds to a desired property then an AGENT_DATA_TYPE shall be constructed from the MULTITREE node. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = elementType.elementHandle.desiredPropertyHandle; + + const char* desiredPropertyType = Schema_GetModelDesiredPropertyType(desiredPropertyHandle); + AGENT_DATA_TYPE output; + if (DecodeValueFromNode(Schema_GetSchemaForModelType(modelHandle), &output, child, desiredPropertyType) != 0) + { + LogError("failure in DecodeValueFromNode"); + i = nChildren; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_008: [ The desired property shall be constructed in memory by calling pfDesiredPropertyFromAGENT_DATA_TYPE. ]*/ + pfDesiredPropertyFromAGENT_DATA_TYPE leFunction = Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(desiredPropertyHandle); + if (leFunction(&output, (char*)startAddress + offset + Schema_GetModelDesiredProperty_offset(desiredPropertyHandle)) != 0) + { + LogError("failure in a function that converts from AGENT_DATA_TYPE to C data"); + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_013: [ If the desired property has a non-NULL pfOnDesiredProperty then it shall be called. ]*/ + pfOnDesiredProperty onDesiredProperty = Schema_GetModelDesiredProperty_pfOnDesiredProperty(desiredPropertyHandle); + if (onDesiredProperty != NULL) + { + onDesiredProperty((char*)startAddress + offset); + } + nProcessedChildren++; + } + Destroy_AGENT_DATA_TYPE(&output); + } + + break; + } + case(SCHEMA_MODEL_IN_MODEL): + { + SCHEMA_MODEL_TYPE_HANDLE modelModel = elementType.elementHandle.modelHandle; + + /*Codes_SRS_COMMAND_DECODER_02_009: [ If the child name corresponds to a model in model then the function shall call itself recursively. ]*/ + if (!validateModel_vs_Multitree(startAddress, modelModel, child, offset + Schema_GetModelModelByName_Offset(modelHandle, childName_str))) + { + LogError("failure in validateModel_vs_Multitree"); + i = nChildren; + } + else + { + /*if the model in model so happened to be a WITH_DESIRED_PROPERTY... (only those has non_NULL pfOnDesiredProperty) */ + /*Codes_SRS_COMMAND_DECODER_02_012: [ If the child model in model has a non-NULL pfOnDesiredProperty then pfOnDesiredProperty shall be called. ]*/ + pfOnDesiredProperty onDesiredProperty = Schema_GetModelModelByName_OnDesiredProperty(modelHandle, childName_str); + if (onDesiredProperty != NULL) + { + onDesiredProperty((char*)startAddress + offset); + } + + nProcessedChildren++; + } + + break; + } + + } /*switch*/ + } + STRING_delete(childName); + } + } + } + + if(nProcessedChildren == nChildren) + { + /*Codes_SRS_COMMAND_DECODER_02_010: [ If the complete MULTITREE has been parsed then CommandDecoder_IngestDesiredProperties shall succeed and return EXECUTE_COMMAND_SUCCESS. ]*/ + result = true; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_011: [ Otherwise CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_FAILED. ]*/ + LogError("not all constituents of the JSON have been ingested"); + result = false; + } + return result; +} + +static EXECUTE_COMMAND_RESULT DecodeDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE_DATA* handle, MULTITREE_HANDLE desiredPropertiesTree) +{ + /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/ + return validateModel_vs_Multitree(startAddress, handle->ModelHandle, desiredPropertiesTree, 0 )?EXECUTE_COMMAND_SUCCESS:EXECUTE_COMMAND_FAILED; +} + +/* Raw JSON has properties we don't need; potentially nodes other than "desired" for full TWIN as well as a $version we don't pass to callees */ +static bool RemoveUnneededTwinProperties(MULTITREE_HANDLE initialParsedTree, bool parseDesiredNode, MULTITREE_HANDLE *desiredPropertiesTree) +{ + MULTITREE_HANDLE updateTree; + bool result; + + if (parseDesiredNode) + { + /*Codes_SRS_COMMAND_DECODER_02_014: [ If parseDesiredNode is TRUE, parse only the `desired` part of JSON tree ]*/ + if (MultiTree_GetChildByName(initialParsedTree, "desired", &updateTree) != MULTITREE_OK) + { + LogError("Unable to find 'desired' in tree"); + return false; + } + } + else + { + // Tree already starts on node we want so just use it. + updateTree = initialParsedTree; + } + + /*Codes_COMMAND_DECODER_02_015: [ Remove '$version' string from node, if it is present. It not being present is not an error ]*/ + MULTITREE_RESULT deleteChildResult = MultiTree_DeleteChild(updateTree, "$version"); + if ((deleteChildResult == MULTITREE_OK) || (deleteChildResult == MULTITREE_CHILD_NOT_FOUND)) + { + *desiredPropertiesTree = updateTree; + result = true; + } + else + { + *desiredPropertiesTree = NULL; + result = false; + } + + return result; +} + +EXECUTE_COMMAND_RESULT CommandDecoder_IngestDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE handle, const char* jsonPayload, bool parseDesiredNode) +{ + EXECUTE_COMMAND_RESULT result; + + /*Codes_SRS_COMMAND_DECODER_02_001: [ If startAddress is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/ + /*Codes_SRS_COMMAND_DECODER_02_002: [ If handle is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/ + /*Codes_SRS_COMMAND_DECODER_02_003: [ If jsonPayload is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/ + if( + (startAddress == NULL) || + (handle == NULL) || + (jsonPayload == NULL) + ) + { + LogError("invalid argument COMMAND_DECODER_HANDLE handle=%p, const char* jsonPayload=%p", handle, jsonPayload); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_004: [ CommandDecoder_IngestDesiredProperties shall clone desiredProperties. ]*/ + char* copy; + if (mallocAndStrcpy_s(©, jsonPayload) != 0) + { + LogError("failure in mallocAndStrcpy_s"); + result = EXECUTE_COMMAND_FAILED; + } + else + { + /*Codes_SRS_COMMAND_DECODER_02_005: [ CommandDecoder_IngestDesiredProperties shall create a MULTITREE_HANDLE ouf of the clone of desiredProperties. ]*/ + MULTITREE_HANDLE initialParsedTree; + MULTITREE_HANDLE desiredPropertiesTree; + + if (JSONDecoder_JSON_To_MultiTree(copy, &initialParsedTree) != JSON_DECODER_OK) + { + LogError("Decoding JSON to a multi tree failed"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + if (RemoveUnneededTwinProperties(initialParsedTree, parseDesiredNode, &desiredPropertiesTree) == false) + { + LogError("Removing unneeded twin properties failed"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle; + + /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/ + result = DecodeDesiredProperties(startAddress, commandDecoderInstance, desiredPropertiesTree); + + // Do NOT free desiredPropertiesTree. It is only a pointer into initialParsedTree. + MultiTree_Destroy(initialParsedTree); + } + } + free(copy); + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/datamarshaller.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,365 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> /*for free*/ +#include "azure_c_shared_utility/gballoc.h" + +#include <stdbool.h> +#include "datamarshaller.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "schema.h" +#include "jsonencoder.h" +#include "agenttypesystem.h" +#include "azure_c_shared_utility/xlogging.h" +#include "parson.h" +#include "azure_c_shared_utility/vector.h" + +DEFINE_ENUM_STRINGS(DATA_MARSHALLER_RESULT, DATA_MARSHALLER_RESULT_VALUES); + +#define LOG_DATA_MARSHALLER_ERROR \ + LogError("(result = %s)", ENUM_TO_STRING(DATA_MARSHALLER_RESULT, result)); + +typedef struct DATA_MARSHALLER_HANDLE_DATA_TAG +{ + SCHEMA_MODEL_TYPE_HANDLE ModelHandle; + bool IncludePropertyPath; +} DATA_MARSHALLER_HANDLE_DATA; + +static int NoCloneFunction(void** destination, const void* source) +{ + *destination = (void*)source; + return 0; +} + +static void NoFreeFunction(void* value) +{ + (void)value; +} + +DATA_MARSHALLER_HANDLE DataMarshaller_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, bool includePropertyPath) +{ + DATA_MARSHALLER_HANDLE_DATA* result; + + /*Codes_SRS_DATA_MARSHALLER_99_019:[ DataMarshaller_Create shall return NULL if any argument is NULL.]*/ + if (modelHandle == NULL) + { + result = NULL; + LogError("(result = %s)", ENUM_TO_STRING(DATA_MARSHALLER_RESULT, DATA_MARSHALLER_INVALID_ARG)); + } + else if ((result = (DATA_MARSHALLER_HANDLE_DATA*)malloc(sizeof(DATA_MARSHALLER_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_DATA_MARSHALLER_99_048:[On any other errors not explicitly specified, DataMarshaller_Create shall return NULL.] */ + result = NULL; + LogError("(result = %s)", ENUM_TO_STRING(DATA_MARSHALLER_RESULT, DATA_MARSHALLER_ERROR)); + } + else + { + /*everything ok*/ + /*Codes_SRS_DATA_MARSHALLER_99_018:[ DataMarshaller_Create shall create a new DataMarshaller instance and on success it shall return a non NULL handle.]*/ + result->ModelHandle = modelHandle; + result->IncludePropertyPath = includePropertyPath; + } + return result; +} + +void DataMarshaller_Destroy(DATA_MARSHALLER_HANDLE dataMarshallerHandle) +{ + /* Codes_SRS_DATA_MARSHALLER_99_024:[ When called with a NULL handle, DataMarshaller_Destroy shall do nothing.] */ + if (dataMarshallerHandle != NULL) + { + /* Codes_SRS_DATA_MARSHALLER_99_022:[ DataMarshaller_Destroy shall free all resources associated with the dataMarshallerHandle argument.] */ + DATA_MARSHALLER_HANDLE_DATA* dataMarshallerInstance = (DATA_MARSHALLER_HANDLE_DATA*)dataMarshallerHandle; + free(dataMarshallerInstance); + } +} + +DATA_MARSHALLER_RESULT DataMarshaller_SendData(DATA_MARSHALLER_HANDLE dataMarshallerHandle, size_t valueCount, const DATA_MARSHALLER_VALUE* values, unsigned char** destination, size_t* destinationSize) +{ + DATA_MARSHALLER_HANDLE_DATA* dataMarshallerInstance = (DATA_MARSHALLER_HANDLE_DATA*)dataMarshallerHandle; + DATA_MARSHALLER_RESULT result; + MULTITREE_HANDLE treeHandle; + + /* Codes_SRS_DATA_MARSHALLER_99_034:[All argument checks shall be performed before calling any other modules.] */ + /* Codes_SRS_DATA_MARSHALLER_99_004:[ DATA_MARSHALLER_INVALID_ARG shall be returned when the function has detected an invalid parameter (NULL) being passed to the function.] */ + if ((values == NULL) || + (dataMarshallerHandle == NULL) || + (destination == NULL) || + (destinationSize == NULL) || + /* Codes_SRS_DATA_MARSHALLER_99_033:[ DATA_MARSHALLER_INVALID_ARG shall be returned if the valueCount is zero.] */ + (valueCount == 0)) + { + result = DATA_MARSHALLER_INVALID_ARG; + LOG_DATA_MARSHALLER_ERROR + } + else + { + size_t i; + bool includePropertyPath = dataMarshallerInstance->IncludePropertyPath; + /* VS complains wrongly that result is not initialized */ + result = DATA_MARSHALLER_ERROR; + + for (i = 0; i < valueCount; i++) + { + if ((values[i].PropertyPath == NULL) || + (values[i].Value == NULL)) + { + /*Codes_SRS_DATA_MARSHALLER_99_007:[ DATA_MARSHALLER_INVALID_MODEL_PROPERTY shall be returned when any of the items in values contain invalid data]*/ + result = DATA_MARSHALLER_INVALID_MODEL_PROPERTY; + LOG_DATA_MARSHALLER_ERROR + break; + } + + if ((!dataMarshallerInstance->IncludePropertyPath) && + (values[i].Value->type == EDM_COMPLEX_TYPE_TYPE) && + (valueCount > 1)) + { + /* Codes_SRS_DATAMARSHALLER_01_002: [If the includePropertyPath argument passed to DataMarshaller_Create was false and the number of values passed to SendData is greater than 1 and at least one of them is a struct, DataMarshaller_SendData shall fallback to including the complete property path in the output JSON.] */ + includePropertyPath = true; + } + } + + if (i == valueCount) + { + /* Codes_SRS_DATA_MARSHALLER_99_037:[DataMarshaller shall store as MultiTree the data to be encoded by the JSONEncoder module.] */ + if ((treeHandle = MultiTree_Create(NoCloneFunction, NoFreeFunction)) == NULL) + { + /* Codes_SRS_DATA_MARSHALLER_99_035:[DATA_MARSHALLER_MULTITREE_ERROR shall be returned in case any MultiTree API call fails.] */ + result = DATA_MARSHALLER_MULTITREE_ERROR; + LOG_DATA_MARSHALLER_ERROR + } + else + { + size_t j; + result = DATA_MARSHALLER_OK; /* addressing warning in VS compiler */ + /* Codes_SRS_DATA_MARSHALLER_99_038:[For each pair in the values argument, a string : value pair shall exist in the JSON object in the form of propertyName : value.] */ + for (j = 0; j < valueCount; j++) + { + if ((includePropertyPath == false) && (values[j].Value->type == EDM_COMPLEX_TYPE_TYPE)) + { + size_t k; + + /* Codes_SRS_DATAMARSHALLER_01_001: [If the includePropertyPath argument passed to DataMarshaller_Create was false and only one struct is being sent, the relative path of the value passed to DataMarshaller_SendData - including property name - shall be ignored and the value shall be placed at JSON root.] */ + for (k = 0; k < values[j].Value->value.edmComplexType.nMembers; k++) + { + /* Codes_SRS_DATAMARSHALLER_01_004: [In this case the members of the struct shall be added as leafs into the MultiTree, each leaf having the name of the struct member.] */ + if (MultiTree_AddLeaf(treeHandle, values[j].Value->value.edmComplexType.fields[k].fieldName, (void*)values[j].Value->value.edmComplexType.fields[k].value) != MULTITREE_OK) + { + break; + } + } + + if (k < values[j].Value->value.edmComplexType.nMembers) + { + /* Codes_SRS_DATA_MARSHALLER_99_035:[DATA_MARSHALLER_MULTITREE_ERROR shall be returned in case any MultiTree API call fails.] */ + result = DATA_MARSHALLER_MULTITREE_ERROR; + LOG_DATA_MARSHALLER_ERROR + break; + } + } + else + { + /* Codes_SRS_DATA_MARSHALLER_99_039:[ If the includePropertyPath argument passed to DataMarshaller_Create was true each property shall be placed in the appropriate position in the JSON according to its path in the model.] */ + if (MultiTree_AddLeaf(treeHandle, values[j].PropertyPath, (void*)values[j].Value) != MULTITREE_OK) + { + /* Codes_SRS_DATA_MARSHALLER_99_035:[DATA_MARSHALLER_MULTITREE_ERROR shall be returned in case any MultiTree API call fails.] */ + result = DATA_MARSHALLER_MULTITREE_ERROR; + LOG_DATA_MARSHALLER_ERROR + break; + } + } + + } + + if (j == valueCount) + { + STRING_HANDLE payload = STRING_new(); + if (payload == NULL) + { + result = DATA_MARSHALLER_ERROR; + LOG_DATA_MARSHALLER_ERROR + } + else + { + if (JSONEncoder_EncodeTree(treeHandle, payload, (JSON_ENCODER_TOSTRING_FUNC)AgentDataTypes_ToString) != JSON_ENCODER_OK) + { + /* Codes_SRS_DATA_MARSHALLER_99_027:[ DATA_MARSHALLER_JSON_ENCODER_ERROR shall be returned when JSONEncoder returns an error code.] */ + result = DATA_MARSHALLER_JSON_ENCODER_ERROR; + LOG_DATA_MARSHALLER_ERROR + } + else + { + /*Codes_SRS_DATAMARSHALLER_02_007: [DataMarshaller_SendData shall copy in the output parameters *destination, *destinationSize the content and the content length of the encoded JSON tree.] */ + size_t resultSize = STRING_length(payload); + unsigned char* temp = malloc(resultSize); + if (temp == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_99_015:[ DATA_MARSHALLER_ERROR shall be returned in all the other error cases not explicitly defined here.]*/ + result = DATA_MARSHALLER_ERROR; + LOG_DATA_MARSHALLER_ERROR; + } + else + { + (void)memcpy(temp, STRING_c_str(payload), resultSize); + *destination = temp; + *destinationSize = resultSize; + result = DATA_MARSHALLER_OK; + } + } + STRING_delete(payload); + } + } /* if (j==valueCount)*/ + MultiTree_Destroy(treeHandle); + } /* MultiTree_Create */ + } + } + + return result; +} + + +DATA_MARSHALLER_RESULT DataMarshaller_SendData_ReportedProperties(DATA_MARSHALLER_HANDLE dataMarshallerHandle, VECTOR_HANDLE values, unsigned char** destination, size_t* destinationSize) +{ + DATA_MARSHALLER_RESULT result; + /*Codes_SRS_DATA_MARSHALLER_02_021: [ If argument dataMarshallerHandle is NULL then DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_MARSHALLER_02_008: [ If argument values is NULL then DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_MARSHALLER_02_009: [ If argument destination NULL then DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_MARSHALLER_02_010: [ If argument destinationSize NULL then DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_INVALID_ARG. ]*/ + if ( + (dataMarshallerHandle == NULL) || + (values == NULL) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + LogError("invalid argument DATA_MARSHALLER_HANDLE dataMarshallerHandle=%p, VECTOR_HANDLE values=%p, unsigned char** destination=%p, size_t* destinationSize=%p", + dataMarshallerHandle, + values, + destination, + destinationSize); + result = DATA_MARSHALLER_INVALID_ARG; + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_012: [ DataMarshaller_SendData_ReportedProperties shall create an empty JSON_Value. ]*/ + JSON_Value* json = json_value_init_object(); + if (json == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling json_value_init_object"); + result = DATA_MARSHALLER_ERROR; + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_013: [ DataMarshaller_SendData_ReportedProperties shall get the object behind the JSON_Value by calling json_object. ]*/ + JSON_Object* jsonObject = json_object(json); + if (jsonObject == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling json_object"); + result = DATA_MARSHALLER_ERROR; + } + else + { + size_t nReportedProperties = VECTOR_size(values), nProcessedProperties = 0; + + for (size_t i = 0;i < nReportedProperties; i++) + { + DATA_MARSHALLER_VALUE* v = *(DATA_MARSHALLER_VALUE**)VECTOR_element(values, i); + STRING_HANDLE s = STRING_new(); + if (s == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling STRING_new"); + i = nReportedProperties;/*forces loop to break, result is set in the "if" following this for*/ + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_014: [ For every reported property, DataMarshaller_SendData_ReportedProperties shall get the reported property's JSON value (as string) by calling AgentDataTypes_ToString. ]*/ + if (AgentDataTypes_ToString(s, v->Value) != AGENT_DATA_TYPES_OK) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling AgentDataTypes_ToString"); + i = nReportedProperties;/*forces loop to break, result is set in the "if" following this for*/ + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_015: [ DataMarshaller_SendData_ReportedProperties shall import the JSON value (as string) by calling json_parse_string. ]*/ + JSON_Value * rightSide = json_parse_string(STRING_c_str(s)); + if (rightSide == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling json_parse_string"); + i = nReportedProperties;/*forces loop to break, result is set in the "if" following this for*/ + } + else + { + char* leftSide; + if (mallocAndStrcpy_s(&leftSide, v->PropertyPath) != 0) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling mallocAndStrcpy_s"); + json_value_free(rightSide); + i = nReportedProperties;/*forces loop to break, result is set in the "if" following this for*/ + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_016: [ DataMarshaller_SendData_ReportedProperties shall replace all the occurences of / with . in the reported property paths. ]*/ + char *whereIsSlash; + while ((whereIsSlash = strchr(leftSide, '/')) != NULL) + { + *whereIsSlash = '.'; + } + + /*Codes_SRS_DATA_MARSHALLER_02_017: [ DataMarshaller_SendData_ReportedProperties shall use json_object_dotset_value passing the reported property path and the imported json value. ]*/ + /*Codes_SRS_DATA_MARSHALLER_02_011: [ DataMarshaller_SendData_ReportedProperties shall ignore the value of includePropertyPath and shall consider it to be true. ]*/ + if (json_object_dotset_value(jsonObject, leftSide, rightSide) != JSONSuccess) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling json_object_dotset_value"); + json_value_free(rightSide); + i = nReportedProperties;/*forces loop to break, result is set in the "if" following this for*/ + } + else + { + /*all is fine with this property... */ + nProcessedProperties++; + } + free(leftSide); + } + } + } + STRING_delete(s); + } + } + + if (nProcessedProperties != nReportedProperties) + { + result = DATA_MARSHALLER_ERROR; + /*all properties have NOT been processed*/ + /*return result as is*/ + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_018: [ DataMarshaller_SendData_ReportedProperties shall use json_serialize_to_string_pretty to produce the output JSON string that fills out parameters destination and destionationSize. ]*/ + char* temp = json_serialize_to_string_pretty(json); + if (temp == NULL) + { + /*Codes_SRS_DATA_MARSHALLER_02_019: [ If any failure occurs, DataMarshaller_SendData_ReportedProperties shall fail and return DATA_MARSHALLER_ERROR. ]*/ + LogError("failure calling json_serialize_to_string_pretty "); + result = DATA_MARSHALLER_ERROR; + } + else + { + /*Codes_SRS_DATA_MARSHALLER_02_020: [ Otherwise DataMarshaller_SendData_ReportedProperties shall succeed and return DATA_MARSHALLER_OK. ]*/ + *destination = (unsigned char*)temp; + *destinationSize = strlen(temp); + result = DATA_MARSHALLER_OK; + /*all is fine... */ + } + } + } + json_value_free(json); + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/datapublisher.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,571 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include <stdbool.h> +#include "datapublisher.h" +#include "jsonencoder.h" +#include "datamarshaller.h" +#include "agenttypesystem.h" +#include "schema.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/vector.h" + +DEFINE_ENUM_STRINGS(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_RESULT_VALUES) + +#define LOG_DATA_PUBLISHER_ERROR \ + LogError("(result = %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, result)) + +#define DEFAULT_MAX_BUFFER_SIZE 10240 +/* Codes_SRS_DATA_PUBLISHER_99_066:[ A single value shall be used by all instances of DataPublisher.] */ +/* Codes_SRS_DATA_PUBLISHER_99_067:[ Before any call to DataPublisher_SetMaxBufferSize, the default max buffer size shall be equal to 10KB.] */ +static size_t maxBufferSize_ = DEFAULT_MAX_BUFFER_SIZE; + +typedef struct DATA_PUBLISHER_HANDLE_DATA_TAG +{ + DATA_MARSHALLER_HANDLE DataMarshallerHandle; + SCHEMA_MODEL_TYPE_HANDLE ModelHandle; +} DATA_PUBLISHER_HANDLE_DATA; + +typedef struct TRANSACTION_HANDLE_DATA_TAG +{ + DATA_PUBLISHER_HANDLE_DATA* DataPublisherInstance; + size_t ValueCount; + DATA_MARSHALLER_VALUE* Values; +} TRANSACTION_HANDLE_DATA; + +typedef struct REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA_TAG +{ + DATA_PUBLISHER_HANDLE_DATA* DataPublisherInstance; + VECTOR_HANDLE value; /*holds (DATA_MARSHALLER_VALUE*) */ +}REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA; + +DATA_PUBLISHER_HANDLE DataPublisher_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, bool includePropertyPath) +{ + DATA_PUBLISHER_HANDLE_DATA* result; + + /* Codes_SRS_DATA_PUBLISHER_99_042:[ If a NULL argument is passed to it, DataPublisher_Create shall return NULL.] */ + if (modelHandle == NULL) + { + result = NULL; + LogError("(result = %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_INVALID_ARG)); + } + else if ((result = (DATA_PUBLISHER_HANDLE_DATA*)malloc(sizeof(DATA_PUBLISHER_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_DATA_PUBLISHER_99_047:[ For any other error not specified here, DataPublisher_Create shall return NULL.] */ + result = NULL; + LogError("(result = %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_ERROR)); + } + else + { + /* Codes_SRS_DATA_PUBLISHER_99_043:[ DataPublisher_Create shall initialize and hold a handle to a DataMarshaller instance.] */ + /* Codes_SRS_DATA_PUBLISHER_01_001: [DataPublisher_Create shall pass the includePropertyPath argument to DataMarshaller_Create.] */ + if ((result->DataMarshallerHandle = DataMarshaller_Create(modelHandle, includePropertyPath)) == NULL) + { + free(result); + + /* Codes_SRS_DATA_PUBLISHER_99_044:[ If the creation of the DataMarshaller instance fails, DataPublisher_Create shall return NULL.] */ + result = NULL; + LogError("(result = %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_MARSHALLER_ERROR)); + } + else + { + /* Codes_SRS_DATA_PUBLISHER_99_041:[ DataPublisher_Create shall create a new DataPublisher instance and return a non-NULL handle in case of success.] */ + result->ModelHandle = modelHandle; + } + } + + return result; +} + +void DataPublisher_Destroy(DATA_PUBLISHER_HANDLE dataPublisherHandle) +{ + if (dataPublisherHandle != NULL) + { + DATA_PUBLISHER_HANDLE_DATA* dataPublisherInstance = (DATA_PUBLISHER_HANDLE_DATA*)dataPublisherHandle; + DataMarshaller_Destroy(dataPublisherInstance->DataMarshallerHandle); + + free(dataPublisherHandle); + } +} + +TRANSACTION_HANDLE DataPublisher_StartTransaction(DATA_PUBLISHER_HANDLE dataPublisherHandle) +{ + TRANSACTION_HANDLE_DATA* transaction; + + /* Codes_SRS_DATA_PUBLISHER_99_038:[ If DataPublisher_StartTransaction is called with a NULL argument it shall return NULL.] */ + if (dataPublisherHandle == NULL) + { + transaction = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_INVALID_ARG)); + } + else + { + /* Codes_SRS_DATA_PUBLISHER_99_007:[ A call to DataPublisher_StartTransaction shall start a new transaction.] */ + transaction = (TRANSACTION_HANDLE_DATA*)malloc(sizeof(TRANSACTION_HANDLE_DATA)); + if (transaction == NULL) + { + LogError("Allocating transaction failed (Error code: %s)", ENUM_TO_STRING(DATA_PUBLISHER_RESULT, DATA_PUBLISHER_ERROR)); + } + else + { + transaction->ValueCount = 0; + transaction->Values = NULL; + transaction->DataPublisherInstance = (DATA_PUBLISHER_HANDLE_DATA*)dataPublisherHandle; + } + } + + /* Codes_SRS_DATA_PUBLISHER_99_008:[ DataPublisher_StartTransaction shall return a non-NULL handle upon success.] */ + /* Codes_SRS_DATA_PUBLISHER_99_009:[ DataPublisher_StartTransaction shall return NULL upon failure.] */ + return transaction; +} + +DATA_PUBLISHER_RESULT DataPublisher_PublishTransacted(TRANSACTION_HANDLE transactionHandle, const char* propertyPath, const AGENT_DATA_TYPE* data) +{ + DATA_PUBLISHER_RESULT result; + char* propertyPathCopy; + + /* Codes_SRS_DATA_PUBLISHER_99_017:[ When one or more NULL parameter(s) are specified, DataPublisher_PublishTransacted is called with a NULL transactionHandle, it shall return DATA_PUBLISHER_INVALID_ARG.] */ + if ((transactionHandle == NULL) || + (propertyPath == NULL) || + (data == NULL)) + { + result = DATA_PUBLISHER_INVALID_ARG; + LOG_DATA_PUBLISHER_ERROR; + } + else if (mallocAndStrcpy_s(&propertyPathCopy, propertyPath) != 0) + { + /* Codes_SRS_DATA_PUBLISHER_99_020:[ For any errors not explicitly mentioned here the DataPublisher APIs shall return DATA_PUBLISHER_ERROR.] */ + result = DATA_PUBLISHER_ERROR; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + TRANSACTION_HANDLE_DATA* transaction = (TRANSACTION_HANDLE_DATA*)transactionHandle; + AGENT_DATA_TYPE* propertyValue; + + if (!Schema_ModelPropertyByPathExists(transaction->DataPublisherInstance->ModelHandle, propertyPath)) + { + free(propertyPathCopy); + + /* Codes_SRS_DATA_PUBLISHER_99_040:[ When propertyPath does not exist in the supplied model, DataPublisher_Publish shall return DATA_PUBLISHER_SCHEMA_FAILED without dispatching data.] */ + result = DATA_PUBLISHER_SCHEMA_FAILED; + LOG_DATA_PUBLISHER_ERROR; + } + else if ((propertyValue = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE))) == NULL) + { + free(propertyPathCopy); + + /* Codes_SRS_DATA_PUBLISHER_99_020:[ For any errors not explicitly mentioned here the DataPublisher APIs shall return DATA_PUBLISHER_ERROR.] */ + result = DATA_PUBLISHER_ERROR; + LOG_DATA_PUBLISHER_ERROR; + } + else if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(propertyValue, data) != AGENT_DATA_TYPES_OK) + { + free(propertyPathCopy); + free(propertyValue); + + /* Codes_SRS_DATA_PUBLISHER_99_028:[ If creating the copy fails then DATA_PUBLISHER_AGENT_DATA_TYPES_ERROR shall be returned.] */ + result = DATA_PUBLISHER_AGENT_DATA_TYPES_ERROR; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + size_t i; + DATA_MARSHALLER_VALUE* propertySlot = NULL; + + /* Codes_SRS_DATA_PUBLISHER_99_019:[ If the same property is associated twice with a transaction, then the last value shall be kept associated with the transaction.] */ + for (i = 0; i < transaction->ValueCount; i++) + { + if (strcmp(transaction->Values[i].PropertyPath, propertyPath) == 0) + { + propertySlot = &transaction->Values[i]; + break; + } + } + + if (propertySlot == NULL) + { + DATA_MARSHALLER_VALUE* newValues = (DATA_MARSHALLER_VALUE*)realloc(transaction->Values, sizeof(DATA_MARSHALLER_VALUE)* (transaction->ValueCount + 1)); + if (newValues != NULL) + { + transaction->Values = newValues; + propertySlot = &transaction->Values[transaction->ValueCount]; + propertySlot->Value = NULL; + propertySlot->PropertyPath = NULL; + transaction->ValueCount++; + } + } + + if (propertySlot == NULL) + { + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)propertyValue); + free(propertyValue); + free(propertyPathCopy); + + /* Codes_SRS_DATA_PUBLISHER_99_020:[ For any errors not explicitly mentioned here the DataPublisher APIs shall return DATA_PUBLISHER_ERROR.] */ + result = DATA_PUBLISHER_ERROR; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + if (propertySlot->Value != NULL) + { + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)propertySlot->Value); + free((AGENT_DATA_TYPE*)propertySlot->Value); + } + if (propertySlot->PropertyPath != NULL) + { + char* existingValue = (char*)propertySlot->PropertyPath; + free(existingValue); + } + + /* Codes_SRS_DATA_PUBLISHER_99_016:[ When DataPublisher_PublishTransacted is invoked, DataPublisher shall associate the data with the transaction identified by the transactionHandle argument and return DATA_PUBLISHER_OK. No data shall be dispatched at the time of the call.] */ + propertySlot->PropertyPath = propertyPathCopy; + propertySlot->Value = propertyValue; + + result = DATA_PUBLISHER_OK; + } + } + } + + return result; +} + +DATA_PUBLISHER_RESULT DataPublisher_EndTransaction(TRANSACTION_HANDLE transactionHandle, unsigned char** destination, size_t* destinationSize) +{ + DATA_PUBLISHER_RESULT result; + + /*Codes_SRS_DATA_PUBLISHER_02_006: [If the destination argument is NULL, DataPublisher_EndTransaction shall return DATA_PUBLISHER_INVALID_ARG.] */ + /*Codes_SRS_DATA_PUBLISHER_02_007: [If the destinationSize argument is NULL, DataPublisher_EndTransaction shall return DATA_PUBLISHER_INVALID_ARG.] */ + if ( + (transactionHandle == NULL) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + /* Codes_SRS_DATA_PUBLISHER_99_011:[ If the transactionHandle argument is NULL, DataPublisher_EndTransaction shall return DATA_PUBLISHER_INVALID_ARG.] */ + result = DATA_PUBLISHER_INVALID_ARG; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + TRANSACTION_HANDLE_DATA* transaction = (TRANSACTION_HANDLE_DATA*)transactionHandle; + + if (transaction->ValueCount == 0) + { + /* Codes_SRS_DATA_PUBLISHER_99_024:[ If no values have been associated with the transaction, no data shall be dispatched + to DataMarshaller, the transaction shall be discarded and DataPublisher_EndTransaction shall return DATA_PUBLISHER_EMPTY_TRANSACTION.] */ + result = DATA_PUBLISHER_EMPTY_TRANSACTION; + LOG_DATA_PUBLISHER_ERROR; + } + /* Codes_SRS_DATA_PUBLISHER_99_010:[ A call to DataPublisher_EndTransaction shall mark the end of a transaction and, trigger a dispatch of all the data grouped by that transaction.] */ + else if (DataMarshaller_SendData(transaction->DataPublisherInstance->DataMarshallerHandle, transaction->ValueCount, transaction->Values, destination, destinationSize) != DATA_MARSHALLER_OK) + { + /* Codes_SRS_DATA_PUBLISHER_99_025:[ When the DataMarshaller_SendData call fails, DataPublisher_EndTransaction shall return DATA_PUBLISHER_MARSHALLER_ERROR.] */ + result = DATA_PUBLISHER_MARSHALLER_ERROR; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + /* Codes_SRS_DATA_PUBLISHER_99_026:[ On success, DataPublisher_EndTransaction shall return DATA_PUBLISHER_OK.] */ + result = DATA_PUBLISHER_OK; + } + + /* Codes_SRS_DATA_PUBLISHER_99_012:[ DataPublisher_EndTransaction shall dispose of any resources associated with the transaction.] */ + (void)DataPublisher_CancelTransaction(transactionHandle); + } + + return result; +} + +DATA_PUBLISHER_RESULT DataPublisher_CancelTransaction(TRANSACTION_HANDLE transactionHandle) +{ + DATA_PUBLISHER_RESULT result; + + if (transactionHandle == NULL) + { + /* Codes_SRS_DATA_PUBLISHER_99_014:[ If the transactionHandle argument is NULL DataPublisher_CancelTransaction shall return DATA_PUBLISHER_INVALID_ARG.] */ + result = DATA_PUBLISHER_INVALID_ARG; + LOG_DATA_PUBLISHER_ERROR; + } + else + { + TRANSACTION_HANDLE_DATA* transaction = (TRANSACTION_HANDLE_DATA*)transactionHandle; + size_t i; + + /* Codes_SRS_DATA_PUBLISHER_99_015:[ DataPublisher_CancelTransaction shall dispose of any resources associated with the transaction.] */ + for (i = 0; i < transaction->ValueCount; i++) + { + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)transaction->Values[i].Value); + free((char*)transaction->Values[i].PropertyPath); + free((AGENT_DATA_TYPE*)transaction->Values[i].Value); + } + + /* Codes_SRS_DATA_PUBLISHER_99_015:[ DataPublisher_CancelTransaction shall dispose of any resources associated with the transaction.] */ + free(transaction->Values); + free(transaction); + + /* Codes_SRS_DATA_PUBLISHER_99_013:[ A call to DataPublisher_CancelTransaction shall dispose of the transaction without dispatching + the data to the DataMarshaller module and it shall return DATA_PUBLISHER_OK.] */ + result = DATA_PUBLISHER_OK; + } + + return result; +} + +/* Codes_SRS_DATA_PUBLISHER_99_065:[ DataPublisher_SetMaxBufferSize shall directly update the value used to limit how much data (in bytes) can be buffered in the BufferStorage instance.] */ +void DataPublisher_SetMaxBufferSize(size_t value) +{ + maxBufferSize_ = value; +} + +/* Codes_SRS_DATA_PUBLISHER_99_069:[ DataMarshaller_GetMaxBufferSize shall return the current max buffer size value used by any new instance of DataMarshaller.] */ +size_t DataPublisher_GetMaxBufferSize(void) +{ + return maxBufferSize_; +} + +REPORTED_PROPERTIES_TRANSACTION_HANDLE DataPublisher_CreateTransaction_ReportedProperties(DATA_PUBLISHER_HANDLE dataPublisherHandle) +{ + REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA* result; + /*Codes_SRS_DATA_PUBLISHER_02_027: [ If argument dataPublisherHandle is NULL then DataPublisher_CreateTransaction_ReportedProperties shall fail and return NULL. ]*/ + if (dataPublisherHandle == NULL) + { + LogError("invalid argument DATA_PUBLISHER_HANDLE dataPublisherHandle=%p", dataPublisherHandle); + result = NULL; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_028: [ DataPublisher_CreateTransaction_ReportedProperties shall create a VECTOR_HANDLE holding the individual elements of the transaction (DATA_MARSHALLER_VALUE). ]*/ + result = (REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA*)malloc(sizeof(REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_DATA_PUBLISHER_02_029: [ If any error occurs then DataPublisher_CreateTransaction_ReportedProperties shall fail and return NULL. ]*/ + LogError("unable to malloc"); + /*return as is */ + } + else + { + result->value = VECTOR_create(sizeof(DATA_MARSHALLER_VALUE*)); + if (result->value == NULL) + { + /*Codes_SRS_DATA_PUBLISHER_02_029: [ If any error occurs then DataPublisher_CreateTransaction_ReportedProperties shall fail and return NULL. ]*/ + LogError("unable to VECTOR_create"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_030: [ Otherwise DataPublisher_CreateTransaction_ReportedProperties shall succeed and return a non-NULL handle. ]*/ + result->DataPublisherInstance = dataPublisherHandle; + } + } + } + + return result; +} + +static bool reportedPropertyExistsByPath(const void* element, const void* value) +{ + DATA_MARSHALLER_VALUE* dataMarshallerValue = *(DATA_MARSHALLER_VALUE**)element; + return (strcmp(dataMarshallerValue->PropertyPath, (const char*)value) == 0); +} + +DATA_PUBLISHER_RESULT DataPublisher_PublishTransacted_ReportedProperty(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle, const char* reportedPropertyPath, const AGENT_DATA_TYPE* data) +{ + DATA_PUBLISHER_RESULT result; + /*Codes_SRS_DATA_PUBLISHER_02_009: [ If argument transactionHandle is NULL then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_PUBLISHER_02_010: [ If argument reportedPropertyPath is NULL then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_PUBLISHER_02_011: [ If argument data is NULL then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + if ( + (transactionHandle == NULL) || + (reportedPropertyPath == NULL) || + (data == NULL) + ) + { + LogError("invalid argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p, const char* reportedPropertyPath=%p, const AGENT_DATA_TYPE* data=%p", transactionHandle, reportedPropertyPath, data); + result = DATA_PUBLISHER_INVALID_ARG; + } + else + { + REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA* handleData = (REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA*)transactionHandle; + /*Codes_SRS_DATA_PUBLISHER_02_012: [ DataPublisher_PublishTransacted_ReportedProperty shall verify that a reported property having the path reportedPropertyPath exists in the model by calling Schema_ModelReportedPropertyByPathExists ]*/ + if (!Schema_ModelReportedPropertyByPathExists(handleData->DataPublisherInstance->ModelHandle, reportedPropertyPath)) + { + /*Codes_SRS_DATA_PUBLISHER_02_013: [ If a reported property with path reportedPropertyPath does not exist in the model then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + LogError("unable to find a reported property by path \"%s\"", reportedPropertyPath); + result = DATA_PUBLISHER_INVALID_ARG; + } + else + { + DATA_MARSHALLER_VALUE** existingValue = VECTOR_find_if(handleData->value, reportedPropertyExistsByPath, reportedPropertyPath); + if(existingValue != NULL) + { + /*Codes_SRS_DATA_PUBLISHER_02_014: [ If the same (by reportedPropertypath) reported property has already been added to the transaction, then DataPublisher_PublishTransacted_ReportedProperty shall overwrite the previous reported property. ]*/ + AGENT_DATA_TYPE *clone = (AGENT_DATA_TYPE *)malloc(sizeof(AGENT_DATA_TYPE)); + if(clone == NULL) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to malloc"); + result = DATA_PUBLISHER_ERROR; + } + else + { + if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE(clone, data) != AGENT_DATA_TYPES_OK) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE"); + free(clone); + result = DATA_PUBLISHER_ERROR; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_017: [ Otherwise DataPublisher_PublishTransacted_ReportedProperty shall succeed and return DATA_PUBLISHER_OK. ]*/ + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)((*existingValue)->Value)); + free((void*)((*existingValue)->Value)); + (*existingValue)->Value = clone; + result = DATA_PUBLISHER_OK; + } + } + } + else + { + /*totally new reported property*/ + DATA_MARSHALLER_VALUE* newValue = (DATA_MARSHALLER_VALUE*)malloc(sizeof(DATA_MARSHALLER_VALUE)); + if (newValue == NULL) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to malloc"); + result = DATA_PUBLISHER_ERROR; + } + else + { + if (mallocAndStrcpy_s((char**)&(newValue->PropertyPath), reportedPropertyPath) != 0) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + free(newValue); + result = DATA_PUBLISHER_ERROR; + } + else + { + if ((newValue->Value = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE))) == NULL) + { + LogError("unable to malloc"); + free((void*)newValue->PropertyPath); + free(newValue); + result = DATA_PUBLISHER_ERROR; + } + else + { + if (Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)newValue->Value, data) != AGENT_DATA_TYPES_OK) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE"); + free((void*)newValue->Value); + free((void*)newValue->PropertyPath); + free(newValue); + result = DATA_PUBLISHER_ERROR; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_015: [ DataPublisher_PublishTransacted_ReportedProperty shall add a new DATA_MARSHALLER_VALUE to the VECTOR_HANDLE. ]*/ + if (VECTOR_push_back(handleData->value, &newValue, 1) != 0) + { + /*Codes_SRS_DATA_PUBLISHER_02_016: [ If any error occurs then DataPublisher_PublishTransacted_ReportedProperty shall fail and return DATA_PUBLISHER_ERROR. */ + LogError("unable to VECTOR_push_back"); + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)newValue->Value); + free((void*)newValue->Value); + free((void*)newValue->PropertyPath); + free(newValue); + result = DATA_PUBLISHER_ERROR; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_017: [ Otherwise DataPublisher_PublishTransacted_ReportedProperty shall succeed and return DATA_PUBLISHER_OK. ]*/ + result = DATA_PUBLISHER_OK; + } + } + } + } + } + } + } + } + return result; +} + +DATA_PUBLISHER_RESULT DataPublisher_CommitTransaction_ReportedProperties(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle, unsigned char** destination, size_t* destinationSize) +{ + DATA_PUBLISHER_RESULT result; + /*Codes_SRS_DATA_PUBLISHER_02_019: [ If argument transactionHandle is NULL then DataPublisher_CommitTransaction_ReportedProperties shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_PUBLISHER_02_020: [ If argument destination is NULL then DataPublisher_CommitTransaction_ReportedProperties shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + /*Codes_SRS_DATA_PUBLISHER_02_021: [ If argument destinationSize NULL then DataPublisher_CommitTransaction_ReportedProperties shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + if ( + (transactionHandle == NULL) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + LogError("invalid argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p, unsigned char** destination=%p, size_t* destinationSize=%p", transactionHandle, destination, destinationSize); + result = DATA_PUBLISHER_INVALID_ARG; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_031: [ If the transaction contains zero elements then DataPublisher_CommitTransaction_ReportedProperties shall fail and return DATA_PUBLISHER_INVALID_ARG. ]*/ + REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA* handle = (REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA*)transactionHandle; + if (VECTOR_size(handle->value) == 0) + { + LogError("cannot commit empty transaction"); + result = DATA_PUBLISHER_INVALID_ARG; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_022: [ DataPublisher_CommitTransaction_ReportedProperties shall call DataMarshaller_SendData_ReportedProperties providing the VECTOR_HANDLE holding the transacted reported properties, destination and destinationSize. ]*/ + if (DataMarshaller_SendData_ReportedProperties(handle->DataPublisherInstance->DataMarshallerHandle, handle->value, destination, destinationSize) != DATA_MARSHALLER_OK) + { + /*Codes_SRS_DATA_PUBLISHER_02_023: [ If any error occurs then DataPublisher_CommitTransaction_ReportedProperties shall fail and return DATA_PUBLISHER_ERROR. ]*/ + LogError("unable to DataMarshaller_SendData_ReportedProperties"); + result = DATA_PUBLISHER_ERROR; + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_024: [ Otherwise DataPublisher_CommitTransaction_ReportedProperties shall succeed and return DATA_PUBLISHER_OK. ]*/ + result = DATA_PUBLISHER_OK; + } + } + } + + return result; +} + +void DataPublisher_DestroyTransaction_ReportedProperties(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle) +{ + /*Codes_SRS_DATA_PUBLISHER_02_025: [ If argument transactionHandle is NULL then DataPublisher_DestroyTransaction_ReportedProperties shall return. ]*/ + if (transactionHandle == NULL) + { + LogError("invalig argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p", transactionHandle); + } + else + { + /*Codes_SRS_DATA_PUBLISHER_02_026: [ Otherwise DataPublisher_DestroyTransaction_ReportedProperties shall free all resources associated with the reported properties transactionHandle. ]*/ + REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA* handleData = (REPORTED_PROPERTIES_TRANSACTION_HANDLE_DATA*)transactionHandle; + size_t i, nReportedProperties; + nReportedProperties = VECTOR_size(handleData->value); + for (i = 0;i < nReportedProperties;i++) + { + DATA_MARSHALLER_VALUE *value = *(DATA_MARSHALLER_VALUE**)VECTOR_element(handleData->value, i); + Destroy_AGENT_DATA_TYPE((AGENT_DATA_TYPE*)value->Value); + free((void*)value->Value); + free((void*)value->PropertyPath); + free((void*)value); + } + VECTOR_destroy(handleData->value); + free(handleData); + } + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/dataserializer.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/gballoc.h" + +#include "dataserializer.h" +#include "azure_c_shared_utility/xlogging.h" + +DEFINE_ENUM_STRINGS(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_RESULT_VALUES); + +BUFFER_HANDLE DataSerializer_Encode(MULTITREE_HANDLE multiTreeHandle, DATA_SERIALIZER_MULTITREE_TYPE dataType, DATA_SERIALIZER_ENCODE_FUNC encodeFunc) +{ + BUFFER_HANDLE pBuffer; + + /* Codes_SRS_DATA_SERIALIZER_07_003: [NULL shall be returned when an invalid parameter is supplied.] */ + if (multiTreeHandle == NULL || encodeFunc == NULL) + { + pBuffer = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_INVALID_ARG) ); + } + else + { + /* Codes_SRS_DATA_SERIALIZER_07_009: [DataSerializer_Encode function shall call into the given DATA_SERIALIZER_ENCODE_FUNC callback with a valid BUFFER object and valid MULTITREE_HANDLE object.] */ + pBuffer = encodeFunc(multiTreeHandle, dataType); + if (pBuffer == NULL) + { + /* Codes_SRS_DATA_SERIALIZER_07_010: [Upon a DATA_SERIALIZER_ENCODE_FUNC failure the DataSerializer_Encode function shall return NULL.] */ + LogError("(Error code: %s)", ENUM_TO_STRING(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_ERROR) ); + } + } + /* Codes_SRS_DATA_SERIALIZER_07_002: [DataSerializer_Encode shall return a valid BUFFER_HANDLE when the function executes successfully.] */ + return pBuffer; +} + +MULTITREE_HANDLE DataSerializer_Decode(BUFFER_HANDLE data, DATA_SERIALIZER_DECODE_FUNC decodeFunc) +{ + MULTITREE_HANDLE multiTreeHandle; + + /* Codes_SRS_DATA_SERIALIZER_07_007: [NULL shall be returned when an invalid parameter is supplied.] */ + if (data == NULL || decodeFunc == NULL) + { + multiTreeHandle = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_INVALID_ARG) ); + } + else + { + /* Codes_SRS_DATA_SERIALIZER_07_012: [DataSerializer_Decode function shall call into the given DATA_SERIALIZER_DECODE_FUNC callback with a valid BUFFER object and valid MULTITREE_HANDLE object.] */ + multiTreeHandle = decodeFunc(data); + if (multiTreeHandle == NULL) + { + /* Codes_SRS_DATA_SERIALIZER_07_013: [Upon a DATA_SERIALIZER_DECODE_FUNC callback failure the DataSerializer_Encode function Shall return NULL.] */ + LogError("(Error code: %s)", ENUM_TO_STRING(DATA_SERIALIZER_RESULT, DATA_SERIALIZER_ERROR) ); + } + } + + /* Codes_SRS_DATA_SERIALIZER_07_006: [DataSerializer_Decode shall return a valid MULTITREE_HANDLE when the function executes successfully.] */ + return multiTreeHandle; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/iotdevice.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,450 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include <stdbool.h> +#include "iotdevice.h" +#include "datapublisher.h" +#include "commanddecoder.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" + +#define LOG_DEVICE_ERROR \ + LogError("(result = %s)", ENUM_TO_STRING(DEVICE_RESULT, result)) + + + +typedef struct DEVICE_HANDLE_DATA_TAG +{ + SCHEMA_MODEL_TYPE_HANDLE model; + DATA_PUBLISHER_HANDLE dataPublisherHandle; + pfDeviceActionCallback deviceActionCallback; + void* callbackUserContext; + pfDeviceMethodCallback deviceMethodCallback; + void* methodCallbackUserContext; + + COMMAND_DECODER_HANDLE commandDecoderHandle; +} DEVICE_HANDLE_DATA; + +DEFINE_ENUM_STRINGS(DEVICE_RESULT, DEVICE_RESULT_VALUES); + +static EXECUTE_COMMAND_RESULT DeviceInvokeAction(void* actionCallbackContext, const char* relativeActionPath, const char* actionName, size_t argCount, const AGENT_DATA_TYPE* args) +{ + EXECUTE_COMMAND_RESULT result; + + /*Codes_SRS_DEVICE_02_011: [If the parameter actionCallbackContent passed the callback is NULL then the callback shall return EXECUTE_COMMAND_ERROR.] */ + if (actionCallbackContext == NULL) + { + result = EXECUTE_COMMAND_ERROR; + LogError("(Error code = %s)", ENUM_TO_STRING(DEVICE_RESULT, DEVICE_INVALID_ARG)); + } + else + { + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)actionCallbackContext; + + /* Codes_SRS_DEVICE_01_052: [When the action callback passed to CommandDecoder is called, Device shall call the appropriate user callback associated with the device handle.] */ + /* Codes_SRS_DEVICE_01_055: [The value passed in callbackUserContext when creating the device shall be passed to the callback as the value for the callbackUserContext argument.] */ + result = device->deviceActionCallback((DEVICE_HANDLE)device, device->callbackUserContext, relativeActionPath, actionName, argCount, args); + } + + return result; +} + +static METHODRETURN_HANDLE DeviceInvokeMethod(void* methodCallbackContext, const char* relativeMethodPath, const char* methodName, size_t argCount, const AGENT_DATA_TYPE* args) +{ + METHODRETURN_HANDLE result; + + if (methodCallbackContext == NULL) + { + result = NULL; + LogError("(Error code = %s)", ENUM_TO_STRING(DEVICE_RESULT, DEVICE_INVALID_ARG)); + } + else + { + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)methodCallbackContext; + + result = device->deviceMethodCallback((DEVICE_HANDLE)device, device->methodCallbackUserContext, relativeMethodPath, methodName, argCount, args); + } + + return result; +} + +DEVICE_RESULT Device_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, pfDeviceActionCallback deviceActionCallback, void* callbackUserContext, pfDeviceMethodCallback methodCallback, void* methodCallbackContext, bool includePropertyPath, DEVICE_HANDLE* deviceHandle) +{ + DEVICE_RESULT result; + + /* Codes_SRS_DEVICE_05_014: [If any of the modelHandle, deviceHandle or deviceActionCallback arguments are NULL, Device_Create shall return DEVICE_INVALID_ARG.]*/ + if (modelHandle == NULL || deviceHandle == NULL || deviceActionCallback == NULL || methodCallback == NULL || methodCallbackContext == NULL) + { + result = DEVICE_INVALID_ARG; + LOG_DEVICE_ERROR; + } + else + { + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)malloc(sizeof(DEVICE_HANDLE_DATA)); + if (device == NULL) + { + /* Codes_SRS_DEVICE_05_015: [If an error occurs while trying to create the device, Device_Create shall return DEVICE_ERROR.] */ + result = DEVICE_ERROR; + LOG_DEVICE_ERROR; + } + else + { + /* Codes_SRS_DEVICE_01_018: [Device_Create shall create a DataPublisher instance by calling DataPublisher_Create.] */ + /* Codes_SRS_DEVICE_01_020: [Device_Create shall pass to DataPublisher_Create the FrontDoor instance obtained earlier.] */ + /* Codes_SRS_DEVICE_01_004: [DeviceCreate shall pass to DataPublisher_create the includePropertyPath argument.] */ + if ((device->dataPublisherHandle = DataPublisher_Create(modelHandle, includePropertyPath)) == NULL) + { + free(device); + + /* Codes_SRS_DEVICE_01_019: [If creating the DataPublisher instance fails, Device_Create shall return DEVICE_DATA_PUBLISHER_FAILED.] */ + result = DEVICE_DATA_PUBLISHER_FAILED; + LOG_DEVICE_ERROR; + } + else + { + device->model = modelHandle; + device->deviceActionCallback = deviceActionCallback; + device->callbackUserContext = callbackUserContext; + device->deviceMethodCallback = methodCallback; + device->methodCallbackUserContext = methodCallbackContext; + + /* Codes_SRS_DEVICE_01_001: [Device_Create shall create a CommandDecoder instance by calling CommandDecoder_Create and passing to it the model handle.] */ + /* Codes_SRS_DEVICE_01_002: [Device_Create shall also pass to CommandDecoder_Create a callback to be invoked when a command is received and a context that shall be the device handle.] */ + if ((device->commandDecoderHandle = CommandDecoder_Create(modelHandle, DeviceInvokeAction, device, DeviceInvokeMethod, device)) == NULL) + { + DataPublisher_Destroy(device->dataPublisherHandle); + free(device); + + /* Codes_SRS_DEVICE_01_003: [If CommandDecoder_Create fails, Device_Create shall return DEVICE_COMMAND_DECODER_FAILED.] */ + result = DEVICE_COMMAND_DECODER_FAILED; + LOG_DEVICE_ERROR; + } + else + { + /* Codes_SRS_DEVICE_03_003: [The DEVICE_HANDLE shall be provided via the deviceHandle out argument.] */ + *deviceHandle = (DEVICE_HANDLE)device; + /* Codes_SRS_DEVICE_03_004: [Device_Create shall return DEVICE_OK upon success.] */ + result = DEVICE_OK; + } + } + } + } + + return result; +} + +/* Codes_SRS_DEVICE_03_006: [Device_Destroy shall free all resources associated with a device.] */ +void Device_Destroy(DEVICE_HANDLE deviceHandle) +{ + /* Codes_SRS_DEVICE_03_007: [Device_Destroy will not do anything if deviceHandle is NULL.] */ + if (deviceHandle != NULL) + { + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)deviceHandle; + + DataPublisher_Destroy(device->dataPublisherHandle); + CommandDecoder_Destroy(device->commandDecoderHandle); + + free(device); + } +} + +TRANSACTION_HANDLE Device_StartTransaction(DEVICE_HANDLE deviceHandle) +{ + TRANSACTION_HANDLE result; + + /* Codes_SRS_DEVICE_01_035: [If any argument is NULL, Device_StartTransaction shall return NULL.] */ + if (deviceHandle == NULL) + { + result = NULL; + LogError("(Error code = %s)", ENUM_TO_STRING(DEVICE_RESULT, DEVICE_INVALID_ARG)); + } + else + { + DEVICE_HANDLE_DATA* deviceInstance = (DEVICE_HANDLE_DATA*)deviceHandle; + + /* Codes_SRS_DEVICE_01_034: [Device_StartTransaction shall invoke DataPublisher_StartTransaction for the DataPublisher handle associated with the deviceHandle argument.] */ + /* Codes_SRS_DEVICE_01_043: [On success, Device_StartTransaction shall return a non NULL handle.] */ + /* Codes_SRS_DEVICE_01_048: [When DataPublisher_StartTransaction fails, Device_StartTransaction shall return NULL.] */ + result = DataPublisher_StartTransaction(deviceInstance->dataPublisherHandle); + } + + return result; +} + +DEVICE_RESULT Device_PublishTransacted(TRANSACTION_HANDLE transactionHandle, const char* propertyPath, const AGENT_DATA_TYPE* data) +{ + DEVICE_RESULT result; + + /* Codes_SRS_DEVICE_01_037: [If any argument is NULL, Device_PublishTransacted shall return DEVICE_INVALID_ARG.] */ + if ( + (transactionHandle == NULL) || + (propertyPath == NULL) || + (data == NULL) + ) + { + result = DEVICE_INVALID_ARG; + LOG_DEVICE_ERROR; + } + /* Codes_SRS_DEVICE_01_036: [Device_PublishTransacted shall invoke DataPublisher_PublishTransacted.] */ + else if (DataPublisher_PublishTransacted(transactionHandle, propertyPath, data) != DATA_PUBLISHER_OK) + { + /* Codes_SRS_DEVICE_01_049: [When DataPublisher_PublishTransacted fails, Device_PublishTransacted shall return DEVICE_DATA_PUBLISHER_FAILED.] */ + result = DEVICE_DATA_PUBLISHER_FAILED; + LOG_DEVICE_ERROR; + } + else + { + /* Codes_SRS_DEVICE_01_044: [On success, Device_PublishTransacted shall return DEVICE_OK.] */ + result = DEVICE_OK; + } + + return result; +} + +DEVICE_RESULT Device_EndTransaction(TRANSACTION_HANDLE transactionHandle, unsigned char** destination, size_t* destinationSize) +{ + DEVICE_RESULT result; + + /* Codes_SRS_DEVICE_01_039: [If any parameter is NULL, Device_EndTransaction shall return DEVICE_INVALID_ARG.]*/ + if ( + (transactionHandle == NULL) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + result = DEVICE_INVALID_ARG; + LOG_DEVICE_ERROR; + } + /* Codes_SRS_DEVICE_01_038: [Device_EndTransaction shall invoke DataPublisher_EndTransaction.] */ + else if (DataPublisher_EndTransaction(transactionHandle, destination, destinationSize) != DATA_PUBLISHER_OK) + { + /* Codes_SRS_DEVICE_01_050: [When DataPublisher_EndTransaction fails, Device_EndTransaction shall return DEVICE_DATA_PUBLISHER_FAILED.] */ + result = DEVICE_DATA_PUBLISHER_FAILED; + LOG_DEVICE_ERROR; + } + else + { + /* Codes_SRS_DEVICE_01_045: [On success, Device_EndTransaction shall return DEVICE_OK.] */ + result = DEVICE_OK; + } + + return result; +} + +DEVICE_RESULT Device_CancelTransaction(TRANSACTION_HANDLE transactionHandle) +{ + DEVICE_RESULT result; + + /* Codes_SRS_DEVICE_01_041: [If any argument is NULL, Device_CancelTransaction shall return DEVICE_INVALID_ARG.] */ + if (transactionHandle == NULL) + { + result = DEVICE_INVALID_ARG; + LOG_DEVICE_ERROR; + } + /* Codes_SRS_DEVICE_01_040: [Device_CancelTransaction shall invoke DataPublisher_CancelTransaction.] */ + else if (DataPublisher_CancelTransaction(transactionHandle) != DATA_PUBLISHER_OK) + { + /* Codes_SRS_DEVICE_01_051: [When DataPublisher_CancelTransaction fails, Device_CancelTransaction shall return DEVICE_DATA_PUBLISHER_FAILED.] */ + result = DEVICE_DATA_PUBLISHER_FAILED; + LOG_DEVICE_ERROR; + } + else + { + /* Codes_SRS_DEVICE_01_046: [On success, Device_PublishTransacted shall return DEVICE_OK.] */ + result = DEVICE_OK; + } + + return result; +} + +EXECUTE_COMMAND_RESULT Device_ExecuteCommand(DEVICE_HANDLE deviceHandle, const char* command) +{ + EXECUTE_COMMAND_RESULT result; + /*Codes_SRS_DEVICE_02_012: [If any parameters are NULL, then Device_ExecuteCommand shall return EXECUTE_COMMAND_ERROR.] */ + if ( + (deviceHandle == NULL) || + (command == NULL) + ) + { + result = EXECUTE_COMMAND_ERROR; + LogError("invalid parameter (NULL passed to Device_ExecuteCommand DEVICE_HANDLE deviceHandle=%p, const char* command=%p", deviceHandle, command); + } + else + { + /*Codes_SRS_DEVICE_02_013: [Otherwise, Device_ExecuteCommand shall call CommandDecoder_ExecuteCommand and return what CommandDecoder_ExecuteCommand is returning.] */ + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)deviceHandle; + result = CommandDecoder_ExecuteCommand(device->commandDecoderHandle, command); + } + return result; +} + +METHODRETURN_HANDLE Device_ExecuteMethod(DEVICE_HANDLE deviceHandle, const char* methodName, const char* methodPayload) +{ + METHODRETURN_HANDLE result; + if ( + (deviceHandle == NULL) || + (methodName == NULL) /*methodPayload can be NULL*/ + ) + { + result = NULL; + LogError("invalid parameter (NULL passed to Device_ExecuteMethod DEVICE_HANDLE deviceHandle=%p, const char* methodPayload=%p", deviceHandle, methodPayload); + } + else + { + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)deviceHandle; + result = CommandDecoder_ExecuteMethod(device->commandDecoderHandle, methodName, methodPayload); + } + return result; +} + +REPORTED_PROPERTIES_TRANSACTION_HANDLE Device_CreateTransaction_ReportedProperties(DEVICE_HANDLE deviceHandle) +{ + REPORTED_PROPERTIES_TRANSACTION_HANDLE result; + + /*Codes_SRS_DEVICE_02_014: [ If argument deviceHandle is NULL then Device_CreateTransaction_ReportedProperties shall fail and return NULL. ]*/ + if (deviceHandle == NULL) + { + LogError("invalid argument DEVICE_HANDLE deviceHandle=%p", deviceHandle); + result = NULL; + } + else + { + DEVICE_HANDLE_DATA* deviceInstance = (DEVICE_HANDLE_DATA*)deviceHandle; + /*Codes_SRS_DEVICE_02_015: [ Otherwise, Device_CreateTransaction_ReportedProperties shall call DataPublisher_CreateTransaction_ReportedProperties. ]*/ + /*Codes_SRS_DEVICE_02_016: [ If DataPublisher_CreateTransaction_ReportedProperties fails then Device_CreateTransaction_ReportedProperties shall fail and return NULL. ]*/ + /*Codes_SRS_DEVICE_02_017: [ Otherwise Device_CreateTransaction_ReportedProperties shall succeed and return a non-NULL value. ]*/ + result = DataPublisher_CreateTransaction_ReportedProperties(deviceInstance->dataPublisherHandle); + if (result == NULL) + { + LogError("unable to DataPublisher_CreateTransaction_ReportedProperties"); + /*return as is*/ + } + else + { + /*return as is*/ + } + } + + return result; +} + +DEVICE_RESULT Device_PublishTransacted_ReportedProperty(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle, const char* reportedPropertyPath, const AGENT_DATA_TYPE* data) +{ + DEVICE_RESULT result; + /*Codes_SRS_DEVICE_02_018: [ If argument transactionHandle is NULL then Device_PublishTransacted_ReportedProperty shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_019: [ If argument reportedPropertyPath is NULL then Device_PublishTransacted_ReportedProperty shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_020: [ If argument data is NULL then Device_PublishTransacted_ReportedProperty shall fail and return DEVICE_INVALID_ARG. ]*/ + if ( + (transactionHandle == NULL) || + (reportedPropertyPath == NULL) || + (data == NULL) + ) + { + LogError("invalid argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p, const char* reportedPropertyPath=%s, const AGENT_DATA_TYPE* data=%p", transactionHandle, reportedPropertyPath, data); + result = DEVICE_INVALID_ARG; + } + else + { + /*Codes_SRS_DEVICE_02_021: [ Device_PublishTransacted_ReportedProperty shall call DataPublisher_PublishTransacted_ReportedProperty. ]*/ + DATA_PUBLISHER_RESULT r = DataPublisher_PublishTransacted_ReportedProperty(transactionHandle, reportedPropertyPath, data); + if (r != DATA_PUBLISHER_OK) + { + LogError("unable to DataPublisher_PublishTransacted_ReportedProperty"); + /*Codes_SRS_DEVICE_02_022: [ If DataPublisher_PublishTransacted_ReportedProperty fails then Device_PublishTransacted_ReportedProperty shall fail and return DEVICE_DATA_PUBLISHER_FAILED. ]*/ + result = DEVICE_DATA_PUBLISHER_FAILED; + } + else + { + /*Codes_SRS_DEVICE_02_023: [ Otherwise, Device_PublishTransacted_ReportedProperty shall succeed and return DEVICE_OK. ]*/ + result = DEVICE_OK; + } + } + return result; +} + +DEVICE_RESULT Device_CommitTransaction_ReportedProperties(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle, unsigned char** destination, size_t* destinationSize) +{ + DEVICE_RESULT result; + + /*Codes_SRS_DEVICE_02_024: [ If argument transactionHandle is NULL then Device_CommitTransaction_ReportedProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_025: [ If argument destination is NULL then Device_CommitTransaction_ReportedProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_026: [ If argument destinationSize is NULL then Device_CommitTransaction_ReportedProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + if ( + (transactionHandle == NULL) || + (destination == NULL) || + (destinationSize == NULL) + ) + { + LogError("invalid argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p, unsigned char** destination=%p, size_t* destinationSize=%p", transactionHandle, destination, destinationSize); + result = DEVICE_INVALID_ARG; + } + else + { + /*Codes_SRS_DEVICE_02_027: [ Device_CommitTransaction_ReportedProperties shall call DataPublisher_CommitTransaction_ReportedProperties. ]*/ + DATA_PUBLISHER_RESULT r = DataPublisher_CommitTransaction_ReportedProperties(transactionHandle, destination, destinationSize); + + /*Codes_SRS_DEVICE_02_028: [ If DataPublisher_CommitTransaction_ReportedProperties fails then Device_CommitTransaction_ReportedProperties shall fail and return DEVICE_DATA_PUBLISHER_FAILED. ]*/ + if (r != DATA_PUBLISHER_OK) + { + LogError("unable to DataPublisher_CommitTransaction_ReportedProperties"); + result = DEVICE_DATA_PUBLISHER_FAILED; + } + else + { + /*Codes_SRS_DEVICE_02_029: [ Otherwise Device_CommitTransaction_ReportedProperties shall succeed and return DEVICE_OK. ]*/ + result = DEVICE_OK; + } + } + return result; +} + +void Device_DestroyTransaction_ReportedProperties(REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle) +{ + /*Codes_SRS_DEVICE_02_030: [ If argument transactionHandle is NULL then Device_DestroyTransaction_ReportedProperties shall return. ]*/ + if (transactionHandle == NULL) + { + LogError("invalid argument REPORTED_PROPERTIES_TRANSACTION_HANDLE transactionHandle=%p", transactionHandle); + } + else + { + /*Codes_SRS_DEVICE_02_031: [ Otherwise Device_DestroyTransaction_ReportedProperties shall free all used resources. ]*/ + DataPublisher_DestroyTransaction_ReportedProperties(transactionHandle); + } +} + +DEVICE_RESULT Device_IngestDesiredProperties(void* startAddress, DEVICE_HANDLE deviceHandle, const char* jsonPayload, bool parseDesiredNode) +{ + DEVICE_RESULT result; + /*Codes_SRS_DEVICE_02_032: [ If deviceHandle is NULL then Device_IngestDesiredProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_033: [ If jsonPayload is NULL then Device_IngestDesiredProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + /*Codes_SRS_DEVICE_02_037: [ If startAddress is NULL then Device_IngestDesiredProperties shall fail and return DEVICE_INVALID_ARG. ]*/ + if ( + (deviceHandle == NULL) || + (jsonPayload == NULL) || + (startAddress == NULL) + ) + { + LogError("invalid argument void* startAddress=%p, DEVICE_HANDLE deviceHandle=%p, const char* jsonPayload=%p\n", startAddress, deviceHandle, jsonPayload); + result = DEVICE_INVALID_ARG; + } + else + { + /*Codes_SRS_DEVICE_02_034: [ Device_IngestDesiredProperties shall call CommandDecoder_IngestDesiredProperties. ]*/ + DEVICE_HANDLE_DATA* device = (DEVICE_HANDLE_DATA*)deviceHandle; + if (CommandDecoder_IngestDesiredProperties(startAddress, device->commandDecoderHandle, jsonPayload, parseDesiredNode) != EXECUTE_COMMAND_SUCCESS) + { + /*Codes_SRS_DEVICE_02_035: [ If any failure happens then Device_IngestDesiredProperties shall fail and return DEVICE_ERROR. ]*/ + LogError("failure in CommandDecoder_IngestDesiredProperties"); + result = DEVICE_ERROR; + } + else + { + /*Codes_SRS_DEVICE_02_036: [ Otherwise, Device_IngestDesiredProperties shall succeed and return DEVICE_OK. ]*/ + result = DEVICE_OK; + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/jsondecoder.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,611 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +#include "jsondecoder.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stddef.h> + +#define IsWhiteSpace(A) (((A) == 0x20) || ((A) == 0x09) || ((A) == 0x0A) || ((A) == 0x0D)) + +typedef struct PARSER_STATE_TAG +{ + char* json; +} PARSER_STATE; + +static JSON_DECODER_RESULT ParseArray(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode); +static JSON_DECODER_RESULT ParseObject(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode); + +/* Codes_SRS_JSON_DECODER_99_049:[ JSONDecoder shall not allocate new string values for the leafs, but rather point to strings in the original JSON.] */ +static void NoFreeFunction(void* value) +{ + (void)value; +} + +static int NOPCloneFunction(void** destination, const void* source) +{ + *destination = (void**)source; + return 0; +} + +void SkipWhiteSpaces(PARSER_STATE* parserState) +{ + while ((*(parserState->json) != '\0') && IsWhiteSpace(*(parserState->json))) + { + parserState->json++; + } +} + +static JSON_DECODER_RESULT ParseString(PARSER_STATE* parserState, char** stringBegin) +{ + JSON_DECODER_RESULT result = JSON_DECODER_OK; + *stringBegin = parserState->json; + + /* Codes_SRS_JSON_DECODER_99_028:[ A string begins and ends with quotation marks.] */ + if (*(parserState->json) != '"') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + while ((*(parserState->json) != '"') && (*(parserState->json) != '\0')) + { + /* Codes_SRS_JSON_DECODER_99_030:[ Any character may be escaped.] */ + /* Codes_SRS_JSON_DECODER_99_033:[ Alternatively, there are two-character sequence escape representations of some popular characters. So, for example, a string containing only a single reverse solidus character may be represented more compactly as "\\".] */ + if (*(parserState->json) == '\\') + { + parserState->json++; + if ( + /* Codes_SRS_JSON_DECODER_99_051:[ %x5C / ; \ reverse solidus U+005C] */ + (*parserState->json == '\\') || + /* Codes_SRS_JSON_DECODER_99_050:[ %x22 / ; " quotation mark U+0022] */ + (*parserState->json == '"') || + /* Codes_SRS_JSON_DECODER_99_052:[ %x2F / ; / solidus U+002F] */ + (*parserState->json == '/') || + /* Codes_SRS_JSON_DECODER_99_053:[ %x62 / ; b backspace U+0008] */ + (*parserState->json == 'b') || + /* Codes_SRS_JSON_DECODER_99_054:[ %x66 / ; f form feed U+000C] */ + (*parserState->json == 'f') || + /* Codes_SRS_JSON_DECODER_99_055:[ %x6E / ; n line feed U+000A] */ + (*parserState->json == 'n') || + /* Codes_SRS_JSON_DECODER_99_056:[ %x72 / ; r carriage return U+000D] */ + (*parserState->json == 'r') || + /* Codes_SRS_JSON_DECODER_99_057:[ %x74 / ; t tab U+0009] */ + (*parserState->json == 't')) + { + parserState->json++; + } + else + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + break; + } + } + else + { + parserState->json++; + } + } + + if (*(parserState->json) != '"') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + result = JSON_DECODER_OK; + } + } + + return result; +} + +static JSON_DECODER_RESULT ParseNumber(PARSER_STATE* parserState) +{ + JSON_DECODER_RESULT result = JSON_DECODER_OK; + size_t digitCount = 0; + + if (*(parserState->json) == '-') + { + parserState->json++; + } + + /* Codes_SRS_JSON_DECODER_99_043:[ A number contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part.] */ + while (*(parserState->json) != '\0') + { + /* Codes_SRS_JSON_DECODER_99_044:[ Octal and hex forms are not allowed.] */ + if (ISDIGIT(*(parserState->json))) + { + digitCount++; + /* simply continue */ + } + else + { + break; + } + + parserState->json++; + } + + if ((digitCount == 0) || + /* Codes_SRS_JSON_DECODER_99_045:[ Leading zeros are not allowed.] */ + ((digitCount > 1) && *(parserState->json - digitCount) == '0')) + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + /* Codes_SRS_JSON_DECODER_99_046:[ A fraction part is a decimal point followed by one or more digits.] */ + if (*(parserState->json) == '.') + { + /* optional fractional part */ + parserState->json++; + digitCount = 0; + + while (*(parserState->json) != '\0') + { + /* Codes_SRS_JSON_DECODER_99_044:[ Octal and hex forms are not allowed.] */ + if (ISDIGIT(*(parserState->json))) + { + digitCount++; + /* simply continue */ + } + else + { + break; + } + + parserState->json++; + } + + if (digitCount == 0) + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + } + + /* Codes_SRS_JSON_DECODER_99_047:[ An exponent part begins with the letter E in upper or lowercase, which may be followed by a plus or minus sign.] */ + if ((*(parserState->json) == 'e') || (*(parserState->json) == 'E')) + { + parserState->json++; + + /* optional sign */ + if ((*(parserState->json) == '-') || (*(parserState->json) == '+')) + { + parserState->json++; + } + + digitCount = 0; + + /* Codes_SRS_JSON_DECODER_99_048:[ The E and optional sign are followed by one or more digits.] */ + while (*(parserState->json) != '\0') + { + /* Codes_SRS_JSON_DECODER_99_044:[ Octal and hex forms are not allowed.] */ + if (ISDIGIT(*(parserState->json))) + { + digitCount++; + /* simply continue */ + } + else + { + break; + } + + parserState->json++; + } + + if (digitCount == 0) + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + } + } + + return result; +} + +static JSON_DECODER_RESULT ParseValue(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode, char** stringBegin) +{ + JSON_DECODER_RESULT result; + + SkipWhiteSpaces(parserState); + + if (*(parserState->json) == '"') + { + result = ParseString(parserState, stringBegin); + } + /* Codes_SRS_JSON_DECODER_99_018:[ A JSON value MUST be an object, array, number, or string, or one of the following three literal names: false null true] */ + /* Codes_SRS_JSON_DECODER_99_019:[ The literal names MUST be lowercase.] */ + /* Codes_SRS_JSON_DECODER_99_020:[ No other literal names are allowed.] */ + else if (strncmp(parserState->json, "false", 5) == 0) + { + *stringBegin = parserState->json; + parserState->json += 5; + result = JSON_DECODER_OK; + } + else if (strncmp(parserState->json, "true", 4) == 0) + { + *stringBegin = parserState->json; + parserState->json += 4; + result = JSON_DECODER_OK; + } + else if (strncmp(parserState->json, "null", 4) == 0) + { + *stringBegin = parserState->json; + parserState->json += 4; + result = JSON_DECODER_OK; + } + /* Tests_SRS_JSON_DECODER_99_018:[ A JSON value MUST be an object, array, number, or string, or one of the following three literal names: false null true] */ + else if (*(parserState->json) == '[') + { + result = ParseArray(parserState, currentNode); + *stringBegin = NULL; + } + else if (*(parserState->json) == '{') + { + result = ParseObject(parserState, currentNode); + *stringBegin = NULL; + } + else if ( + ( + ISDIGIT(*(parserState->json)) + ) + || (*(parserState->json) == '-')) + { + *stringBegin = parserState->json; + result = ParseNumber(parserState); + } + else + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + + return result; +} + +static JSON_DECODER_RESULT ParseColon(PARSER_STATE* parserState) +{ + JSON_DECODER_RESULT result; + + SkipWhiteSpaces(parserState); + /* Codes_SRS_JSON_DECODER_99_023:[ A single colon comes after each name, separating the name from the value.] */ + if (*(parserState->json) != ':') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + result = JSON_DECODER_OK; + } + + return result; +} + +static JSON_DECODER_RESULT ParseOpenCurly(PARSER_STATE* parserState) +{ + JSON_DECODER_RESULT result = JSON_DECODER_OK; + + SkipWhiteSpaces(parserState); + + /* Codes_SRS_JSON_DECODER_99_021:[ An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members).] */ + if (*(parserState->json) != '{') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + result = JSON_DECODER_OK; + } + + return result; +} + +static JSON_DECODER_RESULT ParseNameValuePair(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode) +{ + JSON_DECODER_RESULT result; + char* memberNameBegin; + + SkipWhiteSpaces(parserState); + + /* Codes_SRS_JSON_DECODER_99_022:[ A name is a string.] */ + result = ParseString(parserState, &memberNameBegin); + if (result == JSON_DECODER_OK) + { + char* valueBegin; + MULTITREE_HANDLE childNode; + *(parserState->json - 1) = 0; + + result = ParseColon(parserState); + if (result == JSON_DECODER_OK) + { + /* Codes_SRS_JSON_DECODER_99_025:[ The names within an object SHOULD be unique.] */ + /* Multi Tree takes care of not having 2 children with the same name */ + /* Codes_SRS_JSON_DECODER_99_002:[ JSONDecoder_JSON_To_MultiTree shall use the MultiTree APIs to create the multi tree and add leafs to the multi tree.] */ + /* Codes_SRS_JSON_DECODER_99_003:[ When a JSON element is decoded from the JSON object then a leaf shall be added to the MultiTree.] */ + /* Codes_SRS_JSON_DECODER_99_004:[ The leaf node name in the multi tree shall be the JSON element name.] */ + if (MultiTree_AddChild(currentNode, memberNameBegin + 1, &childNode) != MULTITREE_OK) + { + /* Codes_SRS_JSON_DECODER_99_038:[ If any MultiTree API fails, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_MULTITREE_FAILED.] */ + result = JSON_DECODER_MULTITREE_FAILED; + } + else + { + result = ParseValue(parserState, childNode, &valueBegin); + if ((result == JSON_DECODER_OK) && (valueBegin != NULL)) + { + /* Codes_SRS_JSON_DECODER_99_005:[ The leaf node added in the multi tree shall have the value the string value of the JSON element as parsed from the JSON object.] */ + if (MultiTree_SetValue(childNode, valueBegin) != MULTITREE_OK) + { + /* Codes_SRS_JSON_DECODER_99_038:[ If any MultiTree API fails, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_MULTITREE_FAILED.] */ + result = JSON_DECODER_MULTITREE_FAILED; + } + } + } + } + } + + return result; +} + +static JSON_DECODER_RESULT ParseObject(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode) +{ + JSON_DECODER_RESULT result = ParseOpenCurly(parserState); + if (result == JSON_DECODER_OK) + { + char jsonChar; + + SkipWhiteSpaces(parserState); + + jsonChar = *(parserState->json); + while ((jsonChar != '}') && (jsonChar != '\0')) + { + char* valueEnd; + + /* decode each value */ + result = ParseNameValuePair(parserState, currentNode); + if (result != JSON_DECODER_OK) + { + break; + } + + valueEnd = parserState->json; + + SkipWhiteSpaces(parserState); + jsonChar = *(parserState->json); + *valueEnd = 0; + + /* Codes_SRS_JSON_DECODER_99_024:[ A single comma separates a value from a following name.] */ + if (jsonChar == ',') + { + parserState->json++; + /* get the next name/value pair */ + } + } + + if (result != JSON_DECODER_OK) + { + /* already have error */ + } + else + { + if (jsonChar != '}') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + } + } + } + + return result; +} + +static JSON_DECODER_RESULT ParseArray(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode) +{ + JSON_DECODER_RESULT result = JSON_DECODER_OK; + + SkipWhiteSpaces(parserState); + + /* Codes_SRS_JSON_DECODER_99_026:[ An array structure is represented as square brackets surrounding zero or more values (or elements).] */ + if (*(parserState->json) != '[') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + char* stringBegin; + char jsonChar; + int arrayIndex = 0; + result = JSON_DECODER_OK; + + parserState->json++; + + SkipWhiteSpaces(parserState); + + jsonChar = *parserState->json; + while ((jsonChar != ']') && (jsonChar != '\0')) + { + char arrayIndexStr[22]; + MULTITREE_HANDLE childNode; + + /* Codes_SRS_JSON_DECODER_99_039:[ For array elements the multi tree node name shall be the string representation of the array index.] */ + if (sprintf(arrayIndexStr, "%d", arrayIndex++) < 0) + { + result = JSON_DECODER_ERROR; + break; + } + /* Codes_SRS_JSON_DECODER_99_003:[ When a JSON element is decoded from the JSON object then a leaf shall be added to the MultiTree.] */ + else if (MultiTree_AddChild(currentNode, arrayIndexStr, &childNode) != MULTITREE_OK) + { + /* Codes_SRS_JSON_DECODER_99_038:[ If any MultiTree API fails, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_MULTITREE_FAILED.] */ + result = JSON_DECODER_MULTITREE_FAILED; + } + else + { + char* valueEnd; + + /* decode each value */ + result = ParseValue(parserState, childNode, &stringBegin); + if (result != JSON_DECODER_OK) + { + break; + } + + if (stringBegin != NULL) + { + /* Codes_SRS_JSON_DECODER_99_005:[ The leaf node added in the multi tree shall have the value the string value of the JSON element as parsed from the JSON object.] */ + if (MultiTree_SetValue(childNode, stringBegin) != MULTITREE_OK) + { + /* Codes_SRS_JSON_DECODER_99_038:[ If any MultiTree API fails, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_MULTITREE_FAILED.] */ + result = JSON_DECODER_MULTITREE_FAILED; + break; + } + } + + valueEnd = parserState->json; + + SkipWhiteSpaces(parserState); + jsonChar = *(parserState->json); + *valueEnd = 0; + + /* Codes_SRS_JSON_DECODER_99_027:[ Elements are separated by commas.] */ + if (jsonChar == ',') + { + parserState->json++; + /* get the next value pair */ + } + else if (jsonChar == ']') + { + break; + } + else + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + break; + } + } + } + + if (result != JSON_DECODER_OK) + { + /* already have error */ + } + else + { + if (jsonChar != ']') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + parserState->json++; + SkipWhiteSpaces(parserState); + } + } + } + + return result; +} + +/* Codes_SRS_JSON_DECODER_99_012:[ A JSON text is a serialized object or array.] */ +static JSON_DECODER_RESULT ParseObjectOrArray(PARSER_STATE* parserState, MULTITREE_HANDLE currentNode) +{ + JSON_DECODER_RESULT result = JSON_DECODER_PARSE_ERROR; + + SkipWhiteSpaces(parserState); + + if (*(parserState->json) == '{') + { + result = ParseObject(parserState, currentNode); + SkipWhiteSpaces(parserState); + } + else if (*(parserState->json) == '[') + { + result = ParseArray(parserState, currentNode); + SkipWhiteSpaces(parserState); + } + else + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + + if ((result == JSON_DECODER_OK) && + (*(parserState->json) != '\0')) + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + + return result; +} + +static JSON_DECODER_RESULT ParseJSON(char* json, MULTITREE_HANDLE currentNode) +{ + /* Codes_SRS_JSON_DECODER_99_009:[ On success, JSONDecoder_JSON_To_MultiTree shall return a handle to the multi tree it created in the multiTreeHandle argument and it shall return JSON_DECODER_OK.] */ + PARSER_STATE parseState; + parseState.json = json; + return ParseObjectOrArray(&parseState, currentNode); +} + +JSON_DECODER_RESULT JSONDecoder_JSON_To_MultiTree(char* json, MULTITREE_HANDLE* multiTreeHandle) +{ + JSON_DECODER_RESULT result; + + if ((json == NULL) || + (multiTreeHandle == NULL)) + { + /* Codes_SRS_JSON_DECODER_99_001:[ If any of the parameters passed to the JSONDecoder_JSON_To_MultiTree function is NULL then the function call shall return JSON_DECODER_INVALID_ARG.] */ + result = JSON_DECODER_INVALID_ARG; + } + else if (*json == '\0') + { + /* Codes_SRS_JSON_DECODER_99_007:[ If parsing the JSON fails due to the JSON string being malformed, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_PARSE_ERROR.] */ + result = JSON_DECODER_PARSE_ERROR; + } + else + { + /* Codes_SRS_JSON_DECODER_99_008:[ JSONDecoder_JSON_To_MultiTree shall create a multi tree based on the json string argument.] */ + /* Codes_SRS_JSON_DECODER_99_002:[ JSONDecoder_JSON_To_MultiTree shall use the MultiTree APIs to create the multi tree and add leafs to the multi tree.] */ + /* Codes_SRS_JSON_DECODER_99_009:[ On success, JSONDecoder_JSON_To_MultiTree shall return a handle to the multi tree it created in the multiTreeHandle argument and it shall return JSON_DECODER_OK.] */ + *multiTreeHandle = MultiTree_Create(NOPCloneFunction, NoFreeFunction); + if (*multiTreeHandle == NULL) + { + /* Codes_SRS_JSON_DECODER_99_038:[ If any MultiTree API fails, JSONDecoder_JSON_To_MultiTree shall return JSON_DECODER_MULTITREE_FAILED.] */ + result = JSON_DECODER_MULTITREE_FAILED; + } + else + { + result = ParseJSON(json, *multiTreeHandle); + if (result != JSON_DECODER_OK) + { + MultiTree_Destroy(*multiTreeHandle); + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/jsonencoder.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/gballoc.h" + +#include "jsonencoder.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track variable "i" and link it to childCount*/ +#endif + +DEFINE_ENUM_STRINGS(JSON_ENCODER_TOSTRING_RESULT, JSON_ENCODER_TOSTRING_RESULT_VALUES); +DEFINE_ENUM_STRINGS(JSON_ENCODER_RESULT, JSON_ENCODER_RESULT_VALUES); + +JSON_ENCODER_RESULT JSONEncoder_EncodeTree(MULTITREE_HANDLE treeHandle, STRING_HANDLE destination, JSON_ENCODER_TOSTRING_FUNC toStringFunc) +{ + JSON_ENCODER_RESULT result; + + size_t childCount; + + /* Codes_SRS_JSON_ENCODER_99_032:[If any of the arguments passed to JSONEncoder_EncodeTree is NULL then JSON_ENCODER_INVALID_ARG shall be returned.] */ + if ((treeHandle == NULL) || + (destination == NULL) || + (toStringFunc == NULL)) + { + result = JSON_ENCODER_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + /*Codes_SRS_JSON_ENCODER_99_035:[ JSON encoder shall inquire the number of child nodes (further called childCount) of the current node (given by parameter treeHandle.]*/ + else if (MultiTree_GetChildCount(treeHandle, &childCount) != MULTITREE_OK) + { + result = JSON_ENCODER_MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + size_t i; + /*Codes_SRS_JSON_ENCODER_99_036:[ The string "{" shall be added to the output;]*/ + if (STRING_concat(destination, "{") != 0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + result = JSON_ENCODER_OK; + for (i = 0; (i < childCount) && (result == JSON_ENCODER_OK); i++) + { + MULTITREE_HANDLE childTreeHandle; + + if ((i > 0) && + (STRING_concat(destination, ", ") != 0)) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (STRING_concat(destination, "\"") != 0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + STRING_HANDLE name = STRING_new(); + if (name == NULL) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + if (MultiTree_GetChild(treeHandle, i, &childTreeHandle) != MULTITREE_OK) + { + result = JSON_ENCODER_MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (MultiTree_GetName(childTreeHandle, name) != MULTITREE_OK) + { + result = JSON_ENCODER_MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (STRING_concat_with_STRING(destination, name) != 0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (STRING_concat(destination, "\":") != 0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + size_t innerChildCount; + if (MultiTree_GetChildCount(childTreeHandle, &innerChildCount) != MULTITREE_OK) + { + result = JSON_ENCODER_MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + if (innerChildCount > 0) + { + STRING_HANDLE child = STRING_new(); + if (child == NULL) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + if ((result = JSONEncoder_EncodeTree(childTreeHandle, child, toStringFunc)) != JSON_ENCODER_OK) + { + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (STRING_concat_with_STRING(destination, child)!=0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + STRING_delete(child); + } + } + else + { + const void* value; + if (MultiTree_GetValue(childTreeHandle, &value) != MULTITREE_OK) + { + result = JSON_ENCODER_MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + + } + else + { + STRING_HANDLE childValue = STRING_new(); + if (childValue == NULL) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + if (toStringFunc(childValue, value) != JSON_ENCODER_TOSTRING_OK) + { + result = JSON_ENCODER_TOSTRING_FUNCTION_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else if (STRING_concat_with_STRING(destination, childValue)!=0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + /*do nothing, result = JSON_ENCODER_OK is set above at the beginning of the FOR loop*/ + } + STRING_delete(childValue); + } + } + } + } + } + STRING_delete(name); + } + } + } + + if ((i == childCount) && (result == JSON_ENCODER_OK)) + { + if (STRING_concat(destination, "}") != 0) + { + result = JSON_ENCODER_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_RESULT, result)); + } + else + { + /* Codes_SRS_JSON_ENCODER_99_031:[On success, JSONEncoder_EncodeTree shall return JSON_ENCODER_OK.] */ + result = JSON_ENCODER_OK; + } + } + } + } + + return result; +#ifdef _MSC_VER +#pragma warning(disable: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track variable "i" and link it to childCount*/ +#endif +} + +JSON_ENCODER_TOSTRING_RESULT JSONEncoder_CharPtr_ToString(STRING_HANDLE destination, const void* value) +{ + JSON_ENCODER_TOSTRING_RESULT result; + + /*Coes_SRS_JSON_ENCODER_99_047:[ JSONEncoder_CharPtr_ToString shall return JSON_ENCODER_TOSTRING_INVALID_ARG if destination or value parameters passed to it are NULL.]*/ + if ((destination == NULL) || + (value == NULL)) + { + result = JSON_ENCODER_TOSTRING_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_TOSTRING_RESULT, result)); + } + /*Codes_SRS_JSON_ENCODER_99_048:[ JSONEncoder_CharPtr_ToString shall use strcpy_s to copy from value to destination.]*/ + else if (STRING_concat(destination, (const char*)value) != 0) + { + /*Codes_SRS_JSON_ENCODER_99_049:[ If strcpy_s fails then JSONEncoder_CharPtr_ToString shall return JSON_ENCODER_TOSTRING_ERROR.]*/ + result = JSON_ENCODER_TOSTRING_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(JSON_ENCODER_TOSTRING_RESULT, result)); + } + else + { + /*Codes_SRS_JSON_ENCODER_99_050:[ If strcpy_s doesn't fail, then JSONEncoder_CharPtr_ToString shall return JSON_ENCODER_TOSTRING_OK]*/ + result = JSON_ENCODER_TOSTRING_OK; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/makefile Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,6 @@ +!if 0 +Copyright (c) Microsoft. All rights reserved. +Licensed under the MIT license. See LICENSE file in the project root for full license information. +!endif + +!INCLUDE $(_MAKEENVROOT)\makefile.def
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/methodreturn.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <limits.h> + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/strings.h" +#include "parson.h" + +#define METHODRETURN_C +#include "methodreturn.h" +#undef METHODRETURN_C + +typedef struct METHODRETURN_HANDLE_DATA_TAG +{ + METHODRETURN_DATA data; +}METHODRETURN_HANDLE_DATA; + +bool is_json_present_and_unparsable(const char* jsonValue) +{ + bool is_present_and_unparsable; + if (jsonValue == NULL) + { + // Null json is not considered invalid here + is_present_and_unparsable = false; + } + else + { + JSON_Value* temp = json_parse_string(jsonValue); + if (temp == NULL) + { + is_present_and_unparsable = true; + } + else + { + json_value_free(temp); + is_present_and_unparsable = false; + } + } + return is_present_and_unparsable; +} + +METHODRETURN_HANDLE MethodReturn_Create(int statusCode, const char* jsonValue) +{ + METHODRETURN_HANDLE result; + /*Codes_SRS_METHODRETURN_02_009: [ If jsonValue is not a JSON value then MethodReturn_Create shall fail and return NULL. ]*/ + if (is_json_present_and_unparsable(jsonValue)) + { + LogError("%s is not JSON", jsonValue); + result = NULL; + } + else + { + result = (METHODRETURN_HANDLE_DATA*)malloc(sizeof(METHODRETURN_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_METHODRETURN_02_002: [ If any failure is encountered then MethodReturn_Create shall return NULL ]*/ + LogError("unable to malloc"); + /*return as is*/ + } + else + { + if (jsonValue == NULL) + { + /*Codes_SRS_METHODRETURN_02_001: [ MethodReturn_Create shall create a non-NULL handle containing statusCode and a clone of jsonValue. ]*/ + result->data.jsonValue = NULL; + result->data.statusCode = statusCode; + } + else + { + if (mallocAndStrcpy_s(&(result->data.jsonValue), jsonValue) != 0) + { + LogError("failure in mallocAndStrcpy_s"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_METHODRETURN_02_001: [ MethodReturn_Create shall create a non-NULL handle containing statusCode and a clone of jsonValue. ]*/ + result->data.statusCode = statusCode; + } + } + } + } + + return result; +} + +void MethodReturn_Destroy(METHODRETURN_HANDLE handle) +{ + if (handle == NULL) + { + /*Codes_SRS_METHODRETURN_02_003: [ If handle is NULL then MethodReturn_Destroy shall return. ]*/ + LogError("invalid argument METHODRETURN_HANDLE handle=%p", handle); + } + else + { + /*Codes_SRS_METHODRETURN_02_004: [ Otherwise, MethodReturn_Destroy shall free all used resources by handle. ]*/ + if (handle->data.jsonValue != NULL) + { + free(handle->data.jsonValue); + } + free(handle); + } +} + + +const METHODRETURN_DATA* MethodReturn_GetReturn(METHODRETURN_HANDLE handle) +{ + const METHODRETURN_DATA* result; + if (handle == NULL) + { + /*Codes_SRS_METHODRETURN_02_010: [ If handle is NULL then MethodReturn_GetReturn shall fail and return NULL. ]*/ + result = NULL; + } + else + { + /*Codes_SRS_METHODRETURN_02_011: [ Otherwise, MethodReturn_GetReturn shall return a non-NULL const pointer to a METHODRETURN_DATA. ]*/ + result = &(handle->data); + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/multitree.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,828 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "multitree.h" +#include <string.h> +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/const_defines.h" + +/*assume a name cannot be longer than 100 characters*/ +#define INNER_NODE_NAME_SIZE 128 + +DEFINE_ENUM_STRINGS(MULTITREE_RESULT, MULTITREE_RESULT_VALUES); + +typedef struct MULTITREE_HANDLE_DATA_TAG +{ + char* name; + void* value; + MULTITREE_CLONE_FUNCTION cloneFunction; + MULTITREE_FREE_FUNCTION freeFunction; + size_t nChildren; + struct MULTITREE_HANDLE_DATA_TAG** children; /*an array of nChildren count of MULTITREE_HANDLE_DATA* */ +}MULTITREE_HANDLE_DATA; + + +MULTITREE_HANDLE MultiTree_Create(MULTITREE_CLONE_FUNCTION cloneFunction, MULTITREE_FREE_FUNCTION freeFunction) +{ + MULTITREE_HANDLE_DATA* result; + + /* Codes_SRS_MULTITREE_99_052:[If any of the arguments passed to MultiTree_Create is NULL, the call shall return NULL.]*/ + if ((cloneFunction == NULL) || + (freeFunction == NULL)) + { + LogError("CloneFunction or FreeFunction is Null."); + result = NULL; + } + else + { + /*Codes_SRS_MULTITREE_99_005:[ MultiTree_Create creates a new tree.]*/ + /*Codes_SRS_MULTITREE_99_006:[MultiTree_Create returns a non - NULL pointer if the tree has been successfully created.]*/ + /*Codes_SRS_MULTITREE_99_007:[MultiTree_Create returns NULL if the tree has not been successfully created.]*/ + result = (MULTITREE_HANDLE_DATA*)malloc(sizeof(MULTITREE_HANDLE_DATA)); + if (result != NULL) + { + result->name = NULL; + result->value = NULL; + result->cloneFunction = cloneFunction; + result->freeFunction = freeFunction; + result->nChildren = 0; + result->children = NULL; + } + else + { + LogError("MultiTree_Create failed because malloc failed"); + } + } + + return (MULTITREE_HANDLE)result; +} + + +/*return NULL if a child with the name "name" doesn't exists*/ +/*returns a pointer to the existing child (if any)*/ +static MULTITREE_HANDLE_DATA* getChildByName(MULTITREE_HANDLE_DATA* node, const char* name) +{ + MULTITREE_HANDLE_DATA* result = NULL; + size_t i; + for (i = 0; i < node->nChildren; i++) + { + if (strcmp(node->children[i]->name, name) == 0) + { + result = node->children[i]; + break; + } + } + return result; +} + +/*helper function to create a child immediately under this node*/ +/*return 0 if it created it, any other number is error*/ + +typedef enum CREATELEAF_RESULT_TAG +{ + CREATELEAF_OK, + CREATELEAF_ALREADY_EXISTS, + CREATELEAF_EMPTY_NAME, + CREATELEAF_ERROR, + CREATELEAF_RESULT_COUNT // Used to track the number of elements in the enum + // Do not remove, or add new enum values below this one +}CREATELEAF_RESULT; + +static STATIC_VAR_UNUSED const char* CreateLeaf_ResultAsString[CREATELEAF_RESULT_COUNT] = +{ + TOSTRING(CREATELEAF_OK), + TOSTRING(CREATELEAF_ALREADY_EXISTS), + TOSTRING(CREATELEAF_EMPTY_NAME), + TOSTRING(CREATELEAF_ERROR) +}; + +/*name cannot be empty, value can be empty or NULL*/ +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconditional-uninitialized" +#endif +#ifdef _MSC_VER +#pragma warning(disable: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track linked "newNode" and "result" therefore the warning*/ +#endif +static CREATELEAF_RESULT createLeaf(MULTITREE_HANDLE_DATA* node, const char*name, const char*value, MULTITREE_HANDLE_DATA** childNode) +{ + CREATELEAF_RESULT result; + /*can only create it if it doesn't exist*/ + if (strlen(name) == 0) + { + /*Codes_SRS_MULTITREE_99_024:[ if a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.]*/ + result = CREATELEAF_EMPTY_NAME; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else if (getChildByName(node, name) != NULL) + { + result = CREATELEAF_ALREADY_EXISTS; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else + { + MULTITREE_HANDLE_DATA* newNode = (MULTITREE_HANDLE_DATA*)malloc(sizeof(MULTITREE_HANDLE_DATA)); + if (newNode == NULL) + { + result = CREATELEAF_ERROR; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else + { + newNode->nChildren = 0; + newNode->children = NULL; + if (mallocAndStrcpy_s(&(newNode->name), name) != 0) + { + /*not nice*/ + free(newNode); + newNode = NULL; + result = CREATELEAF_ERROR; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else + { + newNode->cloneFunction = node->cloneFunction; + newNode->freeFunction = node->freeFunction; + + if (value == NULL) + { + newNode->value = NULL; + } + else if (node->cloneFunction(&(newNode->value), value) != 0) + { + free(newNode->name); + newNode->name = NULL; + free(newNode); + newNode = NULL; + result = CREATELEAF_ERROR; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else + { + /*all is fine until now*/ + } + } + + + if (newNode!=NULL) + { + /*allocate space in the father node*/ + MULTITREE_HANDLE_DATA** newChildren = (MULTITREE_HANDLE_DATA**)realloc(node->children, (node->nChildren + 1)*sizeof(MULTITREE_HANDLE_DATA*)); + if (newChildren == NULL) + { + /*no space for the new node*/ + newNode->value = NULL; + free(newNode->name); + newNode->name = NULL; + free(newNode); + newNode = NULL; + result = CREATELEAF_ERROR; + LogError("(result = %s)", CreateLeaf_ResultAsString[result]); + } + else + { + node->children = newChildren; + node->children[node->nChildren] = newNode; + node->nChildren++; + if (childNode != NULL) + { + *childNode = newNode; + } + result = CREATELEAF_OK; + } + } + } + } + + return result; +#ifdef _MSC_VER +#pragma warning(default: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track linked "newNode" and "result" therefore the warning*/ +#endif +#ifdef __APPLE__ +#pragma clang diagnostic pop +#endif +} + +MULTITREE_RESULT MultiTree_AddLeaf(MULTITREE_HANDLE treeHandle, const char* destinationPath, const void* value) +{ + /*codes_SRS_MULTITREE_99_018:[ If the treeHandle parameter is NULL, MULTITREE_INVALID_ARG shall be returned.]*/ + MULTITREE_RESULT result; + if (treeHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_019:[ If parameter destinationPath is NULL, MULTITREE_INVALID_ARG shall be returned.]*/ + else if (destinationPath == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_020:[ If parameter value is NULL, MULTITREE_INVALID_ARG shall be returned.]*/ + else if (value == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_050:[ If destinationPath a string with zero characters, MULTITREE_INVALID_ARG shall be returned.]*/ + else if (strlen(destinationPath) == 0) + { + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*break the path into components*/ + /*find the first child name*/ + MULTITREE_HANDLE_DATA * node = (MULTITREE_HANDLE_DATA *)treeHandle; + char * whereIsDelimiter; + /*if first character is / then skip it*/ + /*Codes_SRS_MULTITREE_99_014:[DestinationPath is a string in the following format: /child1/child12 or child1/child12] */ + if (destinationPath[0] == '/') + { + destinationPath++; + } + /*if there's just a string, it needs to be created here*/ + whereIsDelimiter = (char*)strchr(destinationPath, '/'); + if (whereIsDelimiter == NULL) + { + /*Codes_SRS_MULTITREE_99_017:[ Subsequent names designate hierarchical children in the tree. The last child designates the child that will receive the value.]*/ + CREATELEAF_RESULT res = createLeaf(node, destinationPath, (const char*)value, NULL); + switch (res) + { + default: + { + /*Codes_SRS_MULTITREE_99_025:[The function shall return MULTITREE_ERROR to indicate any other error not specified here.]*/ + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case CREATELEAF_ALREADY_EXISTS: + { + /*Codes_SRS_MULTITREE_99_021:[ If the node already has a value assigned to it, MULTITREE_ALREADY_HAS_A_VALUE shall be returned and the existing value shall not be changed.]*/ + result = MULTITREE_ALREADY_HAS_A_VALUE; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case CREATELEAF_OK: + { + /*Codes_SRS_MULTITREE_99_034:[ The function returns MULTITREE_OK when data has been stored in the tree.]*/ + result = MULTITREE_OK; + break; + } + case CREATELEAF_EMPTY_NAME: + { + /*Codes_SRS_MULTITREE_99_024:[ if a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.]*/ + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + } + } + else + { + /*if there's more or 1 delimiter in the path... */ + /*Codes_SRS_MULTITREE_99_017:[ Subsequent names designate hierarchical children in the tree. The last child designates the child that will receive the value.]*/ + char firstInnerNodeName[INNER_NODE_NAME_SIZE]; + if (strncpy_s(firstInnerNodeName, INNER_NODE_NAME_SIZE, destinationPath, whereIsDelimiter - destinationPath) != 0) + { + /*Codes_SRS_MULTITREE_99_025:[ The function shall return MULTITREE_ERROR to indicate any other error not specified here.]*/ + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA *child = getChildByName(node, firstInnerNodeName); + if (child == NULL) + { + /*Codes_SRS_MULTITREE_99_022:[ If a child along the path does not exist, it shall be created.] */ + /*Codes_SRS_MULTITREE_99_023:[ The newly created children along the path shall have a NULL value by default.]*/ + CREATELEAF_RESULT res = createLeaf(node, firstInnerNodeName, NULL, NULL); + switch (res) + { + default: + { + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case(CREATELEAF_EMPTY_NAME): + { + /*Codes_SRS_MULTITREE_99_024:[ if a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.]*/ + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case(CREATELEAF_OK): + { + MULTITREE_HANDLE_DATA *createdChild = getChildByName(node, firstInnerNodeName); + result = MultiTree_AddLeaf(createdChild, whereIsDelimiter, value); + break; + } + }; + } + else + { + result = MultiTree_AddLeaf(child, whereIsDelimiter, value); + } + } + } + } + return result; +} + +/* Codes_SRS_MULTITREE_99_053:[ MultiTree_AddChild shall add a new node with the name childName to the multi tree node identified by treeHandle] */ +MULTITREE_RESULT MultiTree_AddChild(MULTITREE_HANDLE treeHandle, const char* childName, MULTITREE_HANDLE* childHandle) +{ + MULTITREE_RESULT result; + /* Codes_SRS_MULTITREE_99_055:[ If any argument is NULL, MultiTree_AddChild shall return MULTITREE_INVALID_ARG.] */ + if ((treeHandle == NULL) || + (childName == NULL) || + (childHandle == NULL)) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA* childNode; + + /* Codes_SRS_MULTITREE_99_060:[ The value associated with the new node shall be NULL.] */ + CREATELEAF_RESULT res = createLeaf((MULTITREE_HANDLE_DATA*)treeHandle, childName, NULL, &childNode); + switch (res) + { + default: + { + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case CREATELEAF_ALREADY_EXISTS: + { + /* Codes_SRS_MULTITREE_99_061:[ If a child node with the same name already exists, MultiTree_AddChild shall return MULTITREE_ALREADY_HAS_A_VALUE.] */ + result = MULTITREE_ALREADY_HAS_A_VALUE; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + case CREATELEAF_OK: + { + /* Codes_SRS_MULTITREE_99_062:[ The new node handle shall be returned in the childHandle argument.] */ + *childHandle = childNode; + + /* Codes_SRS_MULTITREE_99_054:[ On success, MultiTree_AddChild shall return MULTITREE_OK.] */ + result = MULTITREE_OK; + break; + } + case CREATELEAF_EMPTY_NAME: + { + /* Tests_SRS_MULTITREE_99_066:[ If the childName argument is an empty string, MultiTree_AddChild shall return MULTITREE_EMPTY_CHILD_NAME.] */ + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + } + } + + return result; +} + +MULTITREE_RESULT MultiTree_GetChildCount(MULTITREE_HANDLE treeHandle, size_t* count) +{ + MULTITREE_RESULT result; + /*Codes_SRS_MULTITREE_99_027:[If treeHandle is NULL, the function returns MULTITREE_INVALID_ARG.]*/ + if (treeHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_028:[ If parameter count is NULL, the function returns MULTITREE_INVALID_ARG.]*/ + else if (count == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*Codes_SRS_MULTITREE_99_029:[ This function writes in *count the number of direct children for a tree node specified by the parameter treeHandle]*/ + *count = ((MULTITREE_HANDLE_DATA*)treeHandle)->nChildren; + /*Codes_SRS_MULTITREE_99_035:[ The function shall return MULTITREE_OK when *count contains the number of children of the node pointed to be parameter treeHandle.]*/ + result = MULTITREE_OK; + } + return result; +} + +MULTITREE_RESULT MultiTree_GetChild(MULTITREE_HANDLE treeHandle, size_t index, MULTITREE_HANDLE *childHandle) +{ + MULTITREE_RESULT result; + /*Codes_SRS_MULTITREE_99_031:[ If parameter treeHandle is NULL, the function returns MULTITREE_INVALID_ARG.]*/ + if (treeHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_033:[ If parameter childHandle is NULL, the function shall return MULTITREE_INVALID_ARG.]*/ + else if (childHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA * node = (MULTITREE_HANDLE_DATA *)treeHandle; + /*Codes_SRS_MULTITREE_99_032:[If parameter index is out of range, the function shall return MULTITREE_OUT_OF_RANGE_INDEX]*/ + if (node->nChildren <= index) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*Codes_SRS_MULTITREE_99_030:[ This function writes in *childHandle parameter the "index"th child of the node pointed to by parameter treeHandle]*/ + /*Codes_SRS_MULTITREE_99_035:[ The function returns MULTITREE_OK when *childHandle contains a handle to the "index"th child of the tree designated by parameter treeHandle.]*/ + *childHandle = node->children[index]; + result = MULTITREE_OK; + } + } + return result; +} + +MULTITREE_RESULT MultiTree_GetName(MULTITREE_HANDLE treeHandle, STRING_HANDLE destination) +{ + MULTITREE_RESULT result; + /*Codes_SRS_MULTITREE_99_037:[ If treeHandle is NULL, the function shall return MULTITREE_INVALID_ARG.]*/ + if (treeHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_038:[If destination is NULL, the function shall return MULTITREE_INVALID_ARG.]*/ + else if (destination == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA *node = (MULTITREE_HANDLE_DATA*)treeHandle; + /*Codes_SRS_MULTITREE_99_051:[ The function returns MULTITREE_EMPTY_CHILD_NAME when used with the root of the tree.]*/ + if (node->name == NULL) + { + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_036:[ This function fills the buffer pointed to by parameter destination with the name of the root node of the tree designated by parameter treeHandle.]*/ + else if (STRING_concat(destination, node->name)!=0) + { + /*Codes_SRS_MULTITREE_99_040:[ The function returns MULTITREE_ERROR to indicate any other error.]*/ + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*Codes_SRS_MULTITREE_99_039:[ The function returns MULTITREE_OK when destination contains the name of the root node of the tree designated by treeHandle parameter.]*/ + result = MULTITREE_OK; + } + } + + return result; +} + +/* Codes_SRS_MULTITREE_99_063:[ MultiTree_GetChildByName shall retrieve the handle of the child node childName from the treeNode node.] */ +MULTITREE_RESULT MultiTree_GetChildByName(MULTITREE_HANDLE treeHandle, const char* childName, MULTITREE_HANDLE *childHandle) +{ + MULTITREE_RESULT result; + + /* Codes_SRS_MULTITREE_99_065:[ If any argument is NULL, MultiTree_GetChildByName shall return MULTITREE_INVALID_ARG.] */ + if ((treeHandle == NULL) || + (childHandle == NULL) || + (childName == NULL)) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA * node = (MULTITREE_HANDLE_DATA *)treeHandle; + size_t i; + + for (i = 0; i < node->nChildren; i++) + { + if (strcmp(node->children[i]->name, childName) == 0) + { + break; + } + } + + if (i == node->nChildren) + { + /* Codes_SRS_MULTITREE_99_068:[ If the specified child is not found, MultiTree_GetChildByName shall return MULTITREE_CHILD_NOT_FOUND.] */ + result = MULTITREE_CHILD_NOT_FOUND; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /* Codes_SRS_MULTITREE_99_067:[ The child node handle shall be returned in the childHandle argument.] */ + *childHandle = node->children[i]; + + /* Codes_SRS_MULTITREE_99_064:[ On success, MultiTree_GetChildByName shall return MULTITREE_OK.] */ + result = MULTITREE_OK; + } + } + return result; +} + +MULTITREE_RESULT MultiTree_GetValue(MULTITREE_HANDLE treeHandle, const void** destination) +{ + MULTITREE_RESULT result; + /*Codes_SRS_MULTITREE_99_042:[If treeHandle is NULL, the function shall return MULTITREE_INVALID_ARG.]*/ + if (treeHandle == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_043:[ If destination is NULL, the function shall return MULTITREE_INVALID_ARG.]*/ + else if (destination == NULL) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA * node = (MULTITREE_HANDLE_DATA*)treeHandle; + /*Codes_SRS_MULTITREE_99_044:[ If there is no value in the node then MULTITREE_EMPTY_VALUE shall be returned.]*/ + if (node->value == NULL) + { + result = MULTITREE_EMPTY_VALUE; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*Codes_SRS_MULTITREE_99_041:[This function updates the *destination parameter to the internally stored value.]*/ + *destination = node->value; + result = MULTITREE_OK; + } + } + return result; +} + +MULTITREE_RESULT MultiTree_SetValue(MULTITREE_HANDLE treeHandle, void* value) +{ + MULTITREE_RESULT result; + + /* Codes_SRS_MULTITREE_99_074:[ If any argument is NULL, MultiTree_SetValue shall return MULTITREE_INVALID_ARG.] */ + if ((treeHandle == NULL) || + (value == NULL)) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + MULTITREE_HANDLE_DATA * node = (MULTITREE_HANDLE_DATA*)treeHandle; + if (node->value != NULL) + { + /* Codes_SRS_MULTITREE_99_076:[ If the node already has a value then MultiTree_SetValue shall return MULTITREE_ALREADY_HAS_A_VALUE.] */ + result = MULTITREE_ALREADY_HAS_A_VALUE; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /* Codes_SRS_MULTITREE_99_072:[ MultiTree_SetValue shall set the value of the node indicated by the treeHandle argument to the value of the argument value.] */ + if (node->cloneFunction(&node->value, value) != 0) + { + /* Codes_SRS_MULTITREE_99_075:[ MultiTree_SetValue shall return MULTITREE_ERROR to indicate any other error.] */ + result = MULTITREE_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /* Codes_SRS_MULTITREE_99_073:[ On success, MultiTree_SetValue shall return MULTITREE_OK.] */ + result = MULTITREE_OK; + } + } + } + return result; +} + +void MultiTree_Destroy(MULTITREE_HANDLE treeHandle) +{ + if (treeHandle != NULL) + { + MULTITREE_HANDLE_DATA* node = (MULTITREE_HANDLE_DATA*)treeHandle; + size_t i; + for (i = 0; i < node->nChildren;i++) + { + /*Codes_SRS_MULTITREE_99_047:[ This function frees any system resource used by the tree designated by parameter treeHandle]*/ + MultiTree_Destroy(node->children[i]); + } + /*Codes_SRS_MULTITREE_99_047:[ This function frees any system resource used by the tree designated by parameter treeHandle]*/ + if (node->children != NULL) + { + free(node->children); + node->children = NULL; + } + + /*Codes_SRS_MULTITREE_99_047:[ This function frees any system resource used by the tree designated by parameter treeHandle]*/ + if (node->name != NULL) + { + free(node->name); + node->name = NULL; + } + + /*Codes_SRS_MULTITREE_99_047:[ This function frees any system resource used by the tree designated by parameter treeHandle]*/ + if (node->value != NULL) + { + node->freeFunction(node->value); + node->value = NULL; + } + + /*Codes_SRS_MULTITREE_99_047:[ This function frees any system resource used by the tree designated by parameter treeHandle]*/ + free(node); + } +} + +MULTITREE_RESULT MultiTree_GetLeafValue(MULTITREE_HANDLE treeHandle, const char* leafPath, const void** destination) +{ + MULTITREE_RESULT result; + + /* Codes_SRS_MULTITREE_99_055:[ If any argument is NULL, MultiTree_GetLeafValue shall return MULTITREE_INVALID_ARG.] */ + if ((treeHandle == NULL) || + (leafPath == NULL) || + (destination == NULL)) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /* Codes_SRS_MULTITREE_99_058:[ The last child designates the child that will receive the value. If a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.] */ + else if (strlen(leafPath) == 0) + { + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + /*break the path into components*/ + /*find the first child name*/ + MULTITREE_HANDLE_DATA* node = (MULTITREE_HANDLE_DATA *)treeHandle; + const char* pos = leafPath; + const char * whereIsDelimiter; + + /*if first character is / then skip it*/ + if (*pos == '/') + { + pos++; + } + + if (*pos == '\0') + { + /* Codes_SRS_MULTITREE_99_069:[ If a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.] */ + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + result = MULTITREE_OK; + + /* Codes_SRS_MULTITREE_99_056:[ The leafPath argument is a string in the following format: /child1/child12 or child1/child12.] */ + /* Codes_SRS_MULTITREE_99_058:[ The last child designates the child that will receive the value.] */ + while (*pos != '\0') + { + size_t i; + size_t childCount = node->nChildren; + + whereIsDelimiter = pos; + + while ((*whereIsDelimiter != '/') && (*whereIsDelimiter != '\0')) + { + whereIsDelimiter++; + } + + if (whereIsDelimiter == pos) + { + /* Codes_SRS_MULTITREE_99_069:[ If a child name is empty (such as in "/child1//child12"), MULTITREE_EMPTY_CHILD_NAME shall be returned.] */ + result = MULTITREE_EMPTY_CHILD_NAME; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + else if (childCount == 0) + { + /* Codes_SRS_MULTITREE_99_071:[ When the child node is not found, MultiTree_GetLeafValue shall return MULTITREE_CHILD_NOT_FOUND.] */ + result = MULTITREE_CHILD_NOT_FOUND; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + else + { + for (i = 0; i < childCount; i++) + { + if (strncmp(node->children[i]->name, pos, whereIsDelimiter - pos) == 0) + { + /* Codes_SRS_MULTITREE_99_057:[ Subsequent names designate hierarchical children in the tree.] */ + node = node->children[i]; + break; + } + } + + if (i == childCount) + { + /* Codes_SRS_MULTITREE_99_071:[ When the child node is not found, MultiTree_GetLeafValue shall return MULTITREE_CHILD_NOT_FOUND.] */ + result = MULTITREE_CHILD_NOT_FOUND; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + break; + } + else + { + if (*whereIsDelimiter == '/') + { + pos = whereIsDelimiter + 1; + } + else + { + /* end of path */ + pos = whereIsDelimiter; + break; + } + } + } + } + + if (*pos == 0) + { + if (node->value == NULL) + { + /* Codes_SRS_MULTITREE_99_070:[ If an attempt is made to get the value for a node that does not have a value set, then MultiTree_GetLeafValue shall return MULTITREE_EMPTY_VALUE.] */ + result = MULTITREE_EMPTY_VALUE; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + /*Codes_SRS_MULTITREE_99_053:[ MultiTree_GetLeafValue shall copy into the *destination argument the value of the node identified by the leafPath argument.]*/ + else + { + *destination = node->value; + /* Codes_SRS_MULTITREE_99_054:[ On success, MultiTree_GetLeafValue shall return MULTITREE_OK.] */ + result = MULTITREE_OK; + } + } + } + } + return result; +} + +/* Codes_SRS_MULTITREE_99_077:[ MultiTree_DeleteChild shall remove the direct children node (no recursive search) set by childName.] */ +MULTITREE_RESULT MultiTree_DeleteChild(MULTITREE_HANDLE treeHandle, const char* childName) +{ + MULTITREE_RESULT result; + /* Codes_SRS_MULTITREE_99_077:[ If any argument is NULL, MultiTree_DeleteChild shall return MULTITREE_INVALID_ARG.] */ + if ((treeHandle == NULL) || + (childName == NULL)) + { + result = MULTITREE_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(MULTITREE_RESULT, result)); + } + else + { + size_t i; + size_t childToRemove = treeHandle->nChildren; + MULTITREE_HANDLE treeToRemove = NULL; + + for (i = 0; i < treeHandle->nChildren; i++) + { + if (0 == strcmp(treeHandle->children[i]->name, childName)) + { + childToRemove = i; + treeToRemove = treeHandle->children[childToRemove]; + break; + } + } + + if (i == treeHandle->nChildren) + { + /* Codes_SRS_MULTITREE_99_079:[If childName is not found, MultiTree_DeleteChild shall return MULTITREE_CHILD_NOT_FOUND.] */ + result = MULTITREE_CHILD_NOT_FOUND; + // Don't log error; this function is best effort only. Caller will determine actual error state. + } + else + { + for (i = childToRemove; i < treeHandle->nChildren - 1; i++) + { + treeHandle->children[i] = treeHandle->children[i+1]; + } + + /* Codes_SRS_MULTITREE_99_077:[ MultiTree_DeleteChild shall remove the direct children node (no recursive search) set by childName */ + MultiTree_Destroy(treeToRemove); + + // Even though this isn't reachable anymore after decrementing count, NULL out for cleanliness + treeHandle->children[treeHandle->nChildren - 1] = NULL; + treeHandle->nChildren = treeHandle->nChildren - 1; + + result = MULTITREE_OK; + } + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/schema.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,3220 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include "schema.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/vector.h" + + +DEFINE_ENUM_STRINGS(SCHEMA_RESULT, SCHEMA_RESULT_VALUES); + +typedef struct SCHEMA_PROPERTY_HANDLE_DATA_TAG +{ + const char* PropertyName; + const char* PropertyType; +} SCHEMA_PROPERTY_HANDLE_DATA; + +typedef struct SCHEMA_REPORTED_PROPERTY_HANDLE_DATA_TAG +{ + const char* reportedPropertyName; + const char* reportedPropertyType; +} SCHEMA_REPORTED_PROPERTY_HANDLE_DATA; + +typedef struct SCHEMA_DESIRED_PROPERTY_HANDLE_DATA_TAG +{ + pfOnDesiredProperty onDesiredProperty; + pfDesiredPropertyInitialize desiredPropertInitialize; + pfDesiredPropertyDeinitialize desiredPropertDeinitialize; + const char* desiredPropertyName; + const char* desiredPropertyType; + pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE; + size_t offset; +} SCHEMA_DESIRED_PROPERTY_HANDLE_DATA; + +typedef struct SCHEMA_ACTION_ARGUMENT_HANDLE_DATA_TAG +{ + const char* Name; + const char* Type; +} SCHEMA_ACTION_ARGUMENT_HANDLE_DATA; + +typedef struct SCHEMA_METHOD_ARGUMENT_HANDLE_DATA_TAG +{ + char* Name; + char* Type; +} SCHEMA_METHOD_ARGUMENT_HANDLE_DATA; + +typedef struct SCHEMA_ACTION_HANDLE_DATA_TAG +{ + const char* ActionName; + size_t ArgumentCount; + SCHEMA_ACTION_ARGUMENT_HANDLE* ArgumentHandles; +} SCHEMA_ACTION_HANDLE_DATA; + +typedef struct SCHEMA_METHOD_HANDLE_DATA_TAG +{ + char* methodName; + VECTOR_HANDLE methodArguments; /*holds SCHEMA_METHOD_ARGUMENT_HANDLE*/ +} SCHEMA_METHOD_HANDLE_DATA; + +typedef struct MODEL_IN_MODEL_TAG +{ + pfOnDesiredProperty onDesiredProperty; /*is NULL if not specified or if the model in model is not WITH_DESIRED_PROPERTY*/ + size_t offset; /*offset of the model in model (offsetof)*/ + const char* propertyName; + SCHEMA_MODEL_TYPE_HANDLE modelHandle; +} MODEL_IN_MODEL; + +typedef struct SCHEMA_MODEL_TYPE_HANDLE_DATA_TAG +{ + VECTOR_HANDLE methods; /*holds SCHEMA_METHOD_HANDLE*/ + VECTOR_HANDLE desiredProperties; /*holds SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*/ + const char* Name; + SCHEMA_HANDLE SchemaHandle; + SCHEMA_PROPERTY_HANDLE* Properties; + size_t PropertyCount; + VECTOR_HANDLE reportedProperties; /*holds SCHEMA_REPORTED_PROPERTY_HANDLE*/ + SCHEMA_ACTION_HANDLE* Actions; + size_t ActionCount; + VECTOR_HANDLE models; + size_t DeviceCount; +} SCHEMA_MODEL_TYPE_HANDLE_DATA; + +typedef struct SCHEMA_STRUCT_TYPE_HANDLE_DATA_TAG +{ + const char* Name; + SCHEMA_PROPERTY_HANDLE* Properties; + size_t PropertyCount; +} SCHEMA_STRUCT_TYPE_HANDLE_DATA; + +typedef struct SCHEMA_HANDLE_DATA_TAG +{ + void* metadata; + const char* Namespace; + SCHEMA_MODEL_TYPE_HANDLE* ModelTypes; + size_t ModelTypeCount; + SCHEMA_STRUCT_TYPE_HANDLE* StructTypes; + size_t StructTypeCount; +} SCHEMA_HANDLE_DATA; + +static VECTOR_HANDLE g_schemas = NULL; + +static void DestroyProperty(SCHEMA_PROPERTY_HANDLE propertyHandle) +{ + SCHEMA_PROPERTY_HANDLE_DATA* propertyType = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle; + free((void*)propertyType->PropertyName); + free((void*)propertyType->PropertyType); + free(propertyType); +} + +static void DestroyActionArgument(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle) +{ + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle; + if (actionArgument != NULL) + { + free((void*)actionArgument->Name); + free((void*)actionArgument->Type); + free(actionArgument); + } +} + +static void DestroyMethodArgument(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle) +{ + free(methodArgumentHandle->Name); + free(methodArgumentHandle->Type); + free(methodArgumentHandle); +} + +static void DestroyAction(SCHEMA_ACTION_HANDLE actionHandle) +{ + SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + if (action != NULL) + { + size_t j; + + for (j = 0; j < action->ArgumentCount; j++) + { + DestroyActionArgument(action->ArgumentHandles[j]); + } + free(action->ArgumentHandles); + + free((void*)action->ActionName); + free(action); + } +} + +static void DestroyMethod(SCHEMA_METHOD_HANDLE methodHandle) +{ + size_t nArguments = VECTOR_size(methodHandle->methodArguments); + + for (size_t j = 0; j < nArguments; j++) + { + SCHEMA_METHOD_ARGUMENT_HANDLE* methodArgumentHandle = VECTOR_element(methodHandle->methodArguments, j); + DestroyMethodArgument(*methodArgumentHandle); + } + free(methodHandle->methodName); + VECTOR_destroy(methodHandle->methodArguments); + free(methodHandle); +} + +static void DestroyMethods(SCHEMA_MODEL_TYPE_HANDLE modelHandle) +{ + size_t nMethods = VECTOR_size(modelHandle->methods); + + for (size_t j = 0; j < nMethods; j++) + { + SCHEMA_METHOD_HANDLE* methodHandle = VECTOR_element(modelHandle->methods, j); + DestroyMethod(*methodHandle); + } + VECTOR_destroy(modelHandle->methods); +} + + +static void DestroyStruct(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle) +{ + size_t i; + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle; + if (structType != NULL) + { + for (i = 0; i < structType->PropertyCount; i++) + { + DestroyProperty(structType->Properties[i]); + } + free(structType->Properties); + + free((void*)structType->Name); + + free(structType); + } +} + +static void DestroyModel(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + size_t i, nReportedProperties; + + free((void*)modelType->Name); + modelType->Name = NULL; + + for (i = 0; i < modelType->PropertyCount; i++) + { + DestroyProperty(modelType->Properties[i]); + } + + free(modelType->Properties); + + for (i = 0; i < modelType->ActionCount; i++) + { + DestroyAction(modelType->Actions[i]); + } + + DestroyMethods(modelType); + + /*destroy the vector holding the added models. This does not destroy the said models, however, their names shall be*/ + for (i = 0; i < VECTOR_size(modelType->models); i++) + { + MODEL_IN_MODEL* temp = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i); + free((void*)temp->propertyName); + } + + nReportedProperties = VECTOR_size(modelType->reportedProperties); + for (i = 0;i < nReportedProperties;i++) + { + SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = *(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA **)VECTOR_element(modelType->reportedProperties, i); + free((void*)reportedProperty->reportedPropertyName); + free((void*)reportedProperty->reportedPropertyType); + free(reportedProperty); + } + VECTOR_destroy(modelType->reportedProperties); + + size_t nDesiredProperties = VECTOR_size(modelType->desiredProperties); + for (i = 0;i < nDesiredProperties;i++) + { + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = *(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA **)VECTOR_element(modelType->desiredProperties, i); + free((void*)desiredProperty->desiredPropertyName); + free((void*)desiredProperty->desiredPropertyType); + free(desiredProperty); + } + VECTOR_destroy(modelType->desiredProperties); + + VECTOR_clear(modelType->models); + VECTOR_destroy(modelType->models); + + free(modelType->Actions); + free(modelType); +} + +static SCHEMA_RESULT AddModelProperty(SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType, const char* name, const char* type) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_013:[If any of the arguments is NULL, Schema_AddModelProperty shall return SCHEMA_INVALID_ARG.] */ + if ((modelType == NULL) || + (name == NULL) || + (type == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + size_t i; + + /* Codes_SRS_SCHEMA_99_015:[The property name shall be unique per model, if the same property name is added twice to a model, SCHEMA_DUPLICATE_ELEMENT shall be returned.] */ + for (i = 0; i < modelType->PropertyCount; i++) + { + SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i]; + if (strcmp(property->PropertyName, name) == 0) + { + break; + } + } + + if (i < modelType->PropertyCount) + { + result = SCHEMA_DUPLICATE_ELEMENT; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_PROPERTY_HANDLE* newProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(modelType->Properties, sizeof(SCHEMA_PROPERTY_HANDLE) * (modelType->PropertyCount + 1)); + if (newProperties == NULL) + { + /* Codes_SRS_SCHEMA_99_014:[On any other error, Schema_AddModelProperty shall return SCHEMA_ERROR.] */ + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_PROPERTY_HANDLE_DATA* newProperty; + + modelType->Properties = newProperties; + if ((newProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_PROPERTY_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_SCHEMA_99_014:[On any other error, Schema_AddModelProperty shall return SCHEMA_ERROR.] */ + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + if (mallocAndStrcpy_s((char**)&newProperty->PropertyName, name) != 0) + { + /* Codes_SRS_SCHEMA_99_014:[On any other error, Schema_AddModelProperty shall return SCHEMA_ERROR.] */ + free(newProperty); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else if (mallocAndStrcpy_s((char**)&newProperty->PropertyType, type) != 0) + { + /* Codes_SRS_SCHEMA_99_014:[On any other error, Schema_AddModelProperty shall return SCHEMA_ERROR.] */ + free((void*)newProperty->PropertyName); + free(newProperty); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + modelType->Properties[modelType->PropertyCount] = (SCHEMA_PROPERTY_HANDLE)newProperty; + modelType->PropertyCount++; + + /* Codes_SRS_SCHEMA_99_012:[On success, Schema_AddModelProperty shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + } + + /* If possible, reduce the memory of over allocation */ + if (result != SCHEMA_OK) + { + SCHEMA_PROPERTY_HANDLE* oldProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(modelType->Properties, sizeof(SCHEMA_PROPERTY_HANDLE) * modelType->PropertyCount); + if (oldProperties == NULL) + { + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + modelType->Properties = oldProperties; + } + } + } + } + } + + return result; +} + +static bool SchemaHandlesMatch(const SCHEMA_HANDLE* handle, const SCHEMA_HANDLE* otherHandle) +{ + return (*handle == *otherHandle); +} + +static bool SchemaNamespacesMatch(const SCHEMA_HANDLE* handle, const char* schemaNamespace) +{ + const SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)*handle; + return (strcmp(schema->Namespace, schemaNamespace) == 0); +} + +/* Codes_SRS_SCHEMA_99_001:[Schema_Create shall initialize a schema with a given namespace.] */ +SCHEMA_HANDLE Schema_Create(const char* schemaNamespace, void* metadata) +{ + SCHEMA_HANDLE_DATA* result; + /*Codes_SRS_SCHEMA_02_090: [ If metadata is NULL then Schema_Create shall fail and return NULL. ]*/ + /* Codes_SRS_SCHEMA_99_004:[If schemaNamespace is NULL, Schema_Create shall fail.] */ + if ( + (schemaNamespace == NULL)|| + (metadata == NULL) + ) + { + /* Codes_SRS_SCHEMA_99_003:[On failure, NULL shall be returned.] */ + LogError("invalid arg const char* schemaNamespace=%p, void* metadata=%p",schemaNamespace, metadata); + result = NULL; + } + else + { + if (g_schemas == NULL && (g_schemas = VECTOR_create(sizeof(SCHEMA_HANDLE_DATA*) ) ) == NULL) + { + /* Codes_SRS_SCHEMA_99_003:[On failure, NULL shall be returned.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else if ((result = (SCHEMA_HANDLE_DATA*)malloc(sizeof(SCHEMA_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_SCHEMA_99_003:[On failure, NULL shall be returned.] */ + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else if (mallocAndStrcpy_s((char**)&result->Namespace, schemaNamespace) != 0) + { + /* Codes_SRS_SCHEMA_99_003:[On failure, NULL shall be returned.] */ + free(result); + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else if (VECTOR_push_back(g_schemas, &result, 1) != 0) + { + free((void*)result->Namespace); + free(result); + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + /* Codes_SRS_SCHEMA_99_002:[On success a non-NULL handle to the newly created schema shall be returned.] */ + result->ModelTypes = NULL; + result->ModelTypeCount = 0; + result->StructTypes = NULL; + result->StructTypeCount = 0; + result->metadata = metadata; + } + } + + return (SCHEMA_HANDLE)result; +} + +size_t Schema_GetSchemaCount(void) +{ + /* Codes_SRS_SCHEMA_99_153: [Schema_GetSchemaCount shall return the number of "active" schemas (all schemas created with Schema_Create + in the current process, for which Schema_Destroy has not been called).] */ + return VECTOR_size(g_schemas); +} + +SCHEMA_HANDLE Schema_GetSchemaByNamespace(const char* schemaNamespace) +{ + /* Codes_SRS_SCHEMA_99_151: [If no active schema matches the schemaNamespace argument, Schema_GetSchemaByNamespace shall return NULL.] */ + SCHEMA_HANDLE result = NULL; + + /* Codes_SRS_SCHEMA_99_150: [If the schemaNamespace argument is NULL, Schema_GetSchemaByNamespace shall return NULL.] */ + if (schemaNamespace != NULL) + { + SCHEMA_HANDLE* handle = (g_schemas==NULL)?NULL:(SCHEMA_HANDLE*)VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaNamespacesMatch, schemaNamespace); + if (handle != NULL) + { + /* Codes_SRS_SCHEMA_99_148: [Schema_GetSchemaByNamespace shall search all active schemas and return the schema with the + namespace given by the schemaNamespace argument.] */ + result = *handle; + } + } + + return result; +} + +const char* Schema_GetSchemaNamespace(SCHEMA_HANDLE schemaHandle) +{ + const char* result; + + /* Codes_SRS_SCHEMA_99_130: [If the schemaHandle argument is NULL, Schema_GetSchemaNamespace shall return NULL.] */ + if (schemaHandle == NULL) + { + result = NULL; + } + else + { + /* Codes_SRS_SCHEMA_99_129: [Schema_GetSchemaNamespace shall return the namespace for the schema identified by schemaHandle.] */ + result = ((SCHEMA_HANDLE_DATA*)schemaHandle)->Namespace; + } + + return result; +} + +void Schema_Destroy(SCHEMA_HANDLE schemaHandle) +{ + /* Codes_SRS_SCHEMA_99_006:[If the schemaHandle is NULL, Schema_Destroy shall do nothing.] */ + if (schemaHandle != NULL) + { + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + size_t i; + + /* Codes_SRS_SCHEMA_99_005:[Schema_Destroy shall free all resources associated with a schema.] */ + for (i = 0; i < schema->ModelTypeCount; i++) + { + DestroyModel(schema->ModelTypes[i]); + } + + free(schema->ModelTypes); + + /* Codes_SRS_SCHEMA_99_005:[Schema_Destroy shall free all resources associated with a schema.] */ + for (i = 0; i < schema->StructTypeCount; i++) + { + DestroyStruct(schema->StructTypes[i]); + } + + free(schema->StructTypes); + free((void*)schema->Namespace); + free(schema); + + schema = (SCHEMA_HANDLE_DATA*)VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaHandlesMatch, &schemaHandle); + if (schema != NULL) + { + VECTOR_erase(g_schemas, schema, 1); + } + // If the g_schema is empty then destroy it + if (VECTOR_size(g_schemas) == 0) + { + VECTOR_destroy(g_schemas); + g_schemas = NULL; + } + } +} + +SCHEMA_RESULT Schema_DestroyIfUnused(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_07_189: [If modelHandle variable is NULL Schema_DestroyIfUnused shall do nothing.] */ + if (modelTypeHandle != NULL) + { + SCHEMA_HANDLE schemaHandle = Schema_GetSchemaForModelType(modelTypeHandle); + if (schemaHandle == NULL) + { + result = SCHEMA_ERROR; + } + else + { + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + size_t nIndex; + + /* Codes_SRS_SCHEMA_07_190: [Schema_DestroyIfUnused shall iterate through the ModuleTypes objects and if all the DeviceCount variables 0 then it will delete the schemaHandle.] */ + for (nIndex = 0; nIndex < schema->ModelTypeCount; nIndex++) + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)schema->ModelTypes[nIndex]; + if (modelType->DeviceCount > 0) + break; + } + /* Codes_SRS_SCHEMA_07_191: [If 1 or more DeviceCount variables are > 0 then Schema_DestroyIfUnused shall do nothing.] */ + if (nIndex == schema->ModelTypeCount) + { + Schema_Destroy(schemaHandle); + result = SCHEMA_OK; + } + else + { + result = SCHEMA_MODEL_IN_USE; + } + } + } + else + { + result = SCHEMA_INVALID_ARG; + } + return result; +} + +SCHEMA_RESULT Schema_AddDeviceRef(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + SCHEMA_RESULT result; + if (modelTypeHandle == NULL) + { + /* Codes_SRS_SCHEMA_07_187: [Schema_AddDeviceRef shall return SCHEMA_INVALID_ARG if modelTypeHandle is NULL.] */ + result = SCHEMA_INVALID_ARG; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /* Codes_SRS_SCHEMA_07_188: [If the modelTypeHandle is nonNULL, Schema_AddDeviceRef shall increment the SCHEMA_MODEL_TYPE_HANDLE_DATA DeviceCount variable.] */ + model->DeviceCount++; + result = SCHEMA_OK; + } + return result; +} + +SCHEMA_RESULT Schema_ReleaseDeviceRef(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + SCHEMA_RESULT result; + /* Codes_SRS_SCHEMA_07_184: [Schema_DeviceRelease shall do nothing if the supplied modelHandle is NULL.] */ + if (modelTypeHandle == NULL) + { + result = SCHEMA_INVALID_ARG; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + if (model->DeviceCount > 0) + { + /* Codes_SRS_SCHEMA_07_186: [On a nonNULL SCHEMA_MODEL_TYPE_HANDLE if the DeviceCount variable is > 0 then the variable will be decremented.] */ + model->DeviceCount--; + result = SCHEMA_OK; + } + else + { +result = SCHEMA_DEVICE_COUNT_ZERO; +LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + } + return result; +} + +/* Codes_SRS_SCHEMA_99_007:[Schema_CreateModelType shall create a new model type and return a handle to it.] */ +SCHEMA_MODEL_TYPE_HANDLE Schema_CreateModelType(SCHEMA_HANDLE schemaHandle, const char* modelName) +{ + SCHEMA_MODEL_TYPE_HANDLE result; + + /* Codes_SRS_SCHEMA_99_010:[If any of the arguments is NULL, Schema_CreateModelType shall fail.] */ + if ((schemaHandle == NULL) || + (modelName == NULL)) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + + /* Codes_SRS_SCHEMA_99_100: [Schema_CreateModelType shall return SCHEMA_DUPLICATE_ELEMENT if modelName already exists.] */ + size_t i; + for (i = 0; i < schema->ModelTypeCount; i++) + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)(schema->ModelTypes[i]); + if (strcmp(model->Name, modelName) == 0) + { + break; + } + } + + if (i < schema->ModelTypeCount) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + result = NULL; + LogError("%s Model Name already exists", modelName); + } + + else + { + SCHEMA_MODEL_TYPE_HANDLE* newModelTypes = (SCHEMA_MODEL_TYPE_HANDLE*)realloc(schema->ModelTypes, sizeof(SCHEMA_MODEL_TYPE_HANDLE) * (schema->ModelTypeCount + 1)); + if (newModelTypes == NULL) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType; + schema->ModelTypes = newModelTypes; + + if ((modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)malloc(sizeof(SCHEMA_MODEL_TYPE_HANDLE_DATA))) == NULL) + { + + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else if (mallocAndStrcpy_s((char**)&modelType->Name, modelName) != 0) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + result = NULL; + free(modelType); + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + modelType->models = VECTOR_create(sizeof(MODEL_IN_MODEL)); + if (modelType->models == NULL) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + LogError("unable to VECTOR_create"); + free((void*)modelType->Name); + free((void*)modelType); + result = NULL; + } + else + { + if ((modelType->reportedProperties = VECTOR_create(sizeof(SCHEMA_REPORTED_PROPERTY_HANDLE))) == NULL) + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + LogError("failed to VECTOR_create (reported properties)"); + VECTOR_destroy(modelType->models); + free((void*)modelType->Name); + free((void*)modelType); + result = NULL; + + } + else + { + /* Codes_SRS_SCHEMA_99_009:[On failure, Schema_CreateModelType shall return NULL.] */ + if ((modelType->desiredProperties = VECTOR_create(sizeof(SCHEMA_DESIRED_PROPERTY_HANDLE))) == NULL) + { + LogError("failure in VECTOR_create (desired properties)"); + VECTOR_destroy(modelType->reportedProperties); + VECTOR_destroy(modelType->models); + free((void*)modelType->Name); + free((void*)modelType); + result = NULL; + } + else + { + if ((modelType->methods = VECTOR_create(sizeof(SCHEMA_METHOD_HANDLE))) == NULL) + { + LogError("failure in VECTOR_create (desired properties)"); + VECTOR_destroy(modelType->desiredProperties); + VECTOR_destroy(modelType->reportedProperties); + VECTOR_destroy(modelType->models); + free((void*)modelType->Name); + free((void*)modelType); + result = NULL; + } + else + { + modelType->PropertyCount = 0; + modelType->Properties = NULL; + modelType->ActionCount = 0; + modelType->Actions = NULL; + modelType->SchemaHandle = schemaHandle; + modelType->DeviceCount = 0; + + schema->ModelTypes[schema->ModelTypeCount] = modelType; + schema->ModelTypeCount++; + /* Codes_SRS_SCHEMA_99_008:[On success, a non-NULL handle shall be returned.] */ + result = (SCHEMA_MODEL_TYPE_HANDLE)modelType; + } + } + } + } + } + + /* If possible, reduce the memory of over allocation */ + if ((result == NULL) &&(schema->ModelTypeCount>0)) + { + SCHEMA_MODEL_TYPE_HANDLE* oldModelTypes = (SCHEMA_MODEL_TYPE_HANDLE*)realloc(schema->ModelTypes, sizeof(SCHEMA_MODEL_TYPE_HANDLE) * schema->ModelTypeCount); + if (oldModelTypes == NULL) + { + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + schema->ModelTypes = oldModelTypes; + } + } + } + } + } + + return result; +} + +SCHEMA_HANDLE Schema_GetSchemaForModelType(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + SCHEMA_HANDLE result; + + if (modelTypeHandle == NULL) + { + /* Codes_SRS_SCHEMA_99_132: [If the modelTypeHandle argument is NULL, Schema_GetSchemaForModelType shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_SCHEMA_99_131: [Schema_GetSchemaForModelType returns the schema handle for a given model type.] */ + result = ((SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle)->SchemaHandle; + } + + return result; +} + +/* Codes_SRS_SCHEMA_99_011:[Schema_AddModelProperty shall add one property to the model type identified by modelTypeHandle.] */ +SCHEMA_RESULT Schema_AddModelProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName, const char* propertyType) +{ + return AddModelProperty((SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle, propertyName, propertyType); +} + +static bool reportedPropertyExists(const void* element, const void* value) +{ + SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = *(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA**)element; + return (strcmp(reportedProperty->reportedPropertyName, value) == 0); +} + +SCHEMA_RESULT Schema_AddModelReportedProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyName, const char* reportedPropertyType) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_02_001: [ If modelTypeHandle is NULL then Schema_AddModelReportedProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_002: [ If reportedPropertyName is NULL then Schema_AddModelReportedProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_003: [ If reportedPropertyType is NULL then Schema_AddModelReportedProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ( + (modelTypeHandle == NULL) || + (reportedPropertyName == NULL) || + (reportedPropertyType == NULL) + ) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyName=%p, const char* reportedPropertyType=%p", modelTypeHandle, reportedPropertyName, reportedPropertyType); + result = SCHEMA_INVALID_ARG; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_004: [ If reportedPropertyName has already been added then Schema_AddModelReportedProperty shall fail and return SCHEMA_PROPERTY_ELEMENT_EXISTS. ]*/ + if (VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyName) != NULL) + { + LogError("unable to add reportedProperty %s because it already exists", reportedPropertyName); + result = SCHEMA_DUPLICATE_ELEMENT; + } + else + { + /*Codes_SRS_SCHEMA_02_005: [ Schema_AddModelReportedProperty shall record reportedPropertyName and reportedPropertyType. ]*/ + SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = (SCHEMA_REPORTED_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA)); + if (reportedProperty == NULL) + { + /*Codes_SRS_SCHEMA_02_006: [ If any error occurs then Schema_AddModelReportedProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("unable to malloc"); + result = SCHEMA_ERROR; + } + else + { + if (mallocAndStrcpy_s((char**)&reportedProperty->reportedPropertyName, reportedPropertyName) != 0) + { + /*Codes_SRS_SCHEMA_02_006: [ If any error occurs then Schema_AddModelReportedProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + free(reportedProperty); + result = SCHEMA_ERROR; + } + else + { + if (mallocAndStrcpy_s((char**)&reportedProperty->reportedPropertyType, reportedPropertyType) != 0) + { + /*Codes_SRS_SCHEMA_02_006: [ If any error occurs then Schema_AddModelReportedProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + free((void*)reportedProperty->reportedPropertyName); + free(reportedProperty); + result = SCHEMA_ERROR; + } + else + { + if (VECTOR_push_back(modelType->reportedProperties, &reportedProperty, 1) != 0) + { + /*Codes_SRS_SCHEMA_02_006: [ If any error occurs then Schema_AddModelReportedProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("unable to VECTOR_push_back"); + free((void*)reportedProperty->reportedPropertyType); + free((void*)reportedProperty->reportedPropertyName); + free(reportedProperty); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_007: [ Otherwise Schema_AddModelReportedProperty shall succeed and return SCHEMA_OK. ]*/ + result = SCHEMA_OK; + } + } + } + } + } + } + return result; +} + +SCHEMA_ACTION_HANDLE Schema_CreateModelAction(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* actionName) +{ + SCHEMA_ACTION_HANDLE result; + + /* Codes_SRS_SCHEMA_99_104: [If any of the modelTypeHandle or actionName arguments is NULL, Schema_CreateModelAction shall return NULL.] */ + if ((modelTypeHandle == NULL) || + (actionName == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + size_t i; + + /* Codes_SRS_SCHEMA_99_105: [The action name shall be unique per model, if the same action name is added twice to a model, Schema_CreateModelAction shall return NULL.] */ + for (i = 0; i < modelType->ActionCount; i++) + { + SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)modelType->Actions[i]; + if (strcmp(action->ActionName, actionName) == 0) + { + break; + } + } + + if (i < modelType->ActionCount) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_DUPLICATE_ELEMENT)); + } + else + { + /* Codes_SRS_SCHEMA_99_102: [Schema_CreateModelAction shall add one action to the model type identified by modelTypeHandle.] */ + SCHEMA_ACTION_HANDLE* newActions = (SCHEMA_ACTION_HANDLE*)realloc(modelType->Actions, sizeof(SCHEMA_ACTION_HANDLE) * (modelType->ActionCount + 1)); + if (newActions == NULL) + { + /* Codes_SRS_SCHEMA_99_106: [On any other error, Schema_CreateModelAction shall return NULL.]*/ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + SCHEMA_ACTION_HANDLE_DATA* newAction; + modelType->Actions = newActions; + + /* Codes_SRS_SCHEMA_99_103: [On success, Schema_CreateModelAction shall return a none-NULL SCHEMA_ACTION_HANDLE to the newly created action.] */ + if ((newAction = (SCHEMA_ACTION_HANDLE_DATA*)malloc(sizeof(SCHEMA_ACTION_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_SCHEMA_99_106: [On any other error, Schema_CreateModelAction shall return NULL.]*/ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + if (mallocAndStrcpy_s((char**)&newAction->ActionName, actionName) != 0) + { + /* Codes_SRS_SCHEMA_99_106: [On any other error, Schema_CreateModelAction shall return NULL.]*/ + free(newAction); + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + newAction->ArgumentCount = 0; + newAction->ArgumentHandles = NULL; + + modelType->Actions[modelType->ActionCount] = newAction; + modelType->ActionCount++; + result = (SCHEMA_ACTION_HANDLE)(newAction); + } + + /* If possible, reduce the memory of over allocation */ + if (result == NULL) + { + SCHEMA_ACTION_HANDLE* oldActions = (SCHEMA_ACTION_HANDLE*)realloc(modelType->Actions, sizeof(SCHEMA_ACTION_HANDLE) * modelType->ActionCount); + if (oldActions == NULL) + { + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + modelType->Actions = oldActions; + } + } + } + } + } + } + return result; +} + + +static bool methodExists(const void* element, const void* value) +{ + const SCHEMA_METHOD_HANDLE* method = (const SCHEMA_METHOD_HANDLE*)element; + return (strcmp((*method)->methodName, value) == 0); +} + + +SCHEMA_METHOD_HANDLE Schema_CreateModelMethod(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* methodName) +{ + SCHEMA_METHOD_HANDLE result; + + /*Codes_SRS_SCHEMA_02_096: [ If modelTypeHandle is NULL then Schema_CreateModelMethod shall fail and return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_097: [ If methodName is NULL then Schema_CreateModelMethod shall fail and return NULL. ]*/ + if ((modelTypeHandle == NULL) || + (methodName == NULL)) + { + result = NULL; + LogError("invalid argument: SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* methodName=%p", modelTypeHandle, methodName); + } + else + { + /*Codes_SRS_SCHEMA_02_103: [ If methodName already exists, then Schema_CreateModelMethod shall fail and return NULL. ]*/ + if (VECTOR_find_if(modelTypeHandle->methods, methodExists, methodName) != NULL) + { + LogError("method %s already exists", methodName); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_098: [ Schema_CreateModelMethod shall allocate the space for the method. ]*/ + result = malloc(sizeof(SCHEMA_METHOD_HANDLE_DATA)); + if (result == NULL) + { + LogError("failed to malloc"); + /*Codes_SRS_SCHEMA_02_102: [ If any of the above fails, then Schema_CreateModelMethod shall fail and return NULL. ]*/ + /*return as is*/ + } + else + { + /*Codes_SRS_SCHEMA_02_099: [ Schema_CreateModelMethod shall create a VECTOR that will hold the method's arguments. ]*/ + result->methodArguments = VECTOR_create(sizeof(SCHEMA_METHOD_ARGUMENT_HANDLE)); + if (result->methodArguments == NULL) + { + /*Codes_SRS_SCHEMA_02_102: [ If any of the above fails, then Schema_CreateModelMethod shall fail and return NULL. ]*/ + LogError("failure in VECTOR_create"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_100: [ Schema_CreateModelMethod shall clone methodName ]*/ + if (mallocAndStrcpy_s(&result->methodName, methodName) != 0) + { + /*Codes_SRS_SCHEMA_02_102: [ If any of the above fails, then Schema_CreateModelMethod shall fail and return NULL. ]*/ + LogError("failure in mallocAndStrcpy_s"); + VECTOR_destroy(result->methodArguments); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_101: [ Schema_CreateModelMethod shall add the new created method to the model's list of methods. ]*/ + if (VECTOR_push_back(modelTypeHandle->methods, &result, 1) != 0) + { + /*Codes_SRS_SCHEMA_02_102: [ If any of the above fails, then Schema_CreateModelMethod shall fail and return NULL. ]*/ + LogError("failure in VECTOR_push_back"); + free(result->methodName); + VECTOR_destroy(result->methodArguments); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_104: [ Otherwise, Schema_CreateModelMethod shall succeed and return a non-NULL SCHEMA_METHOD_HANDLE. ]*/ + /*return as is*/ + } + } + } + } + } + } + return result; +} + + +SCHEMA_RESULT Schema_AddModelActionArgument(SCHEMA_ACTION_HANDLE actionHandle, const char* argumentName, const char* argumentType) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_109: [If any of the arguments is NULL, Schema_AddModelActionArgument shall return SCHEMA_INVALID_ARG.] */ + if ((actionHandle == NULL) || + (argumentName == NULL) || + (argumentType == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + + /* Codes_SRS_SCHEMA_99_110: [The argument name shall be unique per action, if the same name is added twice to an action, SCHEMA_DUPLICATE_ELEMENT shall be returned.] */ + /* Codes_SRS_SCHEMA_99_111: [Schema_AddModelActionArgument shall accept arguments with different names of the same type.] */ + size_t i; + for (i = 0; i < action->ArgumentCount; i++) + { + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)action->ArgumentHandles[i]; + if (strcmp((actionArgument->Name), argumentName) == 0) + { + break; + } + } + + if (i < action->ArgumentCount) + { + result = SCHEMA_DUPLICATE_ELEMENT; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMA_99_107: [Schema_AddModelActionArgument shall add one argument name & type to an action identified by actionHandle.] */ + SCHEMA_ACTION_ARGUMENT_HANDLE* newArguments = (SCHEMA_ACTION_ARGUMENT_HANDLE*)realloc(action->ArgumentHandles, sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE) * (action->ArgumentCount + 1)); + if (newArguments == NULL) + { + /* Codes_SRS_SCHEMA_99_112: [On any other error, Schema_ AddModelActionArgumet shall return SCHEMA_ERROR.] */ + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* newActionArgument; + if ((newActionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)malloc(sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_SCHEMA_99_112: [On any other error, Schema_ AddModelActionArgumet shall return SCHEMA_ERROR.] */ + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + if (mallocAndStrcpy_s((char**)&newActionArgument->Name, argumentName) != 0) + { + /* Codes_SRS_SCHEMA_99_112: [On any other error, Schema_ AddModelActionArgumet shall return SCHEMA_ERROR.] */ + free(newActionArgument); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else if (mallocAndStrcpy_s((char**)&newActionArgument->Type, argumentType) != 0) + { + /* Codes_SRS_SCHEMA_99_112: [On any other error, Schema_ AddModelActionArgumet shall return SCHEMA_ERROR.] */ + free((void*)newActionArgument->Name); + free(newActionArgument); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + action->ArgumentHandles = newArguments; + /* Codes_SRS_SCHEMA_99_119: [Schema_AddModelActionArgument shall preserve the order of the action arguments according to the order in which they were added, starting with index 0 for the first added argument.] */ + action->ArgumentHandles[action->ArgumentCount] = newActionArgument; + action->ArgumentCount++; + + /* Codes_SRS_SCHEMA_99_108: [On success, Schema_AddModelActionArgunent shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + } + + /* If possible, reduce the memory of over allocation */ + if (result == SCHEMA_ERROR) + { + SCHEMA_ACTION_ARGUMENT_HANDLE* oldArguments = (SCHEMA_ACTION_ARGUMENT_HANDLE*)realloc(action->ArgumentHandles, sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE) * action->ArgumentCount); + if (oldArguments == NULL) + { + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + action->ArgumentHandles = oldArguments; + } + } + } + } + } + return result; +} + +static bool methodFindArgumentByBame(const void* element, const void* value) +{ + /*element is a pointer to SCHEMA_METHOD_ARGUMENT_HANDLE*/ + const SCHEMA_METHOD_ARGUMENT_HANDLE* decodedElement = (const SCHEMA_METHOD_ARGUMENT_HANDLE*)element; + const char* name = (const char*)value; + return (strcmp((*decodedElement)->Name, name) == 0); +} + +SCHEMA_RESULT Schema_AddModelMethodArgument(SCHEMA_METHOD_HANDLE methodHandle, const char* argumentName, const char* argumentType) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_02_105: [ If methodHandle is NULL then Schema_AddModelMethodArgument shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_106: [ If argumentName is NULL then Schema_AddModelMethodArgument shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_107: [ If argumentType is NULL then Schema_AddModelMethodArgument shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ((methodHandle == NULL) || + (argumentName == NULL) || + (argumentType == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /*Codes_SRS_SCHEMA_02_108: [ If argumentName already exists in the list of arguments then then Schema_AddModelMethodArgument shall fail and return SCHEMA_INVALID_ARG. ]*/ + if (VECTOR_find_if(methodHandle->methodArguments, methodFindArgumentByBame, argumentName) != NULL) + { + LogError("an argument with name %s already exists", argumentName); + result = SCHEMA_INVALID_ARG; + } + else + { + /*Codes_SRS_SCHEMA_02_109: [ Schema_AddModelMethodArgument shall allocate memory for the new argument. ]*/ + SCHEMA_METHOD_ARGUMENT_HANDLE_DATA* argument = malloc(sizeof(SCHEMA_METHOD_ARGUMENT_HANDLE_DATA)); + if (argument == NULL) + { + /*Codes_SRS_SCHEMA_02_113: [ If any of the above operations fails, then Schema_AddModelMethodArgument shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure to malloc"); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_110: [ Schema_AddModelMethodArgument shall clone methodHandle. ]*/ + if (mallocAndStrcpy_s(&argument->Name, argumentName) != 0) + { + /*Codes_SRS_SCHEMA_02_113: [ If any of the above operations fails, then Schema_AddModelMethodArgument shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in mallocAndStrcpy_s"); + free(argument); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_111: [ Schema_AddModelMethodArgument shall clone argumentType. ]*/ + if (mallocAndStrcpy_s(&argument->Type, argumentType) != 0) + { + /*Codes_SRS_SCHEMA_02_113: [ If any of the above operations fails, then Schema_AddModelMethodArgument shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in mallocAndStrcpy_s"); + free(argument->Name); + free(argument); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_112: [ Schema_AddModelMethodArgument shall add the created argument to the method's list of arguments. ]*/ + if (VECTOR_push_back(methodHandle->methodArguments, &argument, 1) != 0) + { + /*Codes_SRS_SCHEMA_02_113: [ If any of the above operations fails, then Schema_AddModelMethodArgument shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in VECTOR_push_back"); + free(argument->Name); + free(argument->Type); + free(argument); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_114: [ Otherwise, Schema_AddModelMethodArgument shall succeed and return SCHEMA_OK. ]*/ + result = SCHEMA_OK; + } + } + } + } + } + } + return result; +} + +SCHEMA_PROPERTY_HANDLE Schema_GetModelPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName) +{ + SCHEMA_PROPERTY_HANDLE result; + + /* Codes_SRS_SCHEMA_99_038:[Schema_GetModelPropertyByName shall return NULL if unable to find a matching property or if any of the arguments are NULL.] */ + if ((modelTypeHandle == NULL) || + (propertyName == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + size_t i; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_036:[Schema_GetModelPropertyByName shall return a non-NULL SCHEMA_PROPERTY_HANDLE corresponding to the model type identified by modelTypeHandle and matching the propertyName argument value.] */ + for (i = 0; i < modelType->PropertyCount; i++) + { + SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i]; + if (strcmp(modelProperty->PropertyName, propertyName) == 0) + { + break; + } + } + + if (i == modelType->PropertyCount) + { + /* Codes_SRS_SCHEMA_99_038:[Schema_GetModelPropertyByName shall return NULL if unable to find a matching property or if any of the arguments are NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND)); + } + else + { + result = (SCHEMA_PROPERTY_HANDLE)(modelType->Properties[i]); + } + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* propertyCount) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_092: [Schema_GetModelPropertyCount shall return SCHEMA_INVALID_ARG if any of the arguments is NULL.] */ + if ((modelTypeHandle == NULL) || + (propertyCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + } + else + { + /* Codes_SRS_SCHEMA_99_089: [Schema_GetModelPropertyCount shall provide the number of properties defined in the model type identified by modelTypeHandle.] */ + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_090: [The count shall be provided via the out argument propertyCount.]*/ + *propertyCount = modelType->PropertyCount; + + /* Codes_SRS_SCHEMA_99_091: [On success, Schema_GetModelPropertyCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelReportedPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* reportedPropertyCount) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_02_008: [ If parameter modelTypeHandle is NULL then Schema_GetModelReportedPropertyCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_009: [ If parameter reportedPropertyCount is NULL then Schema_GetModelReportedPropertyCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ( + (modelTypeHandle == NULL)|| + (reportedPropertyCount==NULL) + ) + { + LogError("SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t* reportedPropertyCount=%p", modelTypeHandle, reportedPropertyCount); + result = SCHEMA_INVALID_ARG; + } + else + { + /*Codes_SRS_SCHEMA_02_010: [ Schema_GetModelReportedPropertyCount shall provide in reportedPropertyCount the number of reported properties and return SCHEMA_OK. ]*/ + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + *reportedPropertyCount = VECTOR_size(modelType->reportedProperties); + result = SCHEMA_OK; + } + return result; +} + +SCHEMA_REPORTED_PROPERTY_HANDLE Schema_GetModelReportedPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyName) +{ + SCHEMA_REPORTED_PROPERTY_HANDLE result; + /*Codes_SRS_SCHEMA_02_011: [ If argument modelTypeHandle is NULL then Schema_GetModelReportedPropertyByName shall fail and return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_012: [ If argument reportedPropertyName is NULL then Schema_GetModelReportedPropertyByName shall fail and return NULL. ]*/ + if( + (modelTypeHandle == NULL) || + (reportedPropertyName == NULL) + ) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyName=%p", modelTypeHandle, reportedPropertyName); + result = NULL; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_013: [ If reported property by the name reportedPropertyName exists then Schema_GetModelReportedPropertyByName shall succeed and return a non-NULL value. ]*/ + /*Codes_SRS_SCHEMA_02_014: [ Otherwise Schema_GetModelReportedPropertyByName shall fail and return NULL. ]*/ + if((result = VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyName))==NULL) + { + LogError("a reported property with name \"%s\" does not exist", reportedPropertyName); + } + else + { + /*return as is*/ + } + } + return result; +} + +SCHEMA_REPORTED_PROPERTY_HANDLE Schema_GetModelReportedPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + SCHEMA_REPORTED_PROPERTY_HANDLE result; + /*Codes_SRS_SCHEMA_02_015: [ If argument modelTypeHandle is NULL then Schema_GetModelReportedPropertyByIndex shall fail and return NULL. ]*/ + if (modelTypeHandle == NULL) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t index=%lu", modelTypeHandle, index); + result = NULL; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /*Codes_SRS_SCHEMA_02_016: [ If a reported property with index equal to index exists then Schema_GetModelReportedPropertyByIndex shall succeed and return the non-NULL handle of that REPORTED_PROPERTY. ]*/ + /*Codes_SRS_SCHEMA_02_017: [ Otherwise Schema_GetModelReportedPropertyByIndex shall fail and return NULL. ]*/ + if ((result = VECTOR_element(modelType->reportedProperties, index)) == NULL) + { + LogError("index %lu is invalid", index); + } + else + { + /*return as is*/ + } + } + return result; +} + +SCHEMA_PROPERTY_HANDLE Schema_GetModelPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + SCHEMA_PROPERTY_HANDLE result; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_094: [Schema_GetModelProperty shall return NULL if the index specified is outside the valid range or if modelTypeHandle argument is NULL.] */ + if ((modelTypeHandle == NULL) || + (index >= modelType->PropertyCount)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Tests_SRS_SCHEMA_99_093: [Schema_GetModelProperty shall return a non-NULL SCHEMA_PROPERTY_HANDLE corresponding to the model type identified by modelTypeHandle and matching the index number provided by the index argument.] */ + /* Codes_SRS_SCHEMA_99_097: [index is zero based, and the order in which actions were added shall be the index in which they will be retrieved.] */ + result = modelType->Properties[index]; + } + + return result; +} + +SCHEMA_ACTION_HANDLE Schema_GetModelActionByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* actionName) +{ + SCHEMA_ACTION_HANDLE result; + + /* Codes_SRS_SCHEMA_99_041:[Schema_GetModelActionByName shall return NULL if unable to find a matching action, if any of the arguments are NULL.] */ + if ((modelTypeHandle == NULL) || + (actionName == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + size_t i; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_040:[Schema_GetModelActionByName shall return a non-NULL SCHEMA_ACTION_HANDLE corresponding to the model type identified by modelTypeHandle and matching the actionName argument value.] */ + for (i = 0; i < modelType->ActionCount; i++) + { + SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)modelType->Actions[i]; + if (strcmp(modelAction->ActionName, actionName) == 0) + { + break; + } + } + + if (i == modelType->ActionCount) + { + /* Codes_SRS_SCHEMA_99_041:[Schema_GetModelActionByName shall return NULL if unable to find a matching action, if any of the arguments are NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND)); + } + else + { + result = modelType->Actions[i]; + } + } + + return result; +} + +static bool matchModelMethod(const void* element, const void* value) +{ + /*element is a pointer to SCHEMA_METHOD_HANDLE_DATA*/ + const SCHEMA_METHOD_HANDLE* decodedElement = (const SCHEMA_METHOD_HANDLE* )element; + const char* name = (const char*)value; + return (strcmp((*decodedElement)->methodName, name) == 0); +} + +SCHEMA_METHOD_HANDLE Schema_GetModelMethodByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* methodName) +{ + SCHEMA_METHOD_HANDLE result; + + /*Codes_SRS_SCHEMA_02_115: [ If modelTypeHandle is NULL then Schema_GetModelMethodByName shall fail and return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_116: [ If methodName is NULL then Schema_GetModelMethodByName shall fail and return NULL. ]*/ + if ((modelTypeHandle == NULL) || + (methodName == NULL)) + { + result = NULL; + LogError("invalid arguments SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* methodName=%p", modelTypeHandle, methodName); + } + else + { + /*Codes_SRS_SCHEMA_02_117: [ If a method with the name methodName exists then Schema_GetModelMethodByName shall succeed and returns its handle. ]*/ + SCHEMA_METHOD_HANDLE* found = VECTOR_find_if(modelTypeHandle->methods, matchModelMethod, methodName); + if (found == NULL) + { + /*Codes_SRS_SCHEMA_02_118: [ Otherwise, Schema_GetModelMethodByName shall fail and return NULL. ]*/ + LogError("no such method by name = %s", methodName); + result = NULL; + } + else + { + result = *found; + } + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelActionCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* actionCount) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_045:[If any of the modelTypeHandle or actionCount arguments is NULL, Schema_GetModelActionCount shall return SCHEMA_INVALID_ARG.] */ + if ((modelTypeHandle == NULL) || + (actionCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result=%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMA_99_042:[Schema_GetModelActionCount shall provide the total number of actions defined in a model type identified by the modelTypeHandle.] */ + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_043:[The count shall be provided via the out argument actionCount.] */ + *actionCount = modelType->ActionCount; + + /* Codes_SRS_SCHEMA_99_044:[On success, Schema_GetModelActionCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + + return result; +} + +SCHEMA_ACTION_HANDLE Schema_GetModelActionByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + SCHEMA_ACTION_HANDLE result; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_048:[Schema_GetModelAction shall return NULL if the index specified is outside the valid range or if modelTypeHandle argument is NULL.] */ + if ((modelType == NULL) || + (index >= modelType->ActionCount)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_047:[Schema_GetModelAction shall return a non-NULL SCHEMA_ACTION_HANDLE corresponding to the model type identified by modelTypeHandle and matching the index number provided by the index argument.] */ + /* Codes_SRS_SCHEMA_99_096: [index is zero based and the order in which actions were added shall be the index in which they will be retrieved.] */ + result = modelType->Actions[index]; + } + + return result; +} + +const char* Schema_GetModelActionName(SCHEMA_ACTION_HANDLE actionHandle) +{ + const char* result; + + /* Codes_SRS_SCHEMA_99_050:[If the actionHandle is NULL, Schema_GetModelActionName shall return NULL.] */ + if (actionHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + /* Codes_SRS_SCHEMA_99_049:[Schema_GetModelActionName shall return the action name for a given action handle.] */ + result = action->ActionName; + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelActionArgumentCount(SCHEMA_ACTION_HANDLE actionHandle, size_t* argumentCount) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_054:[If any argument is NULL, Schema_GetModelActionArgumentCount shall return SCHEMA_INVALID_ARG.] */ + if ((actionHandle == NULL) || + (argumentCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result=%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + + /* Codes_SRS_SCHEMA_99_051:[Schema_GetModelActionArgumentCount shall provide the number of arguments for a specific schema action identified by actionHandle.] */ + /* Codes_SRS_SCHEMA_99_052:[The argument count shall be provided via the out argument argumentCount.] */ + *argumentCount = modelAction->ArgumentCount; + + /* Codes_SRS_SCHEMA_99_053:[On success, Schema_GetModelActionArgumentCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelMethodArgumentCount(SCHEMA_METHOD_HANDLE methodHandle, size_t* argumentCount) +{ + SCHEMA_RESULT result; + + /*Codes_SRS_SCHEMA_02_119: [ If methodHandle is NULL then Schema_GetModelMethodArgumentCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_120: [ If argumentCount is NULL then Schema_GetModelMethodArgumentCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ((methodHandle == NULL) || + (argumentCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result=%s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /*Codes_SRS_SCHEMA_02_121: [ Otherwise, Schema_GetModelMethodArgumentCount shall succeed, return in argumentCount the number of arguments for the method and return SCHEMA_OK. ]*/ + *argumentCount = VECTOR_size(methodHandle->methodArguments); + result = SCHEMA_OK; + } + + return result; +} + + +SCHEMA_ACTION_ARGUMENT_HANDLE Schema_GetModelActionArgumentByName(SCHEMA_ACTION_HANDLE actionHandle, const char* actionArgumentName) +{ + SCHEMA_ACTION_ARGUMENT_HANDLE result; + + /* Codes_SRS_SCHEMA_99_118: [Schema_GetModelActionArgumentByName shall return NULL if unable to find a matching argument or if any of the arguments are NULL.] */ + if ((actionHandle == NULL) || + (actionArgumentName == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_118: [Schema_GetModelActionArgumentByName shall return NULL if unable to find a matching argument or if any of the arguments are NULL.] */ + SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + size_t i; + + for (i = 0; i < modelAction->ArgumentCount; i++) + { + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)modelAction->ArgumentHandles[i]; + if (strcmp(actionArgument->Name, actionArgumentName) == 0) + { + break; + } + } + + if (i == modelAction->ArgumentCount) + { + /* Codes_SRS_SCHEMA_99_118: [Schema_GetModelActionArgumentByName shall return NULL if unable to find a matching argument or if any of the arguments are NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND)); + } + else + { + /* Codes_SRS_SCHEMA_99_117: [Schema_GetModelActionArgumentByName shall return a non-NULL handle corresponding to an action argument identified by the actionHandle and actionArgumentName.] */ + result = modelAction->ArgumentHandles[i]; + } + } + + return result; +} + +SCHEMA_ACTION_ARGUMENT_HANDLE Schema_GetModelActionArgumentByIndex(SCHEMA_ACTION_HANDLE actionHandle, size_t argumentIndex) +{ + SCHEMA_ACTION_ARGUMENT_HANDLE result; + SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle; + + /* Codes_SRS_SCHEMA_99_056:[Schema_GetModelActionArgument shall return NULL if the index specified is outside the valid range or if the actionHandle argument is NULL.] */ + if ((actionHandle == NULL) || + (argumentIndex >= modelAction->ArgumentCount)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_055:[Schema_GetModelActionArgument shall return a non-NULL SCHEMA_ACTION_ARGUMENT_HANDLE corresponding to the action type identified by actionHandle and matching the index number provided by the argumentIndex argument.] */ + result = modelAction->ArgumentHandles[argumentIndex]; + } + + return result; +} + +SCHEMA_METHOD_ARGUMENT_HANDLE Schema_GetModelMethodArgumentByIndex(SCHEMA_METHOD_HANDLE methodHandle, size_t argumentIndex) +{ + SCHEMA_METHOD_ARGUMENT_HANDLE result; + + /*Codes_SRS_SCHEMA_02_122: [ If methodHandle is NULL then Schema_GetModelMethodArgumentByIndex shall fail and return NULL. ]*/ + if (methodHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /*Codes_SRS_SCHEMA_02_123: [ If argumentIndex does not exist then Schema_GetModelMethodArgumentByIndex shall fail and return NULL. ]*/ + SCHEMA_METHOD_ARGUMENT_HANDLE *temp = VECTOR_element(methodHandle->methodArguments, argumentIndex); + if (temp == NULL) + { + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_124: [ Otherwise, Schema_GetModelMethodArgumentByIndex shall succeed and return a non-NULL value. ]*/ + result = *temp; + } + } + + return result; +} + + +const char* Schema_GetActionArgumentName(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle) +{ + const char* result; + /* Codes_SRS_SCHEMA_99_114: [Schema_GetActionArgumentName shall return NULL if actionArgumentHandle is NULL.] */ + if (actionArgumentHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_113: [Schema_GetActionArgumentName shall return the argument name identified by the actionArgumentHandle.] */ + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle; + result = actionArgument->Name; + } + return result; +} + +const char* Schema_GetMethodArgumentName(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle) +{ + const char* result; + /*Codes_SRS_SCHEMA_02_125: [ If methodArgumentHandle is NULL then Schema_GetMethodArgumentName shall fail and return NULL. ]*/ + if (methodArgumentHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /*Codes_SRS_SCHEMA_02_126: [ Otherwise, Schema_GetMethodArgumentName shall succeed and return a non-NULL value. ]*/ + result = methodArgumentHandle->Name; + } + return result; +} + + +const char* Schema_GetActionArgumentType(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle) +{ + const char* result; + /* Codes_SRS_SCHEMA_99_116: [Schema_GetActionArgumentType shall return NULL if actionArgumentHandle is NULL.] */ + if (actionArgumentHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_115: [Schema_GetActionArgumentType shall return the argument type identified by the actionArgumentHandle.] */ + SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle; + result = actionArgument->Type; + } + return result; +} + +const char* Schema_GetMethodArgumentType(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle) +{ + const char* result; + /*Codes_SRS_SCHEMA_02_127: [ If methodArgumentHandle is NULL then Schema_GetMethodArgumentType shall fail and return NULL. ]*/ + if (methodArgumentHandle == NULL) + { + result = NULL; + LogError("invalid argument SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle=%p", methodArgumentHandle); + } + else + { + /*Codes_SRS_SCHEMA_02_128: [ Otherwise, Schema_GetMethodArgumentType shall succeed and return a non-NULL value. ]*/ + result = methodArgumentHandle->Type; + } + return result; +} + + +SCHEMA_STRUCT_TYPE_HANDLE Schema_CreateStructType(SCHEMA_HANDLE schemaHandle, const char* typeName) +{ + SCHEMA_STRUCT_TYPE_HANDLE result; + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + + /* Codes_SRS_SCHEMA_99_060:[If any of the arguments is NULL, Schema_CreateStructType shall return NULL.] */ + if ((schema == NULL) || + (typeName == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType; + size_t i; + + /* Codes_SRS_SCHEMA_99_061:[If a struct type with the same name already exists, Schema_CreateStructType shall return NULL.] */ + for (i = 0; i < schema->StructTypeCount; i++) + { + structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)schema->StructTypes[i]; + if (strcmp(structType->Name, typeName) == 0) + { + break; + } + } + + if (i < schema->StructTypeCount) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_DUPLICATE_ELEMENT)); + } + else + { + SCHEMA_STRUCT_TYPE_HANDLE* newStructTypes = (SCHEMA_STRUCT_TYPE_HANDLE*)realloc(schema->StructTypes, sizeof(SCHEMA_STRUCT_TYPE_HANDLE) * (schema->StructTypeCount + 1)); + if (newStructTypes == NULL) + { + /* Codes_SRS_SCHEMA_99_066:[On any other error, Schema_CreateStructType shall return NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + schema->StructTypes = newStructTypes; + if ((structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)malloc(sizeof(SCHEMA_STRUCT_TYPE_HANDLE_DATA))) == NULL) + { + /* Codes_SRS_SCHEMA_99_066:[On any other error, Schema_CreateStructType shall return NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else if (mallocAndStrcpy_s((char**)&structType->Name, typeName) != 0) + { + /* Codes_SRS_SCHEMA_99_066:[On any other error, Schema_CreateStructType shall return NULL.] */ + result = NULL; + free(structType); + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + /* Codes_SRS_SCHEMA_99_057:[Schema_CreateStructType shall create a new struct type and return a handle to it.] */ + schema->StructTypes[schema->StructTypeCount] = structType; + schema->StructTypeCount++; + structType->PropertyCount = 0; + structType->Properties = NULL; + + /* Codes_SRS_SCHEMA_99_058:[On success, a non-NULL handle shall be returned.] */ + result = (SCHEMA_STRUCT_TYPE_HANDLE)structType; + } + + /* If possible, reduce the memory of over allocation */ + if (result == NULL) + { + SCHEMA_STRUCT_TYPE_HANDLE* oldStructTypes = (SCHEMA_STRUCT_TYPE_HANDLE*)realloc(schema->StructTypes, sizeof(SCHEMA_STRUCT_TYPE_HANDLE) * schema->StructTypeCount); + if (oldStructTypes == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR)); + } + else + { + schema->StructTypes = oldStructTypes; + } + } + } + } + } + + return result; +} + +const char* Schema_GetStructTypeName(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle) +{ + const char* result; + + /* Codes_SRS_SCHEMA_99_136: [If structTypeHandle is NULL, Schema_GetStructTypeName shall return NULL.] */ + if (structTypeHandle == NULL) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_135: [Schema_GetStructTypeName shall return the name of a struct type identified by the structTypeHandle argument.] */ + result = ((SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle)->Name; + } + + return result; +} + +SCHEMA_RESULT Schema_GetStructTypeCount(SCHEMA_HANDLE schemaHandle, size_t* structTypeCount) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_140: [Schema_GetStructTypeCount shall return SCHEMA_INVALID_ARG if any of the arguments is NULL.] */ + if ((schemaHandle == NULL) || + (structTypeCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMA_99_137: [Schema_GetStructTypeCount shall provide the number of structs defined in the schema identified by schemaHandle.] */ + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + /* Codes_SRS_SCHEMA_99_138: [The count shall be provided via the out argument structTypeCount.] */ + *structTypeCount = schema->StructTypeCount; + /* Codes_SRS_SCHEMA_99_139: [On success, Schema_GetStructTypeCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + + return result; +} + +SCHEMA_STRUCT_TYPE_HANDLE Schema_GetStructTypeByName(SCHEMA_HANDLE schemaHandle, const char* name) +{ + SCHEMA_STRUCT_TYPE_HANDLE result; + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + + /* Codes_SRS_SCHEMA_99_069:[Schema_GetStructTypeByName shall return NULL if unable to find a matching struct or if any of the arguments are NULL.] */ + if ((schemaHandle == NULL) || + (name == NULL)) + { + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + size_t i; + + /* Codes_SRS_SCHEMA_99_068:[Schema_GetStructTypeByName shall return a non-NULL handle corresponding to the struct type identified by the structTypeName in the schemaHandle schema.] */ + for (i = 0; i < schema->StructTypeCount; i++) + { + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)schema->StructTypes[i]; + if (strcmp(structType->Name, name) == 0) + { + break; + } + } + + if (i == schema->StructTypeCount) + { + /* Codes_SRS_SCHEMA_99_069:[Schema_GetStructTypeByName shall return NULL if unable to find a matching struct or if any of the arguments are NULL.] */ + result = NULL; + LogError("(Error code:%s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND)); + } + else + { + result = schema->StructTypes[i]; + } + } + + return result; +} + +SCHEMA_STRUCT_TYPE_HANDLE Schema_GetStructTypeByIndex(SCHEMA_HANDLE schemaHandle, size_t index) +{ + SCHEMA_STRUCT_TYPE_HANDLE result; + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + + /* Codes_SRS_SCHEMA_99_143: [Schema_GetStructTypeByIndex shall return NULL if the index specified is outside the valid range or if schemaHandle argument is NULL.] */ + /* Codes_SRS_SCHEMA_99_142: [The index argument is zero based, and the order in which models were added shall be the index in which they will be retrieved.] */ + if ((schemaHandle == NULL) || + (index >= schema->StructTypeCount)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + + /* Codes_SRS_SCHEMA_99_141: [Schema_GetStructTypeByIndex shall return a non-NULL SCHEMA_STRUCT_TYPE_HANDLE corresponding to the struct type identified by schemaHandle and matching the index number provided by the index argument.] */ + /* Codes_SRS_SCHEMA_99_142: [The index argument is zero based, and the order in which models were added shall be the index in which they will be retrieved.] */ + result = schema->StructTypes[index]; + } + return result; +} + +SCHEMA_RESULT Schema_AddStructTypeProperty(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, const char* propertyName, const char* propertyType) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_072:[If any of the arguments is NULL, Schema_AddStructTypeProperty shall return SCHEMA_INVALID_ARG.] */ + if ((structTypeHandle == NULL) || + (propertyName == NULL) || + (propertyType == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + size_t i; + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle; + + /* Codes_SRS_SCHEMA_99_074:[The property name shall be unique per struct type, if the same property name is added twice to a struct type, SCHEMA_DUPLICATE_ELEMENT shall be returned.] */ + for (i = 0; i < structType->PropertyCount; i++) + { + SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)structType->Properties[i]; + if (strcmp(property->PropertyName, propertyName) == 0) + { + break; + } + } + + if (i < structType->PropertyCount) + { + result = SCHEMA_DUPLICATE_ELEMENT; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_PROPERTY_HANDLE* newProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(structType->Properties, sizeof(SCHEMA_PROPERTY_HANDLE) * (structType->PropertyCount + 1)); + if (newProperties == NULL) + { + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_PROPERTY_HANDLE_DATA* newProperty; + + structType->Properties = newProperties; + if ((newProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_PROPERTY_HANDLE_DATA))) == NULL) + { + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + if (mallocAndStrcpy_s((char**)&newProperty->PropertyName, propertyName) != 0) + { + free(newProperty); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else if (mallocAndStrcpy_s((char**)&newProperty->PropertyType, propertyType) != 0) + { + free((void*)newProperty->PropertyName); + free(newProperty); + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMA_99_070:[Schema_AddStructTypeProperty shall add one property to the struct type identified by structTypeHandle.] */ + structType->Properties[structType->PropertyCount] = (SCHEMA_PROPERTY_HANDLE)newProperty; + structType->PropertyCount++; + + /* Codes_SRS_SCHEMA_99_071:[On success, Schema_AddStructTypeProperty shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + } + + /* If possible, reduce the memory of over allocation */ + if (result != SCHEMA_OK) + { + SCHEMA_PROPERTY_HANDLE* oldProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(structType->Properties, sizeof(SCHEMA_PROPERTY_HANDLE) * structType->PropertyCount); + if (oldProperties == NULL) + { + result = SCHEMA_ERROR; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + structType->Properties = oldProperties; + } + } + } + } + } + + return result; +} + +SCHEMA_PROPERTY_HANDLE Schema_GetStructTypePropertyByName(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, const char* propertyName) +{ + SCHEMA_PROPERTY_HANDLE result; + + /* Codes_SRS_SCHEMA_99_076:[Schema_GetStructTypePropertyByName shall return NULL if unable to find a matching property or if any of the arguments are NULL.] */ + if ((structTypeHandle == NULL) || + (propertyName == NULL)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + size_t i; + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle; + + for (i = 0; i < structType->PropertyCount; i++) + { + SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)structType->Properties[i]; + if (strcmp(modelProperty->PropertyName, propertyName) == 0) + { + break; + } + } + + /* Codes_SRS_SCHEMA_99_076:[Schema_GetStructTypePropertyByName shall return NULL if unable to find a matching property or if any of the arguments are NULL.] */ + if (i == structType->PropertyCount) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND)); + } + /* Codes_SRS_SCHEMA_99_075:[Schema_GetStructTypePropertyByName shall return a non-NULL handle corresponding to a property identified by the structTypeHandle and propertyName.] */ + else + { + result = structType->Properties[i]; + } + } + + return result; +} + +SCHEMA_RESULT Schema_GetStructTypePropertyCount(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, size_t* propertyCount) +{ + SCHEMA_RESULT result; + + /* Codes_SRS_SCHEMA_99_079: [Schema_GetStructTypePropertyCount shall return SCHEMA_INVALID_ARG if any of the structlTypeHandle or propertyCount arguments is NULL.] */ + if ((structTypeHandle == NULL) || + (propertyCount == NULL)) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle; + + /* Codes_SRS_SCHEMA_99_077: [Schema_GetStructTypePropertyCount shall provide the total number of properties defined in a struct type identified by structTypeHandle. The value is provided via the out argument propertyCount.] */ + /* Codes_SRS_SCHEMA_99_081: [The count shall be provided via the out argument propertyCount.] */ + *propertyCount = structType->PropertyCount; + + /* Codes_SRS_SCHEMA_99_078: [On success, Schema_ GetStructTypePropertyCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + + return result; +} + +SCHEMA_PROPERTY_HANDLE Schema_GetStructTypePropertyByIndex(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, size_t index) +{ + SCHEMA_PROPERTY_HANDLE result; + SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle; + + /* Codes_SRS_SCHEMA_99_083: [Schema_ GetStructTypeProperty shall return NULL if the index specified is outside the valid range, if structTypeHandle argument is NULL] */ + if ((structTypeHandle == NULL) || + (index >= structType->PropertyCount)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_082: [Schema_GetStructTypeProperty shall return a non-NULL SCHEMA_PROPERTY_HANDLE corresponding to the struct type identified by strutTypeHandle and matching the index number provided by the index argument.] */ + /* Codes_SRS_SCHEMA_99_098: [index is zero based and the order in which actions were added shall be the index in which they will be retrieved.] */ + result = structType->Properties[index]; + } + + return result; +} + +const char* Schema_GetPropertyName(SCHEMA_PROPERTY_HANDLE propertyHandle) +{ + const char* result; + + /* Codes_SRS_SCHEMA_99_086: [If propertyHandle is NULL, Schema_GetPropertyName shall return NULL.] */ + if (propertyHandle == NULL) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_PROPERTY_HANDLE_DATA* propertyType = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle; + + /* Codes_SRS_SCHEMA_99_085: [Schema_GetPropertyName shall return the property name identified by the propertyHandle.] */ + result = propertyType->PropertyName; + } + + return result; +} + +const char* Schema_GetPropertyType(SCHEMA_PROPERTY_HANDLE propertyHandle) +{ + const char* result; + + /* Codes_SRS_SCHEMA_99_088: [If propertyHandle is NULL, Schema_GetPropertyType shall return NULL.] */ + if (propertyHandle == NULL) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle; + + /* Codes_SRS_SCHEMA_99_087: [Schema_GetPropertyType shall return the property type identified by the propertyHandle.] */ + result = modelProperty->PropertyType; + } + + return result; +} + +SCHEMA_RESULT Schema_GetModelCount(SCHEMA_HANDLE schemaHandle, size_t* modelCount) +{ + SCHEMA_RESULT result; + /* Codes_SRS_SCHEMA_99_123: [Schema_GetModelCount shall return SCHEMA_INVALID_ARG if any of the arguments is NULL.] */ + if ((schemaHandle == NULL) || + (modelCount == NULL) ) + { + result = SCHEMA_INVALID_ARG; + LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMA_99_120: [Schema_GetModelCount shall provide the number of models defined in the schema identified by schemaHandle.] */ + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + /* Codes_SRS_SCHEMA_99_121: [The count shall be provided via the out argument modelCount.] */ + *modelCount = schema->ModelTypeCount; + /* Codes_SRS_SCHEMA_99_122: [On success, Schema_GetModelCount shall return SCHEMA_OK.] */ + result = SCHEMA_OK; + } + return result; +} + +SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelByName(SCHEMA_HANDLE schemaHandle, const char* modelName) +{ + SCHEMA_MODEL_TYPE_HANDLE result; + + /* Codes_SRS_SCHEMA_99_125: [Schema_GetModelByName shall return NULL if unable to find a matching model, or if any of the arguments are NULL.] */ + if ((schemaHandle == NULL) || + (modelName == NULL)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + /* Codes_SRS_SCHEMA_99_124: [Schema_GetModelByName shall return a non-NULL SCHEMA_MODEL_TYPE_HANDLE corresponding to the model identified by schemaHandle and matching the modelName argument value.] */ + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + size_t i; + for (i = 0; i < schema->ModelTypeCount; i++) + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)schema->ModelTypes[i]; + if (strcmp(modelName, modelType->Name)==0) + { + break; + } + } + if (i == schema->ModelTypeCount) + { + /* Codes_SRS_SCHEMA_99_125: [Schema_GetModelByName shall return NULL if unable to find a matching model, or if any of the arguments are NULL.] */ + result = NULL; + } + else + { + result = schema->ModelTypes[i]; + } + } + return result; +} + +SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelByIndex(SCHEMA_HANDLE schemaHandle, size_t index) +{ + SCHEMA_MODEL_TYPE_HANDLE result; + SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle; + + /* Codes_SRS_SCHEMA_99_128: [Schema_GetModelByIndex shall return NULL if the index specified is outside the valid range or if schemaHandle argument is NULL.] */ + /* Codes_SRS_SCHEMA_99_127: [The index argument is zero based, and the order in which models were added shall be the index in which they will be retrieved.] */ + if ((schemaHandle == NULL) || + (index >= schema->ModelTypeCount)) + { + result = NULL; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + + /* Codes_SRS_SCHEMA_99_126: [Schema_GetModelByIndex shall return a non-NULL SCHEMA_MODEL_TYPE_HANDLE corresponding to the model identified by schemaHandle and matching the index number provided by the index argument.] */ + /* Codes_SRS_SCHEMA_99_127: [The index argument is zero based, and the order in which models were added shall be the index in which they will be retrieved.] */ + result = schema->ModelTypes[index]; + } + return result; +} + +/*Codes_SRS_SCHEMA_99_160: [Schema_GetModelName shall return the name of the model identified by modelTypeHandle. If the name cannot be retrieved, then NULL shall be returned.]*/ +const char* Schema_GetModelName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle) +{ + const char* result; + if (modelTypeHandle == NULL) + { + result = NULL; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + result = modelType->Name; + } + return result; +} + +/*Codes_SRS_SCHEMA_99_163: [Schema_AddModelModel shall insert an existing model, identified by the handle modelType, into the existing model identified by modelTypeHandle under a property having the name propertyName.]*/ +SCHEMA_RESULT Schema_AddModelModel(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName, SCHEMA_MODEL_TYPE_HANDLE modelType, size_t offset, pfOnDesiredProperty onDesiredProperty) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_99_165: [If any of the parameters is NULL then Schema_AddModelModel shall return SCHEMA_INVALID_ARG.]*/ + if ( + (modelTypeHandle == NULL) || + (propertyName == NULL) || + (modelType == NULL) + ) + { + result = SCHEMA_INVALID_ARG; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* parentModel = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + MODEL_IN_MODEL temp; + temp.modelHandle = modelType; + temp.offset = offset; + temp.onDesiredProperty = onDesiredProperty; + if (mallocAndStrcpy_s((char**)&(temp.propertyName), propertyName) != 0) + { + result = SCHEMA_ERROR; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else if (VECTOR_push_back(parentModel->models, &temp, 1) != 0) + { + /*Codes_SRS_SCHEMA_99_174: [The function shall return SCHEMA_ERROR if any other error occurs.]*/ + free((void*)temp.propertyName); + result = SCHEMA_ERROR; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + /*Codes_SRS_SCHEMA_99_164: [If the function succeeds, then the return value shall be SCHEMA_OK.]*/ + + result = SCHEMA_OK; + } + } + return result; +} + + +SCHEMA_RESULT Schema_GetModelModelCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* modelCount) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_99_169: [If any of the parameters is NULL, then the function shall return SCHEMA_INVALID_ARG.]*/ + if ( + (modelTypeHandle == NULL) || + (modelCount == NULL) + ) + { + result = SCHEMA_INVALID_ARG; + LogError("(Error code: %s)", ENUM_TO_STRING(SCHEMA_RESULT, result)); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_99_167: [Schema_GetModelModelCount shall return in parameter modelCount the number of models inserted in the model identified by parameter modelTypeHandle.]*/ + *modelCount = VECTOR_size(model->models); + /*SRS_SCHEMA_99_168: [If the function succeeds, it shall return SCHEMA_OK.]*/ + result = SCHEMA_OK; + } + return result; +} + +static bool matchModelName(const void* element, const void* value) +{ + MODEL_IN_MODEL* decodedElement = (MODEL_IN_MODEL*)element; + const char* name = (const char*)value; + return (strcmp(decodedElement->propertyName, name) == 0); +} + +SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelModelByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName) +{ + SCHEMA_MODEL_TYPE_HANDLE result; + if ( + (modelTypeHandle == NULL) || + (propertyName == NULL) + ) + { + /*Codes_SRS_SCHEMA_99_171: [If Schema_GetModelModelByName is unable to provide the handle it shall return NULL.]*/ + result = NULL; + LogError("error SCHEMA_INVALID_ARG"); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_99_170: [Schema_GetModelModelByName shall return a handle to the model identified by the property with the name propertyName in the model identified by the handle modelTypeHandle.]*/ + /*Codes_SRS_SCHEMA_99_171: [If Schema_GetModelModelByName is unable to provide the handle it shall return NULL.]*/ + void* temp = VECTOR_find_if(model->models, matchModelName, propertyName); + if (temp == NULL) + { + LogError("specified propertyName not found (%s)", propertyName); + result = NULL; + } + else + { + result = ((MODEL_IN_MODEL*)temp)->modelHandle; + } + } + return result; +} + +size_t Schema_GetModelModelByName_Offset(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName) +{ + size_t result; + /*Codes_SRS_SCHEMA_02_053: [ If modelTypeHandle is NULL then Schema_GetModelModelByName_Offset shall fail and return 0. ]*/ + /*Codes_SRS_SCHEMA_02_054: [ If propertyName is NULL then Schema_GetModelModelByName_Offset shall fail and return 0. ]*/ + if ( + (modelTypeHandle == NULL) || + (propertyName == NULL) + ) + { + /*Codes_SRS_SCHEMA_99_171: [If Schema_GetModelModelByName is unable to provide the handle it shall return NULL.]*/ + result = 0; + LogError("error SCHEMA_INVALID_ARG"); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_056: [ If propertyName is not a model then Schema_GetModelModelByName_Offset shall fail and return 0. ]*/ + void* temp = VECTOR_find_if(model->models, matchModelName, propertyName); + if (temp == NULL) + { + LogError("specified propertyName not found (%s)", propertyName); + result = 0; + } + else + { + /*Codes_SRS_SCHEMA_02_055: [ Otherwise Schema_GetModelModelByName_Offset shall succeed and return the offset. ]*/ + result = ((MODEL_IN_MODEL*)temp)->offset; + } + } + return result; +} + +pfOnDesiredProperty Schema_GetModelModelByName_OnDesiredProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName) +{ + pfOnDesiredProperty result; + /*Codes_SRS_SCHEMA_02_086: [ If modelTypeHandle is NULL then Schema_GetModelModelByName_OnDesiredProperty shall return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_087: [ If propertyName is NULL then Schema_GetModelModelByName_OnDesiredProperty shall return NULL. ]*/ + if ( + (modelTypeHandle == NULL) || + (propertyName == NULL) + ) + { + result = NULL; + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* propertyName=%p",modelTypeHandle, propertyName); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + void* temp = VECTOR_find_if(model->models, matchModelName, propertyName); + if (temp == NULL) + { + LogError("specified propertyName not found (%s)", propertyName); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_089: [ Otherwise Schema_GetModelModelByName_OnDesiredProperty shall return the desired property callback. ]*/ + result = ((MODEL_IN_MODEL*)temp)->onDesiredProperty; + } + } + return result; + +} + +size_t Schema_GetModelModelByIndex_Offset(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + size_t result; + /*Codes_SRS_SCHEMA_02_057: [ If modelTypeHandle is NULL then Schema_GetModelModelByIndex_Offset shall fail and return 0. ]*/ + if (modelTypeHandle == NULL) + { + result = 0; + LogError("error SCHEMA_INVALID_ARG"); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_058: [ If index is not valid then Schema_GetModelModelByIndex_Offset shall fail and return 0. ]*/ + void* temp = VECTOR_element(model->models, index); + if (temp == 0) + { + LogError("specified index [out of bounds] (%lu)", index); + result = 0; + } + else + { + /*Codes_SRS_SCHEMA_02_059: [ Otherwise Schema_GetModelModelByIndex_Offset shall succeed and return the offset. ]*/ + result = ((MODEL_IN_MODEL*)temp)->offset; + } + } + return result; +} + +SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelModelyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + SCHEMA_MODEL_TYPE_HANDLE result; + if (modelTypeHandle == NULL) + { + /*Codes_SRS_SCHEMA_99_173: [Schema_GetModelModelyByIndex shall return NULL in the cases when it cannot provide the handle.]*/ + result = NULL; + LogError("error SCHEMA_INVALID_ARG"); + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + size_t nModelsInModel; + /*Codes_SRS_SCHEMA_99_172: [ Schema_GetModelModelyByIndex shall return a handle to the "index"th model inserted in the model identified by the parameter modelTypeHandle.]*/ + /*Codes_SRS_SCHEMA_99_173: [Schema_GetModelModelyByIndex shall return NULL in the cases when it cannot provide the handle.]*/ + nModelsInModel = VECTOR_size(model->models); + if (index < nModelsInModel) + { + result = ((MODEL_IN_MODEL*)VECTOR_element(model->models, index))->modelHandle; + } + else + { + LogError("attempted out of bounds access in array."); + result = NULL; /*out of bounds array access*/ + } + } + return result; +} + +const char* Schema_GetModelModelPropertyNameByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + const char* result; + if (modelTypeHandle == NULL) + { + /*Codes_SRS_SCHEMA_99_176: [If Schema_GetModelModelPropertyNameByIndex cannot produce the property name, it shall return NULL.]*/ + result = NULL; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + size_t nModelsInModel; + /*Codes_SRS_SCHEMA_99_175: [Schema_GetModelModelPropertyNameByIndex shall return the name of the property for the "index"th model in the model identified by modelTypeHandle parameter.]*/ + /*Codes_SRS_SCHEMA_99_176: [If Schema_GetModelModelPropertyNameByIndex cannot produce the property name, it shall return NULL.]*/ + nModelsInModel = VECTOR_size(model->models); + if (index < nModelsInModel) + { + result = ((MODEL_IN_MODEL*)VECTOR_element(model->models, index))->propertyName; + } + else + { + LogError("attempted out of bounds access in array."); + result = NULL; /*out of bounds array access*/ + } + } + return result; +} + +bool Schema_ModelPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyPath) +{ + bool result; + + /* Codes_SRS_SCHEMA_99_180: [If any of the arguments are NULL, Schema_ModelPropertyByPathExists shall return false.] */ + if ((modelTypeHandle == NULL) || + (propertyPath == NULL)) + { + LogError("error SCHEMA_INVALID_ARG"); + result = false; + } + else + { + const char* slashPos; + result = false; + + /* Codes_SRS_SCHEMA_99_182: [A single slash ('/') at the beginning of the path shall be ignored and the path shall still be valid.] */ + if (*propertyPath == '/') + { + propertyPath++; + } + + do + { + const char* endPos; + size_t i; + size_t modelCount; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + /* Codes_SRS_SCHEMA_99_179: [The propertyPath shall be assumed to be in the format model1/model2/.../propertyName.] */ + slashPos = strchr(propertyPath, '/'); + endPos = slashPos; + if (endPos == NULL) + { + endPos = &propertyPath[strlen(propertyPath)]; + } + + /* get the child-model */ + modelCount = VECTOR_size(modelType->models); + for (i = 0; i < modelCount; i++) + { + MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i); + if ((strncmp(childModel->propertyName, propertyPath, endPos - propertyPath) == 0) && + (strlen(childModel->propertyName) == (size_t)(endPos - propertyPath))) + { + /* found */ + modelTypeHandle = childModel->modelHandle; + break; + } + } + + if (i < modelCount) + { + /* model found, check if there is more in the path */ + if (slashPos == NULL) + { + /* this is the last one, so this is the thing we were looking for */ + result = true; + break; + } + else + { + /* continue looking, there's more */ + propertyPath = slashPos + 1; + } + } + else + { + /* no model found, let's see if this is a property */ + /* Codes_SRS_SCHEMA_99_178: [The argument propertyPath shall be used to find the leaf property.] */ + for (i = 0; i < modelType->PropertyCount; i++) + { + SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i]; + if ((strncmp(property->PropertyName, propertyPath, endPos - propertyPath) == 0) && + (strlen(property->PropertyName) == (size_t)(endPos - propertyPath))) + { + /* found property */ + /* Codes_SRS_SCHEMA_99_177: [Schema_ModelPropertyByPathExists shall return true if a leaf property exists in the model modelTypeHandle.] */ + result = true; + break; + } + } + + break; + } + } while (slashPos != NULL); + } + + return result; +} + +bool Schema_ModelReportedPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyPath) +{ + bool result; + + /*Codes_SRS_SCHEMA_02_018: [ If argument modelTypeHandle is NULL then Schema_ModelReportedPropertyByPathExists shall fail and return false. ]*/ + /*Codes_SRS_SCHEMA_02_019: [ If argument reportedPropertyPath is NULL then Schema_ModelReportedPropertyByPathExists shall fail and return false. ]*/ + if ((modelTypeHandle == NULL) || + (reportedPropertyPath == NULL)) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyPath=%s", modelTypeHandle, reportedPropertyPath); + result = false; + } + else + { + const char* slashPos; + + /*Codes_SRS_SCHEMA_02_021: [ If the reported property cannot be found Schema_ModelReportedPropertyByPathExists shall fail and return false. ]*/ + result = false; + + /*Codes_SRS_SCHEMA_02_020: [ reportedPropertyPath shall be assumed to be in the format model1/model2/.../reportedPropertyName. ]*/ + if (*reportedPropertyPath == '/') + { + reportedPropertyPath++; + } + + do + { + const char* endPos; + size_t i; + size_t modelCount; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + slashPos = strchr(reportedPropertyPath, '/'); + endPos = slashPos; + if (endPos == NULL) + { + endPos = &reportedPropertyPath[strlen(reportedPropertyPath)]; + } + + modelCount = VECTOR_size(modelType->models); + for (i = 0; i < modelCount; i++) + { + MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i); + if ((strncmp(childModel->propertyName, reportedPropertyPath, endPos - reportedPropertyPath) == 0) && + (strlen(childModel->propertyName) == (size_t)(endPos - reportedPropertyPath))) + { + /* found */ + modelTypeHandle = childModel->modelHandle; + break; + } + } + + if (i < modelCount) + { + /* model found, check if there is more in the path */ + if (slashPos == NULL) + { + /*Codes_SRS_SCHEMA_02_022: [ If the path reportedPropertyPath points to a sub-model, Schema_ModelReportedPropertyByPathExists shall succeed and true. ]*/ + /* this is the last one, so this is the thing we were looking for */ + result = true; + break; + } + else + { + /* continue looking, there's more */ + reportedPropertyPath = slashPos + 1; + } + } + else + { + /* no model found, let's see if this is a property */ + result = (VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyPath) != NULL); + if (!result) + { + LogError("no such reported property \"%s\"", reportedPropertyPath); + } + break; + } + } while (slashPos != NULL); + } + + return result; +} + +static bool desiredPropertyExists(const void* element, const void* value) +{ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = *(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA**)element; + return (strcmp(desiredProperty->desiredPropertyName, value) == 0); +} + +SCHEMA_RESULT Schema_AddModelDesiredProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyName, const char* desiredPropertyType, pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE, pfDesiredPropertyInitialize desiredPropertyInitialize, pfDesiredPropertyDeinitialize desiredPropertyDeinitialize, size_t offset, pfOnDesiredProperty onDesiredProperty) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_02_024: [ If modelTypeHandle is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_025: [ If desiredPropertyName is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_026: [ If desiredPropertyType is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_048: [ If desiredPropertyFromAGENT_DATA_TYPE is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_049: [ If desiredPropertyInitialize is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_050: [ If desiredPropertyDeinitialize is NULL then Schema_AddModelDesiredProperty shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ( + (modelTypeHandle == NULL) || + (desiredPropertyName == NULL) || + (desiredPropertyType == NULL) || + (desiredPropertyFromAGENT_DATA_TYPE == NULL) || + (desiredPropertyInitialize == NULL) || + (desiredPropertyDeinitialize== NULL) + ) + { + LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyName=%p, const char* desiredPropertyType=%p, pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE=%p, pfDesiredPropertyInitialize desiredPropertyInitialize=%p, pfDesiredPropertyDeinitialize desiredPropertyDeinitialize=%p, size_t offset=%lu", + modelTypeHandle, desiredPropertyName, desiredPropertyType, desiredPropertyFromAGENT_DATA_TYPE, desiredPropertyInitialize, desiredPropertyDeinitialize, offset); + result = SCHEMA_INVALID_ARG; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_027: [ Schema_AddModelDesiredProperty shall add the desired property given by the name desiredPropertyName and the type desiredPropertyType to the collection of existing desired properties. ]*/ + if (VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, desiredPropertyName) != NULL) + { + /*Codes_SRS_SCHEMA_02_047: [ If the desired property already exists, then Schema_AddModelDesiredProperty shall fail and return SCHEMA_DUPLICATE_ELEMENT. ]*/ + LogError("unable to Schema_AddModelDesiredProperty because a desired property with the same name (%s) already exists", desiredPropertyName); + result = SCHEMA_DUPLICATE_ELEMENT; + } + else + { + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA)); + if (desiredProperty == NULL) + { + /*Codes_SRS_SCHEMA_02_028: [ If any failure occurs then Schema_AddModelDesiredProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in malloc"); + result = SCHEMA_ERROR; + } + else + { + if (mallocAndStrcpy_s((char**)&desiredProperty->desiredPropertyName, desiredPropertyName) != 0) + { + /*Codes_SRS_SCHEMA_02_028: [ If any failure occurs then Schema_AddModelDesiredProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in mallocAndStrcpy_s"); + free(desiredProperty); + result = SCHEMA_ERROR; + } + else + { + if (mallocAndStrcpy_s((char**)&desiredProperty->desiredPropertyType, desiredPropertyType) != 0) + { + /*Codes_SRS_SCHEMA_02_028: [ If any failure occurs then Schema_AddModelDesiredProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in mallocAndStrcpy_s"); + free((void*)desiredProperty->desiredPropertyName); + free(desiredProperty); + result = SCHEMA_ERROR; + } + else + { + if (VECTOR_push_back(handleData->desiredProperties, &desiredProperty, 1) != 0) + { + /*Codes_SRS_SCHEMA_02_028: [ If any failure occurs then Schema_AddModelDesiredProperty shall fail and return SCHEMA_ERROR. ]*/ + LogError("failure in VECTOR_push_back"); + free((void*)desiredProperty->desiredPropertyType); + free((void*)desiredProperty->desiredPropertyName); + free(desiredProperty); + result = SCHEMA_ERROR; + } + else + { + /*Codes_SRS_SCHEMA_02_029: [ Otherwise, Schema_AddModelDesiredProperty shall succeed and return SCHEMA_OK. ]*/ + desiredProperty->desiredPropertyFromAGENT_DATA_TYPE = desiredPropertyFromAGENT_DATA_TYPE; + desiredProperty->desiredPropertInitialize = desiredPropertyInitialize; + desiredProperty->desiredPropertDeinitialize = desiredPropertyDeinitialize; + desiredProperty->onDesiredProperty = onDesiredProperty; /*NULL is a perfectly fine value*/ + desiredProperty->offset = offset; + result = SCHEMA_OK; + } + } + } + } + } + } + return result; +} + + +SCHEMA_RESULT Schema_GetModelDesiredPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* desiredPropertyCount) +{ + SCHEMA_RESULT result; + /*Codes_SRS_SCHEMA_02_030: [ If modelTypeHandle is NULL then Schema_GetModelDesiredPropertyCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_031: [ If desiredPropertyCount is NULL then Schema_GetModelDesiredPropertyCount shall fail and return SCHEMA_INVALID_ARG. ]*/ + if ( + (modelTypeHandle == NULL) || + (desiredPropertyCount == NULL) + ) + { + LogError("invalid arg: SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t* desiredPropertyCount=%p", modelTypeHandle, desiredPropertyCount); + result = SCHEMA_INVALID_ARG; + } + else + { + /*Codes_SRS_SCHEMA_02_032: [ Otherwise, Schema_GetModelDesiredPropertyCount shall succeed and write in desiredPropertyCount the existing number of desired properties. ]*/ + SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + *desiredPropertyCount = VECTOR_size(handleData->desiredProperties); + result = SCHEMA_OK; + } + return result; +} + +SCHEMA_DESIRED_PROPERTY_HANDLE Schema_GetModelDesiredPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyName) +{ + SCHEMA_DESIRED_PROPERTY_HANDLE result; + /*Codes_SRS_SCHEMA_02_034: [ If modelTypeHandle is NULL then Schema_GetModelDesiredPropertyByName shall fail and return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_035: [ If desiredPropertyName is NULL then Schema_GetModelDesiredPropertyByName shall fail and return NULL. ]*/ + if ( + (modelTypeHandle == NULL) || + (desiredPropertyName == NULL) + ) + { + LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyName=%p", modelTypeHandle, desiredPropertyName); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_036: [ If a desired property having the name desiredPropertyName exists then Schema_GetModelDesiredPropertyByName shall succeed and return a non-NULL value. ]*/ + /*Codes_SRS_SCHEMA_02_037: [ Otherwise, Schema_GetModelDesiredPropertyByName shall fail and return NULL. ]*/ + SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + SCHEMA_DESIRED_PROPERTY_HANDLE* temp = VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, desiredPropertyName); + if (temp == NULL) + { + LogError("no such desired property by name %s", desiredPropertyName); + result = NULL; + } + else + { + result = *temp; + } + } + return result; +} + +SCHEMA_DESIRED_PROPERTY_HANDLE Schema_GetModelDesiredPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index) +{ + SCHEMA_DESIRED_PROPERTY_HANDLE result; + /*Codes_SRS_SCHEMA_02_038: [ If modelTypeHandle is NULL then Schema_GetModelDesiredPropertyByIndex shall fail and return NULL. ]*/ + if (modelTypeHandle == NULL) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t index=%p", modelTypeHandle, index); + result = NULL; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + /*Codes_SRS_SCHEMA_02_039: [ If index is outside the range for existing indexes of desire properties, then Schema_GetModelDesiredPropertyByIndex shall fail and return NULL. ]*/ + /*Codes_SRS_SCHEMA_02_040: [ Otherwise, Schema_GetModelDesiredPropertyByIndex shall succeed and return a non-NULL value. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE* temp = VECTOR_element(handleData->desiredProperties, index); + if (temp == NULL) + { + LogError("VECTOR_element produced NULL (likely out of bounds index)"); + result = NULL; + } + else + { + result = *temp; + } + } + return result; +} + +bool Schema_ModelDesiredPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyPath) +{ + bool result; + /*Codes_SRS_SCHEMA_02_041: [ If modelTypeHandle is NULL then Schema_ModelDesiredPropertyByPathExists shall fail and return false. ]*/ + /*Codes_SRS_SCHEMA_02_042: [ If desiredPropertyPath is NULL then Schema_ModelDesiredPropertyByPathExists shall fail and return false. ]*/ + if ( + (modelTypeHandle == NULL) || + (desiredPropertyPath == NULL) + ) + { + LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyPath=%p", modelTypeHandle, desiredPropertyPath); + result = false; + } + else + { + const char* slashPos; + + /*Codes_SRS_SCHEMA_02_044: [ If the desired property cannot be found Schema_ModelDesiredPropertyByPathExists shall fail and return false. ]*/ + result = false; + + /*Codes_SRS_SCHEMA_02_043: [ desiredPropertyPath shall be assumed to be in the format model1/model2/.../desiredPropertyName. ]*/ + if (*desiredPropertyPath == '/') + { + desiredPropertyPath++; + } + + do + { + const char* endPos; + size_t i; + size_t modelCount; + SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + slashPos = strchr(desiredPropertyPath, '/'); + endPos = slashPos; + if (endPos == NULL) + { + endPos = &desiredPropertyPath[strlen(desiredPropertyPath)]; + } + + modelCount = VECTOR_size(modelType->models); + for (i = 0; i < modelCount; i++) + { + MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i); + if ((strncmp(childModel->propertyName, desiredPropertyPath, endPos - desiredPropertyPath) == 0) && + (strlen(childModel->propertyName) == (size_t)(endPos - desiredPropertyPath))) + { + /* found */ + modelTypeHandle = childModel->modelHandle; + break; + } + } + + if (i < modelCount) + { + /* model found, check if there is more in the path */ + if (slashPos == NULL) + { + /*Codes_SRS_SCHEMA_02_045: [ If the path desiredPropertyPath points to a sub-model, Schema_ModelDesiredPropertyByPathExists shall succeed and true. ]*/ + /* this is the last one, so this is the thing we were looking for */ + result = true; + break; + } + else + { + /* continue looking, there's more */ + desiredPropertyPath = slashPos + 1; + } + } + else + { + /* no model found, let's see if this is a property */ + result = (VECTOR_find_if(modelType->desiredProperties, desiredPropertyExists, desiredPropertyPath) != NULL); + if (!result) + { + LogError("no such desired property \"%s\"", desiredPropertyPath); + } + break; + } + } while (slashPos != NULL); + } + return result; +} + +const char* Schema_GetModelDesiredPropertyType(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + const char* result; + /*Codes_SRS_SCHEMA_02_062: [ If desiredPropertyHandle is NULL then Schema_GetModelDesiredPropertyType shall fail and return NULL. ]*/ + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_063: [ Otherwise, Schema_GetModelDesiredPropertyType shall return the type of the desired property. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = desirePropertyHandleData->desiredPropertyType; + } + return result; +} + +pfDesiredPropertyFromAGENT_DATA_TYPE Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + pfDesiredPropertyFromAGENT_DATA_TYPE result; + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = NULL; + } + else + { + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = desirePropertyHandleData->desiredPropertyFromAGENT_DATA_TYPE; + } + return result; +} + +pfOnDesiredProperty Schema_GetModelDesiredProperty_pfOnDesiredProperty(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + pfOnDesiredProperty result; + /*Codes_SRS_SCHEMA_02_084: [ If desiredPropertyHandle is NULL then Schema_GetModelDesiredProperty_pfOnDesiredProperty shall return NULL. ]*/ + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_085: [ Otherwise Schema_GetModelDesiredProperty_pfOnDesiredProperty shall return the saved desired property callback. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = desirePropertyHandleData->onDesiredProperty; + } + return result; +} + +size_t Schema_GetModelDesiredProperty_offset(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + size_t result; + /*Codes_SRS_SCHEMA_02_060: [ If desiredPropertyHandle is NULL then Schema_GetModelDesiredProperty_offset shall fail and return 0. ]*/ + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = 0; + } + else + { + /*Codes_SRS_SCHEMA_02_061: [ Otherwise Schema_GetModelDesiredProperty_offset shall succeed and return the offset. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = desirePropertyHandleData->offset; + } + return result; +} + +static bool modelInModelExists(const void* element, const void* value) +{ + MODEL_IN_MODEL* modelInModel = (MODEL_IN_MODEL*)element; + return (strcmp(modelInModel->propertyName, value) == 0); +} + +SCHEMA_MODEL_ELEMENT Schema_GetModelElementByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* elementName) +{ + SCHEMA_MODEL_ELEMENT result; + /*Codes_SRS_SCHEMA_02_076: [ If modelTypeHandle is NULL then Schema_GetModelElementByName shall fail and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_SEARCH_INVALID_ARG. ]*/ + /*Codes_SRS_SCHEMA_02_077: [ If elementName is NULL then Schema_GetModelElementByName shall fail and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_SEARCH_INVALID_ARG. ]*/ + if ( + (modelTypeHandle == NULL) || + (elementName == NULL) + ) + { + LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* elementName=%p", modelTypeHandle, elementName); + result.elementType = SCHEMA_SEARCH_INVALID_ARG; + } + else + { + SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle; + + SCHEMA_DESIRED_PROPERTY_HANDLE* desiredPropertyHandle = VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, elementName); + if (desiredPropertyHandle != NULL) + { + /*Codes_SRS_SCHEMA_02_080: [ If elementName is a desired property then Schema_GetModelElementByName shall succeed and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_DESIRED_PROPERTY and SCHEMA_MODEL_ELEMENT.elementHandle.desiredPropertyHandle to the handle of the desired property. ]*/ + result.elementType = SCHEMA_DESIRED_PROPERTY; + result.elementHandle.desiredPropertyHandle = *desiredPropertyHandle; + } + else + { + size_t nProcessedProperties = 0; + SCHEMA_PROPERTY_HANDLE_DATA* property = NULL; + for (size_t i = 0; i < handleData->PropertyCount;i++) + { + property = (SCHEMA_PROPERTY_HANDLE_DATA*)(handleData->Properties[i]); + if (strcmp(property->PropertyName, elementName) == 0) + { + i = handleData->PropertyCount; /*found it*/ + } + else + { + nProcessedProperties++; + } + } + + if (nProcessedProperties < handleData->PropertyCount) + { + /*Codes_SRS_SCHEMA_02_078: [ If elementName is a property then Schema_GetModelElementByName shall succeed and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_PROPERTY and SCHEMA_MODEL_ELEMENT.elementHandle.propertyHandle to the handle of the property. ]*/ + result.elementType = SCHEMA_PROPERTY; + result.elementHandle.propertyHandle = property; + } + else + { + + SCHEMA_REPORTED_PROPERTY_HANDLE* reportedPropertyHandle = VECTOR_find_if(handleData->reportedProperties, reportedPropertyExists, elementName); + if (reportedPropertyHandle != NULL) + { + /*Codes_SRS_SCHEMA_02_079: [ If elementName is a reported property then Schema_GetModelElementByName shall succeed and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_REPORTED_PROPERTY and SCHEMA_MODEL_ELEMENT.elementHandle.reportedPropertyHandle to the handle of the reported property. ]*/ + result.elementType = SCHEMA_REPORTED_PROPERTY; + result.elementHandle.reportedPropertyHandle = *reportedPropertyHandle; + } + else + { + + size_t nProcessedActions = 0; + SCHEMA_ACTION_HANDLE_DATA* actionHandleData = NULL; + for (size_t i = 0;i < handleData->ActionCount; i++) + { + actionHandleData = (SCHEMA_ACTION_HANDLE_DATA*)(handleData->Actions[i]); + if (strcmp(actionHandleData->ActionName, elementName) == 0) + { + i = handleData->ActionCount; /*get out quickly*/ + } + else + { + nProcessedActions++; + } + } + + if (nProcessedActions < handleData->ActionCount) + { + /*Codes_SRS_SCHEMA_02_081: [ If elementName is a model action then Schema_GetModelElementByName shall succeed and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_MODEL_ACTION and SCHEMA_MODEL_ELEMENT.elementHandle.actionHandle to the handle of the action. ]*/ + result.elementType = SCHEMA_MODEL_ACTION; + result.elementHandle.actionHandle = actionHandleData; + } + else + { + MODEL_IN_MODEL* modelInModel = VECTOR_find_if(handleData->models, modelInModelExists, elementName); + if (modelInModel != NULL) + { + /*Codes_SRS_SCHEMA_02_082: [ If elementName is a model in model then Schema_GetModelElementByName shall succeed and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_MODEL_IN_MODEL and SCHEMA_MODEL_ELEMENT.elementHandle.modelHandle to the handle of the model. ]*/ + result.elementType = SCHEMA_MODEL_IN_MODEL; + result.elementHandle.modelHandle = modelInModel->modelHandle; + } + else + { + /*Codes_SRS_SCHEMA_02_083: [ Otherwise Schema_GetModelElementByName shall fail and set SCHEMA_MODEL_ELEMENT.elementType to SCHEMA_NOT_FOUND. ]*/ + result.elementType = SCHEMA_NOT_FOUND; + } + } + } + } + } + } + return result; +} + +pfDesiredPropertyDeinitialize Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + pfDesiredPropertyDeinitialize result; + /*Ccodes_SRS_SCHEMA_02_064: [ If desiredPropertyHandle is NULL then Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize shall fail and return NULL. ]*/ + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_065: [ Otherwise Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize shall return a non-NULL function pointer. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* handleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = handleData->desiredPropertDeinitialize; + } + return result; +} + +pfDesiredPropertyInitialize Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle) +{ + pfDesiredPropertyInitialize result; + /*Codes_SRS_SCHEMA_02_066: [ If desiredPropertyHandle is NULL then Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize shall fail and return NULL. ]*/ + if (desiredPropertyHandle == NULL) + { + LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_067: [ Otherwise Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize shall return a non-NULL function pointer. ]*/ + SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* handleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle; + result = handleData->desiredPropertInitialize; + } + return result; +} + +static bool SchemaHasModel(const SCHEMA_HANDLE* schemaHandle, const char* modelName) +{ + return (Schema_GetModelByName(*schemaHandle, modelName) != NULL); +} + + +SCHEMA_HANDLE Schema_GetSchemaForModel(const char* modelName) +{ + SCHEMA_HANDLE result; + /*Codes_SRS_SCHEMA_02_093: [ If modelName is NULL then Schema_GetSchemaForModel shall fail and return NULL. ]*/ + if (modelName == NULL) + { + LogError("invalid arg const char* modelName=%p", modelName); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_094: [ Schema_GetSchemaForModel shall find the SCHEMA_HANDLE that contains a model by name modelName and if found, succeed and return that. ]*/ + SCHEMA_HANDLE* temp = VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaHasModel, modelName); + + if (temp == NULL) + { + /*Codes_SRS_SCHEMA_02_095: [ If the model is not found in any schema, then Schema_GetSchemaForModel shall fail and return NULL. ]*/ + LogError("unable to find a schema that has a model named %s", modelName); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_094: [ Schema_GetSchemaForModel shall find the SCHEMA_HANDLE that contains a model by name modelName and if found, succeed and return that. ]*/ + result = *temp; + } + } + + return result; +} + +void* Schema_GetMetadata(SCHEMA_HANDLE schemaHandle) +{ + void* result; + /*Codes_SRS_SCHEMA_02_091: [ If schemaHandle is NULL then Schema_GetMetadata shall fail and return NULL. ]*/ + if (schemaHandle == NULL) + { + LogError("invalid arg SCHEMA_HANDLE schemaHandle=%p", schemaHandle); + result = NULL; + } + else + { + /*Codes_SRS_SCHEMA_02_092: [ Otherwise, Schema_GetMetadata shall succeed and return the saved metadata pointer. ]*/ + result = ((SCHEMA_HANDLE_DATA*)schemaHandle)->metadata; + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/schemalib.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/gballoc.h" + +#include "schemalib.h" +#include "codefirst.h" +#include "schema.h" +#include "datamarshaller.h" +#include "datapublisher.h" +#include <stddef.h> +#include "azure_c_shared_utility/xlogging.h" +#include "iotdevice.h" + +#define DEFAULT_CONTAINER_NAME "Container" + +DEFINE_ENUM_STRINGS(SERIALIZER_RESULT, SERIALIZER_RESULT_VALUES); + +typedef enum AGENT_STATE_TAG +{ + AGENT_NOT_INITIALIZED, + AGENT_INITIALIZED +} AGENT_STATE; + +static AGENT_STATE g_AgentState = AGENT_NOT_INITIALIZED; + +SERIALIZER_RESULT serializer_init(const char* overrideSchemaNamespace) +{ + SERIALIZER_RESULT result; + + /* Codes_SRS_SCHEMALIB_99_074:[serializer_init when already initialized shall return SERIALIZER_ALREADY_INIT.] */ + if (g_AgentState != AGENT_NOT_INITIALIZED) + { + result = SERIALIZER_ALREADY_INIT; + LogError("(result = %s)", ENUM_TO_STRING(SERIALIZER_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMALIB_99_006:[ Initialize CodeFirst by a call to CodeFirst_Init.] */ + /* Codes_SRS_SCHEMALIB_99_076:[serializer_init shall pass the value of overrideSchemaNamespace argument to CodeFirst_Init.] */ + if (CodeFirst_Init(overrideSchemaNamespace) != CODEFIRST_OK) + { + /* Codes_SRS_SCHEMALIB_99_007:[ On error SERIALIZER_CODEFIRST_INIT_FAILED shall be returned.] */ + result = SERIALIZER_CODEFIRST_INIT_FAILED; + LogError("(result = %s)", ENUM_TO_STRING(SERIALIZER_RESULT, result)); + } + else + { + /* Codes_SRS_SCHEMALIB_99_075:[When an serializer_init call fails for any reason the previous initialization state shall be preserved. The initialized state shall only be changed on a succesfull Init.] */ + g_AgentState = AGENT_INITIALIZED; + + /* Codes_SRS_SCHEMALIB_99_073:[On success serializer_init shall return SERIALIZER_OK.] */ + result = SERIALIZER_OK; + } + } + + return result; +} + +void serializer_deinit(void) +{ + /* Codes_SRS_SCHEMALIB_99_025:[ serializer_deinit shall de-initialize all modules initialized by serializer_init.] */ + if (g_AgentState == AGENT_INITIALIZED) + { + CodeFirst_Deinit(); + } + + /* Codes_SRS_SCHEMALIB_99_026:[ A subsequent call to serializer_start shall succeed.] */ + g_AgentState = AGENT_NOT_INITIALIZED; +} + +SERIALIZER_RESULT serializer_setconfig(SERIALIZER_CONFIG which, void* value) +{ + SERIALIZER_RESULT result; + + /* Codes_SRS_SCHEMALIB_99_137:[ If the value argument is NULL, serializer_setconfig shall return SERIALIZER_INVALID_ARG.] */ + if (value == NULL) + { + result = SERIALIZER_INVALID_ARG; + } + /* Codes_SRS_SCHEMALIB_99_142:[ When the which argument is SerializeDelayedBufferMaxSize, serializer_setconfig shall invoke DataPublisher_SetMaxBufferSize with the dereferenced value argument, and shall return SERIALIZER_OK.] */ + else if (which == SerializeDelayedBufferMaxSize) + { + DataPublisher_SetMaxBufferSize(*(size_t*)value); + result = SERIALIZER_OK; + } + /* Codes_SRS_SCHEMALIB_99_138:[ If the which argument is not one of the declared members of the SERIALIZER_CONFIG enum, serializer_setconfig shall return SERIALIZER_INVALID_ARG.] */ + else + { + result = SERIALIZER_INVALID_ARG; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/schemaserializer.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/gballoc.h" + +#include <stddef.h> +#include "schemaserializer.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/macro_utils.h" + +DEFINE_ENUM_STRINGS(SCHEMA_SERIALIZER_RESULT, SCHEMA_SERIALIZER_RESULT_VALUES); + +#define LOG_SCHEMA_SERIALIZER_ERROR(result) LogError("(result = %s)", ENUM_TO_STRING(SCHEMA_SERIALIZER_RESULT, (result))) + +static const char* ConvertType(const char* sourceType) +{ + /* Codes_SRS_SCHEMA_SERIALIZER_01_016: ["ascii_char_ptr" shall be translated to "string".] */ + if (strcmp(sourceType, "ascii_char_ptr") == 0) + { + return "string"; + } + else + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_017: [All other types shall be kept as they are.] */ + return sourceType; + } +} + +/* Codes_SRS_SCHEMA_SERIALIZER_01_001: [SchemaSerializer_SerializeCommandMetadata shall serialize a specific model to a string using JSON as format.] */ +SCHEMA_SERIALIZER_RESULT SchemaSerializer_SerializeCommandMetadata(SCHEMA_MODEL_TYPE_HANDLE modelHandle, STRING_HANDLE schemaText) +{ + SCHEMA_SERIALIZER_RESULT result; + + /* Codes_SRS_SCHEMA_SERIALIZER_01_013: [If the modelHandle argument is NULL, SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_INVALID_ARG.] */ + if ((modelHandle == NULL) || + /* Codes_SRS_SCHEMA_SERIALIZER_01_014: [If the schemaText argument is NULL, SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_INVALID_ARG.] */ + (schemaText == NULL)) + { + result = SCHEMA_SERIALIZER_INVALID_ARG; + LogError("(result = %s), modelHandle = %p, schemaText = %p", ENUM_TO_STRING(SCHEMA_SERIALIZER_RESULT, result), modelHandle, schemaText); + } + else + { + size_t commandCount; + + /* Codes_SRS_SCHEMA_SERIALIZER_01_002: [Only commands shall be serialized, the properties of a model shall be ignored.] */ + + /* Codes_SRS_SCHEMA_SERIALIZER_01_003: [The output JSON shall have an array, where each array element shall represent a command.] */ + /* Codes_SRS_SCHEMA_SERIALIZER_01_011: [The JSON text shall be built into the string indicated by the schemaText argument by using String APIs.] */ + if ((STRING_concat(schemaText, "[") != 0) || + /* Codes_SRS_SCHEMA_SERIALIZER_01_006: [The object for a command shall have a member named Name, whose value shall be the command name as obtained by using Schema APIs.] */ + (Schema_GetModelActionCount(modelHandle, &commandCount) != SCHEMA_OK)) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + result = SCHEMA_SERIALIZER_ERROR; + LOG_SCHEMA_SERIALIZER_ERROR(result); + } + else + { + size_t i; + + for (i = 0; i < commandCount; i++) + { + SCHEMA_ACTION_HANDLE actionHandle = Schema_GetModelActionByIndex(modelHandle, i); + const char* commandName; + size_t argCount; + size_t j; + + /* Codes_SRS_SCHEMA_SERIALIZER_01_005: [Each array element shall be a JSON object.] */ + if ((actionHandle == NULL) || + (STRING_concat(schemaText, "{ \"Name\":\"") != 0) || + ((commandName = Schema_GetModelActionName(actionHandle)) == NULL) || + (STRING_concat(schemaText, commandName) != 0) || + /* Codes_SRS_SCHEMA_SERIALIZER_01_007: [The object for a command shall also have a "Parameters" member.] */ + (STRING_concat(schemaText, "\", \"Parameters\":[") != 0) || + (Schema_GetModelActionArgumentCount(actionHandle, &argCount) != SCHEMA_OK)) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed encoding action."); + break; + } + else + { + for (j = 0; j < argCount; j++) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_008: [The parameters member shall be an array, where each entry is a command parameter.] */ + SCHEMA_ACTION_ARGUMENT_HANDLE argHandle = Schema_GetModelActionArgumentByIndex(actionHandle, j); + const char* argName; + const char* argType; + + /* Codes_SRS_SCHEMA_SERIALIZER_01_009: [Each command parameter shall have a member named "Name", that should have as value the command argument name as obtained by using Schema APIs.] */ + if ((argHandle == NULL) || + (STRING_concat(schemaText, "{\"Name\":\"") != 0) || + ((argName = Schema_GetActionArgumentName(argHandle)) == NULL) || + (STRING_concat(schemaText, argName) != 0) || + /* Codes_SRS_SCHEMA_SERIALIZER_01_010: [Each command parameter shall have a member named "Type", that should have as value the command argument type as obtained by using Schema APIs.] */ + (STRING_concat(schemaText, "\",\"Type\":\"") != 0) || + ((argType = Schema_GetActionArgumentType(argHandle)) == NULL) || + (STRING_concat(schemaText, ConvertType(argType)) != 0)) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed encoding argument."); + break; + } + else + { + if (j + 1 < argCount) + { + if (STRING_concat(schemaText, "\"},") != 0) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed to concatenate arg end."); + break; + } + } + else + { + if (STRING_concat(schemaText, "\"}") != 0) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed to concatenate arg end."); + break; + } + } + } + } + + if (j < argCount) + { + break; + } + + if (i + 1 < commandCount) + { + if (STRING_concat(schemaText, "]},") != 0) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed to concatenate."); + break; + } + } + else + { + if (STRING_concat(schemaText, "]}") != 0) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed to concatenate."); + break; + } + } + } + } + + if (i < commandCount) + { + result = SCHEMA_SERIALIZER_ERROR; + } + else if (STRING_concat(schemaText, "]") != 0) + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_015: [If any of the Schema or String APIs fail then SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_ERROR.] */ + LogError("Failed to concatenate commands object end."); + result = SCHEMA_SERIALIZER_ERROR; + } + else + { + /* Codes_SRS_SCHEMA_SERIALIZER_01_012: [On success SchemaSerializer_SerializeCommandMetadata shall return SCHEMA_SERIALIZER_OK.] */ + result = SCHEMA_SERIALIZER_OK; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer/src/serializer.def Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,218 @@ +LIBRARY serializer +EXPORTS + MethodReturn_Create + MethodReturn_Destroy + MethodReturn_GetReturn + SCHEMA_SERIALIZER_RESULTStringStorage + SCHEMA_SERIALIZER_RESULTStrings + SCHEMA_SERIALIZER_RESULT_FromString + SchemaSerializer_SerializeCommandMetadata + SERIALIZER_RESULTStringStorage + SERIALIZER_RESULTStrings + SERIALIZER_RESULT_FromString + serializer_init + serializer_deinit + serializer_setconfig + SCHEMA_RESULTStringStorage + SCHEMA_RESULTStrings + SCHEMA_RESULT_FromString + Schema_Create + Schema_GetMetadata + Schema_GetSchemaCount + Schema_GetSchemaByNamespace + Schema_GetSchemaForModel + Schema_GetSchemaNamespace + Schema_AddDeviceRef + Schema_ReleaseDeviceRef + Schema_CreateModelType + Schema_GetSchemaForModelType + Schema_GetModelName + Schema_CreateStructType + Schema_GetStructTypeName + Schema_AddStructTypeProperty + Schema_AddModelProperty + Schema_AddModelReportedProperty + Schema_AddModelDesiredProperty + Schema_AddModelModel + Schema_CreateModelAction + Schema_CreateModelMethod + Schema_AddModelActionArgument + Schema_AddModelMethodArgument + Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE + Schema_GetModelDesiredProperty_pfOnDesiredProperty + Schema_GetModelModelByName_Offset + Schema_GetModelModelByName_OnDesiredProperty + Schema_GetModelModelByIndex_Offset + Schema_GetModelDesiredProperty_offset + Schema_GetModelDesiredPropertyType + Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize + Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize + Schema_GetModelElementByName + Schema_GetModelCount + Schema_GetModelByName + Schema_GetModelByIndex + Schema_GetModelPropertyCount + Schema_GetModelPropertyByName + Schema_GetModelPropertyByIndex + Schema_GetModelReportedPropertyCount + Schema_GetModelReportedPropertyByName + Schema_GetModelReportedPropertyByIndex + Schema_GetModelDesiredPropertyCount + Schema_GetModelDesiredPropertyByName + Schema_GetModelDesiredPropertyByIndex + Schema_GetModelModelCount + Schema_GetModelModelByName + Schema_GetModelModelyByIndex + Schema_GetModelModelPropertyNameByIndex + Schema_ModelPropertyByPathExists + Schema_ModelReportedPropertyByPathExists + Schema_ModelDesiredPropertyByPathExists + Schema_GetModelActionCount + Schema_GetModelActionByName + Schema_GetModelMethodByName + Schema_GetModelActionByIndex + Schema_GetModelActionArgumentCount + Schema_GetModelMethodArgumentCount + Schema_GetModelActionName + Schema_GetModelActionArgumentByName + Schema_GetModelActionArgumentByIndex + Schema_GetModelMethodArgumentByIndex + Schema_GetActionArgumentName + Schema_GetMethodArgumentName + Schema_GetActionArgumentType + Schema_GetMethodArgumentType + Schema_GetStructTypeCount + Schema_GetStructTypeByName + Schema_GetStructTypeByIndex + Schema_GetStructTypePropertyCount + Schema_GetStructTypePropertyByName + Schema_GetStructTypePropertyByIndex + Schema_GetPropertyName + Schema_GetPropertyType + Schema_Destroy + Schema_DestroyIfUnused + MULTITREE_RESULTStringStorage + MULTITREE_RESULTStrings + MULTITREE_RESULT_FromString + MultiTree_Create + MultiTree_AddLeaf + MultiTree_AddChild + MultiTree_GetChildCount + MultiTree_GetChild + MultiTree_GetChildByName + MultiTree_GetName + MultiTree_GetValue + MultiTree_GetLeafValue + MultiTree_SetValue + MultiTree_Destroy + JSON_ENCODER_TOSTRING_RESULTStringStorage + JSON_ENCODER_RESULTStringStorage + JSON_ENCODER_RESULTStrings + JSON_ENCODER_RESULT_FromString + JSON_ENCODER_TOSTRING_RESULTStrings + JSON_ENCODER_TOSTRING_RESULT_FromString + JSONEncoder_CharPtr_ToString + JSONEncoder_EncodeTree + JSONDecoder_JSON_To_MultiTree + SkipWhiteSpaces + DEVICE_RESULTStringStorage + DEVICE_RESULTStrings + DEVICE_RESULT_FromString + Device_Create + Device_Destroy + Device_StartTransaction + Device_PublishTransacted + Device_EndTransaction + Device_CancelTransaction + Device_CreateTransaction_ReportedProperties + Device_PublishTransacted_ReportedProperty + Device_CommitTransaction_ReportedProperties + Device_DestroyTransaction_ReportedProperties + Device_ExecuteCommand + Device_ExecuteMethod + Device_IngestDesiredProperties + DATA_SERIALIZER_RESULTStringStorage + DATA_SERIALIZER_RESULTStrings + DATA_SERIALIZER_RESULT_FromString + DataSerializer_Encode + DataSerializer_Decode + DATA_PUBLISHER_RESULTStringStorage + DATA_PUBLISHER_RESULTStrings + DATA_PUBLISHER_RESULT_FromString + DataPublisher_Create + DataPublisher_Destroy + DataPublisher_StartTransaction + DataPublisher_PublishTransacted + DataPublisher_EndTransaction + DataPublisher_CancelTransaction + DataPublisher_SetMaxBufferSize + DataPublisher_GetMaxBufferSize + DataPublisher_CreateTransaction_ReportedProperties + DataPublisher_PublishTransacted_ReportedProperty + DataPublisher_CommitTransaction_ReportedProperties + DataPublisher_DestroyTransaction_ReportedProperties + DATA_MARSHALLER_RESULTStringStorage + DATA_MARSHALLER_RESULTStrings + DATA_MARSHALLER_RESULT_FromString + DataMarshaller_Create + DataMarshaller_Destroy + DataMarshaller_SendData + DataMarshaller_SendData_ReportedProperties + COMMANDDECODER_RESULTStringStorage + AGENT_DATA_TYPE_TYPEStringStorage + AGENT_DATA_TYPE_TYPEStrings + AGENT_DATA_TYPE_TYPE_FromString + COMMANDDECODER_RESULTStrings + COMMANDDECODER_RESULT_FromString + CommandDecoder_Create + CommandDecoder_ExecuteCommand + CommandDecoder_ExecuteMethod + CommandDecoder_Destroy + CommandDecoder_IngestDesiredProperties + CODEFIRST_RESULTStringStorage + EXECUTE_COMMAND_RESULTStringStorage + EXECUTE_COMMAND_RESULTStrings + EXECUTE_COMMAND_RESULT_FromString + CODEFIRST_RESULTStrings + CODEFIRST_RESULT_FromString + CodeFirst_Init + CodeFirst_Deinit + CodeFirst_RegisterSchema + CodeFirst_InvokeAction + CodeFirst_InvokeMethod + CodeFirst_ExecuteCommand + CodeFirst_ExecuteMethod + CodeFirst_CreateDevice + CodeFirst_DestroyDevice + CodeFirst_SendAsync + CodeFirst_SendAsyncReported + CodeFirst_IngestDesiredProperties + CodeFirst_GetPrimitiveType + hexToASCII + AGENT_DATA_TYPES_RESULTStringStorage + AGENT_DATA_TYPES_RESULTStrings + AGENT_DATA_TYPES_RESULT_FromString + AgentDataTypes_ToString + Create_EDM_BOOLEAN_from_int + Create_AGENT_DATA_TYPE_from_UINT8 + Create_AGENT_DATA_TYPE_from_date + Create_EDM_DECIMAL_from_charz + Create_AGENT_DATA_TYPE_from_DOUBLE + Create_AGENT_DATA_TYPE_from_SINT16 + Create_AGENT_DATA_TYPE_from_SINT32 + Create_AGENT_DATA_TYPE_from_SINT64 + Create_AGENT_DATA_TYPE_from_SINT8 + Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET + Create_AGENT_DATA_TYPE_from_EDM_GUID + Create_AGENT_DATA_TYPE_from_EDM_BINARY + Create_AGENT_DATA_TYPE_from_FLOAT + Create_AGENT_DATA_TYPE_from_charz + Create_AGENT_DATA_TYPE_from_charz_no_quotes + Create_NULL_AGENT_DATA_TYPE + Create_AGENT_DATA_TYPE_from_Members + Create_AGENT_DATA_TYPE_from_MemberPointers + Create_AGENT_DATA_TYPE_from_AGENT_DATA_TYPE + Destroy_AGENT_DATA_TYPE + CreateAgentDataType_From_String + AgentDataType_GetComplexTypeField +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/macro_utils.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,12641 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*THIS FILE IS GENERATED*/ +/*DO NOT EDIT BY HAND!!!*/ +/*instead edit macro_utils.tt here: http://www.github.com/azure/azure-macro-utils-c.git */ +/*and then propagate the generated file to all the repos*/ +/* !!! CAUTION!!! This file is copied to multiple places */ +/* in https://github.com/Azure/azure-c-shared-utility.git, */ +/* and all of these copies must be located and replaced. */ + + + +#ifndef MACRO_UTILS_H +#define MACRO_UTILS_H + +#include <string.h> + +#if (defined OPTIMIZE_RETURN_CODES) + #define __FAILURE__ 1 +#else + #define __FAILURE__ __LINE__ +#endif + +/*"pointer or NULL" macro - because when printf-ing arguments NULL is not valid for %s (section 7.1.4 of C11 standard) */ +#define P_OR_NULL(p) (((p)!=NULL)?(p):"NULL") + +#define TOSTRING_(x) #x +#define TOSTRING(x) TOSTRING_(x) + +#define IFCOMMA(N) C2(IFCOMMA_, N) +#define IFCOMMA_0 +#define IFCOMMA_2 +#define IFCOMMA_4 , +#define IFCOMMA_6 , +#define IFCOMMA_8 , +#define IFCOMMA_10 , +#define IFCOMMA_12 , +#define IFCOMMA_14 , +#define IFCOMMA_16 , +#define IFCOMMA_18 , +#define IFCOMMA_20 , +#define IFCOMMA_22 , +#define IFCOMMA_24 , +#define IFCOMMA_26 , +#define IFCOMMA_28 , +#define IFCOMMA_30 , +#define IFCOMMA_32 , +#define IFCOMMA_34 , +#define IFCOMMA_36 , +#define IFCOMMA_38 , +#define IFCOMMA_40 , +#define IFCOMMA_42 , +#define IFCOMMA_44 , +#define IFCOMMA_46 , +#define IFCOMMA_48 , +#define IFCOMMA_50 , +#define IFCOMMA_52 , +#define IFCOMMA_54 , +#define IFCOMMA_56 , +#define IFCOMMA_58 , +#define IFCOMMA_60 , +#define IFCOMMA_62 , +#define IFCOMMA_64 , +#define IFCOMMA_66 , +#define IFCOMMA_68 , +#define IFCOMMA_70 , +#define IFCOMMA_72 , +#define IFCOMMA_74 , +#define IFCOMMA_76 , +#define IFCOMMA_78 , +#define IFCOMMA_80 , +#define IFCOMMA_82 , +#define IFCOMMA_84 , +#define IFCOMMA_86 , +#define IFCOMMA_88 , +#define IFCOMMA_90 , +#define IFCOMMA_92 , +#define IFCOMMA_94 , +#define IFCOMMA_96 , +#define IFCOMMA_98 , +#define IFCOMMA_100 , +#define IFCOMMA_102 , +#define IFCOMMA_104 , +#define IFCOMMA_106 , +#define IFCOMMA_108 , +#define IFCOMMA_110 , +#define IFCOMMA_112 , +#define IFCOMMA_114 , +#define IFCOMMA_116 , +#define IFCOMMA_118 , +#define IFCOMMA_120 , +#define IFCOMMA_122 , +#define IFCOMMA_124 , + +#define IFCOMMA_NOFIRST(N) C2(IFCOMMA_NOFIRST, N) +#define IFCOMMA_NOFIRST1 +#define IFCOMMA_NOFIRST2 , +#define IFCOMMA_NOFIRST3 , +#define IFCOMMA_NOFIRST4 , +#define IFCOMMA_NOFIRST5 , +#define IFCOMMA_NOFIRST6 , +#define IFCOMMA_NOFIRST7 , +#define IFCOMMA_NOFIRST8 , +#define IFCOMMA_NOFIRST9 , +#define IFCOMMA_NOFIRST10 , +#define IFCOMMA_NOFIRST11 , +#define IFCOMMA_NOFIRST12 , +#define IFCOMMA_NOFIRST13 , +#define IFCOMMA_NOFIRST14 , +#define IFCOMMA_NOFIRST15 , +#define IFCOMMA_NOFIRST16 , +#define IFCOMMA_NOFIRST17 , +#define IFCOMMA_NOFIRST18 , +#define IFCOMMA_NOFIRST19 , +#define IFCOMMA_NOFIRST20 , +#define IFCOMMA_NOFIRST21 , +#define IFCOMMA_NOFIRST22 , +#define IFCOMMA_NOFIRST23 , +#define IFCOMMA_NOFIRST24 , +#define IFCOMMA_NOFIRST25 , +#define IFCOMMA_NOFIRST26 , +#define IFCOMMA_NOFIRST27 , +#define IFCOMMA_NOFIRST28 , +#define IFCOMMA_NOFIRST29 , +#define IFCOMMA_NOFIRST30 , +#define IFCOMMA_NOFIRST31 , +#define IFCOMMA_NOFIRST32 , +#define IFCOMMA_NOFIRST33 , +#define IFCOMMA_NOFIRST34 , +#define IFCOMMA_NOFIRST35 , +#define IFCOMMA_NOFIRST36 , +#define IFCOMMA_NOFIRST37 , +#define IFCOMMA_NOFIRST38 , +#define IFCOMMA_NOFIRST39 , +#define IFCOMMA_NOFIRST40 , +#define IFCOMMA_NOFIRST41 , +#define IFCOMMA_NOFIRST42 , +#define IFCOMMA_NOFIRST43 , +#define IFCOMMA_NOFIRST44 , +#define IFCOMMA_NOFIRST45 , +#define IFCOMMA_NOFIRST46 , +#define IFCOMMA_NOFIRST47 , +#define IFCOMMA_NOFIRST48 , +#define IFCOMMA_NOFIRST49 , +#define IFCOMMA_NOFIRST50 , +#define IFCOMMA_NOFIRST51 , +#define IFCOMMA_NOFIRST52 , +#define IFCOMMA_NOFIRST53 , +#define IFCOMMA_NOFIRST54 , +#define IFCOMMA_NOFIRST55 , +#define IFCOMMA_NOFIRST56 , +#define IFCOMMA_NOFIRST57 , +#define IFCOMMA_NOFIRST58 , +#define IFCOMMA_NOFIRST59 , +#define IFCOMMA_NOFIRST60 , +#define IFCOMMA_NOFIRST61 , +#define IFCOMMA_NOFIRST62 , +#define IFCOMMA_NOFIRST63 , +#define IFCOMMA_NOFIRST64 , +#define IFCOMMA_NOFIRST65 , +#define IFCOMMA_NOFIRST66 , +#define IFCOMMA_NOFIRST67 , +#define IFCOMMA_NOFIRST68 , +#define IFCOMMA_NOFIRST69 , +#define IFCOMMA_NOFIRST70 , +#define IFCOMMA_NOFIRST71 , +#define IFCOMMA_NOFIRST72 , +#define IFCOMMA_NOFIRST73 , +#define IFCOMMA_NOFIRST74 , +#define IFCOMMA_NOFIRST75 , +#define IFCOMMA_NOFIRST76 , +#define IFCOMMA_NOFIRST77 , +#define IFCOMMA_NOFIRST78 , +#define IFCOMMA_NOFIRST79 , +#define IFCOMMA_NOFIRST80 , +#define IFCOMMA_NOFIRST81 , +#define IFCOMMA_NOFIRST82 , +#define IFCOMMA_NOFIRST83 , +#define IFCOMMA_NOFIRST84 , +#define IFCOMMA_NOFIRST85 , +#define IFCOMMA_NOFIRST86 , +#define IFCOMMA_NOFIRST87 , +#define IFCOMMA_NOFIRST88 , +#define IFCOMMA_NOFIRST89 , +#define IFCOMMA_NOFIRST90 , +#define IFCOMMA_NOFIRST91 , +#define IFCOMMA_NOFIRST92 , +#define IFCOMMA_NOFIRST93 , +#define IFCOMMA_NOFIRST94 , +#define IFCOMMA_NOFIRST95 , +#define IFCOMMA_NOFIRST96 , +#define IFCOMMA_NOFIRST97 , +#define IFCOMMA_NOFIRST98 , +#define IFCOMMA_NOFIRST99 , +#define IFCOMMA_NOFIRST100 , +#define IFCOMMA_NOFIRST101 , +#define IFCOMMA_NOFIRST102 , +#define IFCOMMA_NOFIRST103 , +#define IFCOMMA_NOFIRST104 , +#define IFCOMMA_NOFIRST105 , +#define IFCOMMA_NOFIRST106 , +#define IFCOMMA_NOFIRST107 , +#define IFCOMMA_NOFIRST108 , +#define IFCOMMA_NOFIRST109 , +#define IFCOMMA_NOFIRST110 , +#define IFCOMMA_NOFIRST111 , +#define IFCOMMA_NOFIRST112 , +#define IFCOMMA_NOFIRST113 , +#define IFCOMMA_NOFIRST114 , +#define IFCOMMA_NOFIRST115 , +#define IFCOMMA_NOFIRST116 , +#define IFCOMMA_NOFIRST117 , +#define IFCOMMA_NOFIRST118 , +#define IFCOMMA_NOFIRST119 , +#define IFCOMMA_NOFIRST120 , +#define IFCOMMA_NOFIRST121 , +#define IFCOMMA_NOFIRST122 , +#define IFCOMMA_NOFIRST123 , +#define IFCOMMA_NOFIRST124 , + +#define DEC(x) C2(DEC,x) +#define DEC1024 1023 +#define DEC1023 1022 +#define DEC1022 1021 +#define DEC1021 1020 +#define DEC1020 1019 +#define DEC1019 1018 +#define DEC1018 1017 +#define DEC1017 1016 +#define DEC1016 1015 +#define DEC1015 1014 +#define DEC1014 1013 +#define DEC1013 1012 +#define DEC1012 1011 +#define DEC1011 1010 +#define DEC1010 1009 +#define DEC1009 1008 +#define DEC1008 1007 +#define DEC1007 1006 +#define DEC1006 1005 +#define DEC1005 1004 +#define DEC1004 1003 +#define DEC1003 1002 +#define DEC1002 1001 +#define DEC1001 1000 +#define DEC1000 999 +#define DEC999 998 +#define DEC998 997 +#define DEC997 996 +#define DEC996 995 +#define DEC995 994 +#define DEC994 993 +#define DEC993 992 +#define DEC992 991 +#define DEC991 990 +#define DEC990 989 +#define DEC989 988 +#define DEC988 987 +#define DEC987 986 +#define DEC986 985 +#define DEC985 984 +#define DEC984 983 +#define DEC983 982 +#define DEC982 981 +#define DEC981 980 +#define DEC980 979 +#define DEC979 978 +#define DEC978 977 +#define DEC977 976 +#define DEC976 975 +#define DEC975 974 +#define DEC974 973 +#define DEC973 972 +#define DEC972 971 +#define DEC971 970 +#define DEC970 969 +#define DEC969 968 +#define DEC968 967 +#define DEC967 966 +#define DEC966 965 +#define DEC965 964 +#define DEC964 963 +#define DEC963 962 +#define DEC962 961 +#define DEC961 960 +#define DEC960 959 +#define DEC959 958 +#define DEC958 957 +#define DEC957 956 +#define DEC956 955 +#define DEC955 954 +#define DEC954 953 +#define DEC953 952 +#define DEC952 951 +#define DEC951 950 +#define DEC950 949 +#define DEC949 948 +#define DEC948 947 +#define DEC947 946 +#define DEC946 945 +#define DEC945 944 +#define DEC944 943 +#define DEC943 942 +#define DEC942 941 +#define DEC941 940 +#define DEC940 939 +#define DEC939 938 +#define DEC938 937 +#define DEC937 936 +#define DEC936 935 +#define DEC935 934 +#define DEC934 933 +#define DEC933 932 +#define DEC932 931 +#define DEC931 930 +#define DEC930 929 +#define DEC929 928 +#define DEC928 927 +#define DEC927 926 +#define DEC926 925 +#define DEC925 924 +#define DEC924 923 +#define DEC923 922 +#define DEC922 921 +#define DEC921 920 +#define DEC920 919 +#define DEC919 918 +#define DEC918 917 +#define DEC917 916 +#define DEC916 915 +#define DEC915 914 +#define DEC914 913 +#define DEC913 912 +#define DEC912 911 +#define DEC911 910 +#define DEC910 909 +#define DEC909 908 +#define DEC908 907 +#define DEC907 906 +#define DEC906 905 +#define DEC905 904 +#define DEC904 903 +#define DEC903 902 +#define DEC902 901 +#define DEC901 900 +#define DEC900 899 +#define DEC899 898 +#define DEC898 897 +#define DEC897 896 +#define DEC896 895 +#define DEC895 894 +#define DEC894 893 +#define DEC893 892 +#define DEC892 891 +#define DEC891 890 +#define DEC890 889 +#define DEC889 888 +#define DEC888 887 +#define DEC887 886 +#define DEC886 885 +#define DEC885 884 +#define DEC884 883 +#define DEC883 882 +#define DEC882 881 +#define DEC881 880 +#define DEC880 879 +#define DEC879 878 +#define DEC878 877 +#define DEC877 876 +#define DEC876 875 +#define DEC875 874 +#define DEC874 873 +#define DEC873 872 +#define DEC872 871 +#define DEC871 870 +#define DEC870 869 +#define DEC869 868 +#define DEC868 867 +#define DEC867 866 +#define DEC866 865 +#define DEC865 864 +#define DEC864 863 +#define DEC863 862 +#define DEC862 861 +#define DEC861 860 +#define DEC860 859 +#define DEC859 858 +#define DEC858 857 +#define DEC857 856 +#define DEC856 855 +#define DEC855 854 +#define DEC854 853 +#define DEC853 852 +#define DEC852 851 +#define DEC851 850 +#define DEC850 849 +#define DEC849 848 +#define DEC848 847 +#define DEC847 846 +#define DEC846 845 +#define DEC845 844 +#define DEC844 843 +#define DEC843 842 +#define DEC842 841 +#define DEC841 840 +#define DEC840 839 +#define DEC839 838 +#define DEC838 837 +#define DEC837 836 +#define DEC836 835 +#define DEC835 834 +#define DEC834 833 +#define DEC833 832 +#define DEC832 831 +#define DEC831 830 +#define DEC830 829 +#define DEC829 828 +#define DEC828 827 +#define DEC827 826 +#define DEC826 825 +#define DEC825 824 +#define DEC824 823 +#define DEC823 822 +#define DEC822 821 +#define DEC821 820 +#define DEC820 819 +#define DEC819 818 +#define DEC818 817 +#define DEC817 816 +#define DEC816 815 +#define DEC815 814 +#define DEC814 813 +#define DEC813 812 +#define DEC812 811 +#define DEC811 810 +#define DEC810 809 +#define DEC809 808 +#define DEC808 807 +#define DEC807 806 +#define DEC806 805 +#define DEC805 804 +#define DEC804 803 +#define DEC803 802 +#define DEC802 801 +#define DEC801 800 +#define DEC800 799 +#define DEC799 798 +#define DEC798 797 +#define DEC797 796 +#define DEC796 795 +#define DEC795 794 +#define DEC794 793 +#define DEC793 792 +#define DEC792 791 +#define DEC791 790 +#define DEC790 789 +#define DEC789 788 +#define DEC788 787 +#define DEC787 786 +#define DEC786 785 +#define DEC785 784 +#define DEC784 783 +#define DEC783 782 +#define DEC782 781 +#define DEC781 780 +#define DEC780 779 +#define DEC779 778 +#define DEC778 777 +#define DEC777 776 +#define DEC776 775 +#define DEC775 774 +#define DEC774 773 +#define DEC773 772 +#define DEC772 771 +#define DEC771 770 +#define DEC770 769 +#define DEC769 768 +#define DEC768 767 +#define DEC767 766 +#define DEC766 765 +#define DEC765 764 +#define DEC764 763 +#define DEC763 762 +#define DEC762 761 +#define DEC761 760 +#define DEC760 759 +#define DEC759 758 +#define DEC758 757 +#define DEC757 756 +#define DEC756 755 +#define DEC755 754 +#define DEC754 753 +#define DEC753 752 +#define DEC752 751 +#define DEC751 750 +#define DEC750 749 +#define DEC749 748 +#define DEC748 747 +#define DEC747 746 +#define DEC746 745 +#define DEC745 744 +#define DEC744 743 +#define DEC743 742 +#define DEC742 741 +#define DEC741 740 +#define DEC740 739 +#define DEC739 738 +#define DEC738 737 +#define DEC737 736 +#define DEC736 735 +#define DEC735 734 +#define DEC734 733 +#define DEC733 732 +#define DEC732 731 +#define DEC731 730 +#define DEC730 729 +#define DEC729 728 +#define DEC728 727 +#define DEC727 726 +#define DEC726 725 +#define DEC725 724 +#define DEC724 723 +#define DEC723 722 +#define DEC722 721 +#define DEC721 720 +#define DEC720 719 +#define DEC719 718 +#define DEC718 717 +#define DEC717 716 +#define DEC716 715 +#define DEC715 714 +#define DEC714 713 +#define DEC713 712 +#define DEC712 711 +#define DEC711 710 +#define DEC710 709 +#define DEC709 708 +#define DEC708 707 +#define DEC707 706 +#define DEC706 705 +#define DEC705 704 +#define DEC704 703 +#define DEC703 702 +#define DEC702 701 +#define DEC701 700 +#define DEC700 699 +#define DEC699 698 +#define DEC698 697 +#define DEC697 696 +#define DEC696 695 +#define DEC695 694 +#define DEC694 693 +#define DEC693 692 +#define DEC692 691 +#define DEC691 690 +#define DEC690 689 +#define DEC689 688 +#define DEC688 687 +#define DEC687 686 +#define DEC686 685 +#define DEC685 684 +#define DEC684 683 +#define DEC683 682 +#define DEC682 681 +#define DEC681 680 +#define DEC680 679 +#define DEC679 678 +#define DEC678 677 +#define DEC677 676 +#define DEC676 675 +#define DEC675 674 +#define DEC674 673 +#define DEC673 672 +#define DEC672 671 +#define DEC671 670 +#define DEC670 669 +#define DEC669 668 +#define DEC668 667 +#define DEC667 666 +#define DEC666 665 +#define DEC665 664 +#define DEC664 663 +#define DEC663 662 +#define DEC662 661 +#define DEC661 660 +#define DEC660 659 +#define DEC659 658 +#define DEC658 657 +#define DEC657 656 +#define DEC656 655 +#define DEC655 654 +#define DEC654 653 +#define DEC653 652 +#define DEC652 651 +#define DEC651 650 +#define DEC650 649 +#define DEC649 648 +#define DEC648 647 +#define DEC647 646 +#define DEC646 645 +#define DEC645 644 +#define DEC644 643 +#define DEC643 642 +#define DEC642 641 +#define DEC641 640 +#define DEC640 639 +#define DEC639 638 +#define DEC638 637 +#define DEC637 636 +#define DEC636 635 +#define DEC635 634 +#define DEC634 633 +#define DEC633 632 +#define DEC632 631 +#define DEC631 630 +#define DEC630 629 +#define DEC629 628 +#define DEC628 627 +#define DEC627 626 +#define DEC626 625 +#define DEC625 624 +#define DEC624 623 +#define DEC623 622 +#define DEC622 621 +#define DEC621 620 +#define DEC620 619 +#define DEC619 618 +#define DEC618 617 +#define DEC617 616 +#define DEC616 615 +#define DEC615 614 +#define DEC614 613 +#define DEC613 612 +#define DEC612 611 +#define DEC611 610 +#define DEC610 609 +#define DEC609 608 +#define DEC608 607 +#define DEC607 606 +#define DEC606 605 +#define DEC605 604 +#define DEC604 603 +#define DEC603 602 +#define DEC602 601 +#define DEC601 600 +#define DEC600 599 +#define DEC599 598 +#define DEC598 597 +#define DEC597 596 +#define DEC596 595 +#define DEC595 594 +#define DEC594 593 +#define DEC593 592 +#define DEC592 591 +#define DEC591 590 +#define DEC590 589 +#define DEC589 588 +#define DEC588 587 +#define DEC587 586 +#define DEC586 585 +#define DEC585 584 +#define DEC584 583 +#define DEC583 582 +#define DEC582 581 +#define DEC581 580 +#define DEC580 579 +#define DEC579 578 +#define DEC578 577 +#define DEC577 576 +#define DEC576 575 +#define DEC575 574 +#define DEC574 573 +#define DEC573 572 +#define DEC572 571 +#define DEC571 570 +#define DEC570 569 +#define DEC569 568 +#define DEC568 567 +#define DEC567 566 +#define DEC566 565 +#define DEC565 564 +#define DEC564 563 +#define DEC563 562 +#define DEC562 561 +#define DEC561 560 +#define DEC560 559 +#define DEC559 558 +#define DEC558 557 +#define DEC557 556 +#define DEC556 555 +#define DEC555 554 +#define DEC554 553 +#define DEC553 552 +#define DEC552 551 +#define DEC551 550 +#define DEC550 549 +#define DEC549 548 +#define DEC548 547 +#define DEC547 546 +#define DEC546 545 +#define DEC545 544 +#define DEC544 543 +#define DEC543 542 +#define DEC542 541 +#define DEC541 540 +#define DEC540 539 +#define DEC539 538 +#define DEC538 537 +#define DEC537 536 +#define DEC536 535 +#define DEC535 534 +#define DEC534 533 +#define DEC533 532 +#define DEC532 531 +#define DEC531 530 +#define DEC530 529 +#define DEC529 528 +#define DEC528 527 +#define DEC527 526 +#define DEC526 525 +#define DEC525 524 +#define DEC524 523 +#define DEC523 522 +#define DEC522 521 +#define DEC521 520 +#define DEC520 519 +#define DEC519 518 +#define DEC518 517 +#define DEC517 516 +#define DEC516 515 +#define DEC515 514 +#define DEC514 513 +#define DEC513 512 +#define DEC512 511 +#define DEC511 510 +#define DEC510 509 +#define DEC509 508 +#define DEC508 507 +#define DEC507 506 +#define DEC506 505 +#define DEC505 504 +#define DEC504 503 +#define DEC503 502 +#define DEC502 501 +#define DEC501 500 +#define DEC500 499 +#define DEC499 498 +#define DEC498 497 +#define DEC497 496 +#define DEC496 495 +#define DEC495 494 +#define DEC494 493 +#define DEC493 492 +#define DEC492 491 +#define DEC491 490 +#define DEC490 489 +#define DEC489 488 +#define DEC488 487 +#define DEC487 486 +#define DEC486 485 +#define DEC485 484 +#define DEC484 483 +#define DEC483 482 +#define DEC482 481 +#define DEC481 480 +#define DEC480 479 +#define DEC479 478 +#define DEC478 477 +#define DEC477 476 +#define DEC476 475 +#define DEC475 474 +#define DEC474 473 +#define DEC473 472 +#define DEC472 471 +#define DEC471 470 +#define DEC470 469 +#define DEC469 468 +#define DEC468 467 +#define DEC467 466 +#define DEC466 465 +#define DEC465 464 +#define DEC464 463 +#define DEC463 462 +#define DEC462 461 +#define DEC461 460 +#define DEC460 459 +#define DEC459 458 +#define DEC458 457 +#define DEC457 456 +#define DEC456 455 +#define DEC455 454 +#define DEC454 453 +#define DEC453 452 +#define DEC452 451 +#define DEC451 450 +#define DEC450 449 +#define DEC449 448 +#define DEC448 447 +#define DEC447 446 +#define DEC446 445 +#define DEC445 444 +#define DEC444 443 +#define DEC443 442 +#define DEC442 441 +#define DEC441 440 +#define DEC440 439 +#define DEC439 438 +#define DEC438 437 +#define DEC437 436 +#define DEC436 435 +#define DEC435 434 +#define DEC434 433 +#define DEC433 432 +#define DEC432 431 +#define DEC431 430 +#define DEC430 429 +#define DEC429 428 +#define DEC428 427 +#define DEC427 426 +#define DEC426 425 +#define DEC425 424 +#define DEC424 423 +#define DEC423 422 +#define DEC422 421 +#define DEC421 420 +#define DEC420 419 +#define DEC419 418 +#define DEC418 417 +#define DEC417 416 +#define DEC416 415 +#define DEC415 414 +#define DEC414 413 +#define DEC413 412 +#define DEC412 411 +#define DEC411 410 +#define DEC410 409 +#define DEC409 408 +#define DEC408 407 +#define DEC407 406 +#define DEC406 405 +#define DEC405 404 +#define DEC404 403 +#define DEC403 402 +#define DEC402 401 +#define DEC401 400 +#define DEC400 399 +#define DEC399 398 +#define DEC398 397 +#define DEC397 396 +#define DEC396 395 +#define DEC395 394 +#define DEC394 393 +#define DEC393 392 +#define DEC392 391 +#define DEC391 390 +#define DEC390 389 +#define DEC389 388 +#define DEC388 387 +#define DEC387 386 +#define DEC386 385 +#define DEC385 384 +#define DEC384 383 +#define DEC383 382 +#define DEC382 381 +#define DEC381 380 +#define DEC380 379 +#define DEC379 378 +#define DEC378 377 +#define DEC377 376 +#define DEC376 375 +#define DEC375 374 +#define DEC374 373 +#define DEC373 372 +#define DEC372 371 +#define DEC371 370 +#define DEC370 369 +#define DEC369 368 +#define DEC368 367 +#define DEC367 366 +#define DEC366 365 +#define DEC365 364 +#define DEC364 363 +#define DEC363 362 +#define DEC362 361 +#define DEC361 360 +#define DEC360 359 +#define DEC359 358 +#define DEC358 357 +#define DEC357 356 +#define DEC356 355 +#define DEC355 354 +#define DEC354 353 +#define DEC353 352 +#define DEC352 351 +#define DEC351 350 +#define DEC350 349 +#define DEC349 348 +#define DEC348 347 +#define DEC347 346 +#define DEC346 345 +#define DEC345 344 +#define DEC344 343 +#define DEC343 342 +#define DEC342 341 +#define DEC341 340 +#define DEC340 339 +#define DEC339 338 +#define DEC338 337 +#define DEC337 336 +#define DEC336 335 +#define DEC335 334 +#define DEC334 333 +#define DEC333 332 +#define DEC332 331 +#define DEC331 330 +#define DEC330 329 +#define DEC329 328 +#define DEC328 327 +#define DEC327 326 +#define DEC326 325 +#define DEC325 324 +#define DEC324 323 +#define DEC323 322 +#define DEC322 321 +#define DEC321 320 +#define DEC320 319 +#define DEC319 318 +#define DEC318 317 +#define DEC317 316 +#define DEC316 315 +#define DEC315 314 +#define DEC314 313 +#define DEC313 312 +#define DEC312 311 +#define DEC311 310 +#define DEC310 309 +#define DEC309 308 +#define DEC308 307 +#define DEC307 306 +#define DEC306 305 +#define DEC305 304 +#define DEC304 303 +#define DEC303 302 +#define DEC302 301 +#define DEC301 300 +#define DEC300 299 +#define DEC299 298 +#define DEC298 297 +#define DEC297 296 +#define DEC296 295 +#define DEC295 294 +#define DEC294 293 +#define DEC293 292 +#define DEC292 291 +#define DEC291 290 +#define DEC290 289 +#define DEC289 288 +#define DEC288 287 +#define DEC287 286 +#define DEC286 285 +#define DEC285 284 +#define DEC284 283 +#define DEC283 282 +#define DEC282 281 +#define DEC281 280 +#define DEC280 279 +#define DEC279 278 +#define DEC278 277 +#define DEC277 276 +#define DEC276 275 +#define DEC275 274 +#define DEC274 273 +#define DEC273 272 +#define DEC272 271 +#define DEC271 270 +#define DEC270 269 +#define DEC269 268 +#define DEC268 267 +#define DEC267 266 +#define DEC266 265 +#define DEC265 264 +#define DEC264 263 +#define DEC263 262 +#define DEC262 261 +#define DEC261 260 +#define DEC260 259 +#define DEC259 258 +#define DEC258 257 +#define DEC257 256 +#define DEC256 255 +#define DEC255 254 +#define DEC254 253 +#define DEC253 252 +#define DEC252 251 +#define DEC251 250 +#define DEC250 249 +#define DEC249 248 +#define DEC248 247 +#define DEC247 246 +#define DEC246 245 +#define DEC245 244 +#define DEC244 243 +#define DEC243 242 +#define DEC242 241 +#define DEC241 240 +#define DEC240 239 +#define DEC239 238 +#define DEC238 237 +#define DEC237 236 +#define DEC236 235 +#define DEC235 234 +#define DEC234 233 +#define DEC233 232 +#define DEC232 231 +#define DEC231 230 +#define DEC230 229 +#define DEC229 228 +#define DEC228 227 +#define DEC227 226 +#define DEC226 225 +#define DEC225 224 +#define DEC224 223 +#define DEC223 222 +#define DEC222 221 +#define DEC221 220 +#define DEC220 219 +#define DEC219 218 +#define DEC218 217 +#define DEC217 216 +#define DEC216 215 +#define DEC215 214 +#define DEC214 213 +#define DEC213 212 +#define DEC212 211 +#define DEC211 210 +#define DEC210 209 +#define DEC209 208 +#define DEC208 207 +#define DEC207 206 +#define DEC206 205 +#define DEC205 204 +#define DEC204 203 +#define DEC203 202 +#define DEC202 201 +#define DEC201 200 +#define DEC200 199 +#define DEC199 198 +#define DEC198 197 +#define DEC197 196 +#define DEC196 195 +#define DEC195 194 +#define DEC194 193 +#define DEC193 192 +#define DEC192 191 +#define DEC191 190 +#define DEC190 189 +#define DEC189 188 +#define DEC188 187 +#define DEC187 186 +#define DEC186 185 +#define DEC185 184 +#define DEC184 183 +#define DEC183 182 +#define DEC182 181 +#define DEC181 180 +#define DEC180 179 +#define DEC179 178 +#define DEC178 177 +#define DEC177 176 +#define DEC176 175 +#define DEC175 174 +#define DEC174 173 +#define DEC173 172 +#define DEC172 171 +#define DEC171 170 +#define DEC170 169 +#define DEC169 168 +#define DEC168 167 +#define DEC167 166 +#define DEC166 165 +#define DEC165 164 +#define DEC164 163 +#define DEC163 162 +#define DEC162 161 +#define DEC161 160 +#define DEC160 159 +#define DEC159 158 +#define DEC158 157 +#define DEC157 156 +#define DEC156 155 +#define DEC155 154 +#define DEC154 153 +#define DEC153 152 +#define DEC152 151 +#define DEC151 150 +#define DEC150 149 +#define DEC149 148 +#define DEC148 147 +#define DEC147 146 +#define DEC146 145 +#define DEC145 144 +#define DEC144 143 +#define DEC143 142 +#define DEC142 141 +#define DEC141 140 +#define DEC140 139 +#define DEC139 138 +#define DEC138 137 +#define DEC137 136 +#define DEC136 135 +#define DEC135 134 +#define DEC134 133 +#define DEC133 132 +#define DEC132 131 +#define DEC131 130 +#define DEC130 129 +#define DEC129 128 +#define DEC128 127 +#define DEC127 126 +#define DEC126 125 +#define DEC125 124 +#define DEC124 123 +#define DEC123 122 +#define DEC122 121 +#define DEC121 120 +#define DEC120 119 +#define DEC119 118 +#define DEC118 117 +#define DEC117 116 +#define DEC116 115 +#define DEC115 114 +#define DEC114 113 +#define DEC113 112 +#define DEC112 111 +#define DEC111 110 +#define DEC110 109 +#define DEC109 108 +#define DEC108 107 +#define DEC107 106 +#define DEC106 105 +#define DEC105 104 +#define DEC104 103 +#define DEC103 102 +#define DEC102 101 +#define DEC101 100 +#define DEC100 99 +#define DEC99 98 +#define DEC98 97 +#define DEC97 96 +#define DEC96 95 +#define DEC95 94 +#define DEC94 93 +#define DEC93 92 +#define DEC92 91 +#define DEC91 90 +#define DEC90 89 +#define DEC89 88 +#define DEC88 87 +#define DEC87 86 +#define DEC86 85 +#define DEC85 84 +#define DEC84 83 +#define DEC83 82 +#define DEC82 81 +#define DEC81 80 +#define DEC80 79 +#define DEC79 78 +#define DEC78 77 +#define DEC77 76 +#define DEC76 75 +#define DEC75 74 +#define DEC74 73 +#define DEC73 72 +#define DEC72 71 +#define DEC71 70 +#define DEC70 69 +#define DEC69 68 +#define DEC68 67 +#define DEC67 66 +#define DEC66 65 +#define DEC65 64 +#define DEC64 63 +#define DEC63 62 +#define DEC62 61 +#define DEC61 60 +#define DEC60 59 +#define DEC59 58 +#define DEC58 57 +#define DEC57 56 +#define DEC56 55 +#define DEC55 54 +#define DEC54 53 +#define DEC53 52 +#define DEC52 51 +#define DEC51 50 +#define DEC50 49 +#define DEC49 48 +#define DEC48 47 +#define DEC47 46 +#define DEC46 45 +#define DEC45 44 +#define DEC44 43 +#define DEC43 42 +#define DEC42 41 +#define DEC41 40 +#define DEC40 39 +#define DEC39 38 +#define DEC38 37 +#define DEC37 36 +#define DEC36 35 +#define DEC35 34 +#define DEC34 33 +#define DEC33 32 +#define DEC32 31 +#define DEC31 30 +#define DEC30 29 +#define DEC29 28 +#define DEC28 27 +#define DEC27 26 +#define DEC26 25 +#define DEC25 24 +#define DEC24 23 +#define DEC23 22 +#define DEC22 21 +#define DEC21 20 +#define DEC20 19 +#define DEC19 18 +#define DEC18 17 +#define DEC17 16 +#define DEC16 15 +#define DEC15 14 +#define DEC14 13 +#define DEC13 12 +#define DEC12 11 +#define DEC11 10 +#define DEC10 9 +#define DEC9 8 +#define DEC8 7 +#define DEC7 6 +#define DEC6 5 +#define DEC5 4 +#define DEC4 3 +#define DEC3 2 +#define DEC2 1 +#define DEC1 0 + +#define INC(x) C2(INC,x) +#define INC1024 1025 +#define INC1023 1024 +#define INC1022 1023 +#define INC1021 1022 +#define INC1020 1021 +#define INC1019 1020 +#define INC1018 1019 +#define INC1017 1018 +#define INC1016 1017 +#define INC1015 1016 +#define INC1014 1015 +#define INC1013 1014 +#define INC1012 1013 +#define INC1011 1012 +#define INC1010 1011 +#define INC1009 1010 +#define INC1008 1009 +#define INC1007 1008 +#define INC1006 1007 +#define INC1005 1006 +#define INC1004 1005 +#define INC1003 1004 +#define INC1002 1003 +#define INC1001 1002 +#define INC1000 1001 +#define INC999 1000 +#define INC998 999 +#define INC997 998 +#define INC996 997 +#define INC995 996 +#define INC994 995 +#define INC993 994 +#define INC992 993 +#define INC991 992 +#define INC990 991 +#define INC989 990 +#define INC988 989 +#define INC987 988 +#define INC986 987 +#define INC985 986 +#define INC984 985 +#define INC983 984 +#define INC982 983 +#define INC981 982 +#define INC980 981 +#define INC979 980 +#define INC978 979 +#define INC977 978 +#define INC976 977 +#define INC975 976 +#define INC974 975 +#define INC973 974 +#define INC972 973 +#define INC971 972 +#define INC970 971 +#define INC969 970 +#define INC968 969 +#define INC967 968 +#define INC966 967 +#define INC965 966 +#define INC964 965 +#define INC963 964 +#define INC962 963 +#define INC961 962 +#define INC960 961 +#define INC959 960 +#define INC958 959 +#define INC957 958 +#define INC956 957 +#define INC955 956 +#define INC954 955 +#define INC953 954 +#define INC952 953 +#define INC951 952 +#define INC950 951 +#define INC949 950 +#define INC948 949 +#define INC947 948 +#define INC946 947 +#define INC945 946 +#define INC944 945 +#define INC943 944 +#define INC942 943 +#define INC941 942 +#define INC940 941 +#define INC939 940 +#define INC938 939 +#define INC937 938 +#define INC936 937 +#define INC935 936 +#define INC934 935 +#define INC933 934 +#define INC932 933 +#define INC931 932 +#define INC930 931 +#define INC929 930 +#define INC928 929 +#define INC927 928 +#define INC926 927 +#define INC925 926 +#define INC924 925 +#define INC923 924 +#define INC922 923 +#define INC921 922 +#define INC920 921 +#define INC919 920 +#define INC918 919 +#define INC917 918 +#define INC916 917 +#define INC915 916 +#define INC914 915 +#define INC913 914 +#define INC912 913 +#define INC911 912 +#define INC910 911 +#define INC909 910 +#define INC908 909 +#define INC907 908 +#define INC906 907 +#define INC905 906 +#define INC904 905 +#define INC903 904 +#define INC902 903 +#define INC901 902 +#define INC900 901 +#define INC899 900 +#define INC898 899 +#define INC897 898 +#define INC896 897 +#define INC895 896 +#define INC894 895 +#define INC893 894 +#define INC892 893 +#define INC891 892 +#define INC890 891 +#define INC889 890 +#define INC888 889 +#define INC887 888 +#define INC886 887 +#define INC885 886 +#define INC884 885 +#define INC883 884 +#define INC882 883 +#define INC881 882 +#define INC880 881 +#define INC879 880 +#define INC878 879 +#define INC877 878 +#define INC876 877 +#define INC875 876 +#define INC874 875 +#define INC873 874 +#define INC872 873 +#define INC871 872 +#define INC870 871 +#define INC869 870 +#define INC868 869 +#define INC867 868 +#define INC866 867 +#define INC865 866 +#define INC864 865 +#define INC863 864 +#define INC862 863 +#define INC861 862 +#define INC860 861 +#define INC859 860 +#define INC858 859 +#define INC857 858 +#define INC856 857 +#define INC855 856 +#define INC854 855 +#define INC853 854 +#define INC852 853 +#define INC851 852 +#define INC850 851 +#define INC849 850 +#define INC848 849 +#define INC847 848 +#define INC846 847 +#define INC845 846 +#define INC844 845 +#define INC843 844 +#define INC842 843 +#define INC841 842 +#define INC840 841 +#define INC839 840 +#define INC838 839 +#define INC837 838 +#define INC836 837 +#define INC835 836 +#define INC834 835 +#define INC833 834 +#define INC832 833 +#define INC831 832 +#define INC830 831 +#define INC829 830 +#define INC828 829 +#define INC827 828 +#define INC826 827 +#define INC825 826 +#define INC824 825 +#define INC823 824 +#define INC822 823 +#define INC821 822 +#define INC820 821 +#define INC819 820 +#define INC818 819 +#define INC817 818 +#define INC816 817 +#define INC815 816 +#define INC814 815 +#define INC813 814 +#define INC812 813 +#define INC811 812 +#define INC810 811 +#define INC809 810 +#define INC808 809 +#define INC807 808 +#define INC806 807 +#define INC805 806 +#define INC804 805 +#define INC803 804 +#define INC802 803 +#define INC801 802 +#define INC800 801 +#define INC799 800 +#define INC798 799 +#define INC797 798 +#define INC796 797 +#define INC795 796 +#define INC794 795 +#define INC793 794 +#define INC792 793 +#define INC791 792 +#define INC790 791 +#define INC789 790 +#define INC788 789 +#define INC787 788 +#define INC786 787 +#define INC785 786 +#define INC784 785 +#define INC783 784 +#define INC782 783 +#define INC781 782 +#define INC780 781 +#define INC779 780 +#define INC778 779 +#define INC777 778 +#define INC776 777 +#define INC775 776 +#define INC774 775 +#define INC773 774 +#define INC772 773 +#define INC771 772 +#define INC770 771 +#define INC769 770 +#define INC768 769 +#define INC767 768 +#define INC766 767 +#define INC765 766 +#define INC764 765 +#define INC763 764 +#define INC762 763 +#define INC761 762 +#define INC760 761 +#define INC759 760 +#define INC758 759 +#define INC757 758 +#define INC756 757 +#define INC755 756 +#define INC754 755 +#define INC753 754 +#define INC752 753 +#define INC751 752 +#define INC750 751 +#define INC749 750 +#define INC748 749 +#define INC747 748 +#define INC746 747 +#define INC745 746 +#define INC744 745 +#define INC743 744 +#define INC742 743 +#define INC741 742 +#define INC740 741 +#define INC739 740 +#define INC738 739 +#define INC737 738 +#define INC736 737 +#define INC735 736 +#define INC734 735 +#define INC733 734 +#define INC732 733 +#define INC731 732 +#define INC730 731 +#define INC729 730 +#define INC728 729 +#define INC727 728 +#define INC726 727 +#define INC725 726 +#define INC724 725 +#define INC723 724 +#define INC722 723 +#define INC721 722 +#define INC720 721 +#define INC719 720 +#define INC718 719 +#define INC717 718 +#define INC716 717 +#define INC715 716 +#define INC714 715 +#define INC713 714 +#define INC712 713 +#define INC711 712 +#define INC710 711 +#define INC709 710 +#define INC708 709 +#define INC707 708 +#define INC706 707 +#define INC705 706 +#define INC704 705 +#define INC703 704 +#define INC702 703 +#define INC701 702 +#define INC700 701 +#define INC699 700 +#define INC698 699 +#define INC697 698 +#define INC696 697 +#define INC695 696 +#define INC694 695 +#define INC693 694 +#define INC692 693 +#define INC691 692 +#define INC690 691 +#define INC689 690 +#define INC688 689 +#define INC687 688 +#define INC686 687 +#define INC685 686 +#define INC684 685 +#define INC683 684 +#define INC682 683 +#define INC681 682 +#define INC680 681 +#define INC679 680 +#define INC678 679 +#define INC677 678 +#define INC676 677 +#define INC675 676 +#define INC674 675 +#define INC673 674 +#define INC672 673 +#define INC671 672 +#define INC670 671 +#define INC669 670 +#define INC668 669 +#define INC667 668 +#define INC666 667 +#define INC665 666 +#define INC664 665 +#define INC663 664 +#define INC662 663 +#define INC661 662 +#define INC660 661 +#define INC659 660 +#define INC658 659 +#define INC657 658 +#define INC656 657 +#define INC655 656 +#define INC654 655 +#define INC653 654 +#define INC652 653 +#define INC651 652 +#define INC650 651 +#define INC649 650 +#define INC648 649 +#define INC647 648 +#define INC646 647 +#define INC645 646 +#define INC644 645 +#define INC643 644 +#define INC642 643 +#define INC641 642 +#define INC640 641 +#define INC639 640 +#define INC638 639 +#define INC637 638 +#define INC636 637 +#define INC635 636 +#define INC634 635 +#define INC633 634 +#define INC632 633 +#define INC631 632 +#define INC630 631 +#define INC629 630 +#define INC628 629 +#define INC627 628 +#define INC626 627 +#define INC625 626 +#define INC624 625 +#define INC623 624 +#define INC622 623 +#define INC621 622 +#define INC620 621 +#define INC619 620 +#define INC618 619 +#define INC617 618 +#define INC616 617 +#define INC615 616 +#define INC614 615 +#define INC613 614 +#define INC612 613 +#define INC611 612 +#define INC610 611 +#define INC609 610 +#define INC608 609 +#define INC607 608 +#define INC606 607 +#define INC605 606 +#define INC604 605 +#define INC603 604 +#define INC602 603 +#define INC601 602 +#define INC600 601 +#define INC599 600 +#define INC598 599 +#define INC597 598 +#define INC596 597 +#define INC595 596 +#define INC594 595 +#define INC593 594 +#define INC592 593 +#define INC591 592 +#define INC590 591 +#define INC589 590 +#define INC588 589 +#define INC587 588 +#define INC586 587 +#define INC585 586 +#define INC584 585 +#define INC583 584 +#define INC582 583 +#define INC581 582 +#define INC580 581 +#define INC579 580 +#define INC578 579 +#define INC577 578 +#define INC576 577 +#define INC575 576 +#define INC574 575 +#define INC573 574 +#define INC572 573 +#define INC571 572 +#define INC570 571 +#define INC569 570 +#define INC568 569 +#define INC567 568 +#define INC566 567 +#define INC565 566 +#define INC564 565 +#define INC563 564 +#define INC562 563 +#define INC561 562 +#define INC560 561 +#define INC559 560 +#define INC558 559 +#define INC557 558 +#define INC556 557 +#define INC555 556 +#define INC554 555 +#define INC553 554 +#define INC552 553 +#define INC551 552 +#define INC550 551 +#define INC549 550 +#define INC548 549 +#define INC547 548 +#define INC546 547 +#define INC545 546 +#define INC544 545 +#define INC543 544 +#define INC542 543 +#define INC541 542 +#define INC540 541 +#define INC539 540 +#define INC538 539 +#define INC537 538 +#define INC536 537 +#define INC535 536 +#define INC534 535 +#define INC533 534 +#define INC532 533 +#define INC531 532 +#define INC530 531 +#define INC529 530 +#define INC528 529 +#define INC527 528 +#define INC526 527 +#define INC525 526 +#define INC524 525 +#define INC523 524 +#define INC522 523 +#define INC521 522 +#define INC520 521 +#define INC519 520 +#define INC518 519 +#define INC517 518 +#define INC516 517 +#define INC515 516 +#define INC514 515 +#define INC513 514 +#define INC512 513 +#define INC511 512 +#define INC510 511 +#define INC509 510 +#define INC508 509 +#define INC507 508 +#define INC506 507 +#define INC505 506 +#define INC504 505 +#define INC503 504 +#define INC502 503 +#define INC501 502 +#define INC500 501 +#define INC499 500 +#define INC498 499 +#define INC497 498 +#define INC496 497 +#define INC495 496 +#define INC494 495 +#define INC493 494 +#define INC492 493 +#define INC491 492 +#define INC490 491 +#define INC489 490 +#define INC488 489 +#define INC487 488 +#define INC486 487 +#define INC485 486 +#define INC484 485 +#define INC483 484 +#define INC482 483 +#define INC481 482 +#define INC480 481 +#define INC479 480 +#define INC478 479 +#define INC477 478 +#define INC476 477 +#define INC475 476 +#define INC474 475 +#define INC473 474 +#define INC472 473 +#define INC471 472 +#define INC470 471 +#define INC469 470 +#define INC468 469 +#define INC467 468 +#define INC466 467 +#define INC465 466 +#define INC464 465 +#define INC463 464 +#define INC462 463 +#define INC461 462 +#define INC460 461 +#define INC459 460 +#define INC458 459 +#define INC457 458 +#define INC456 457 +#define INC455 456 +#define INC454 455 +#define INC453 454 +#define INC452 453 +#define INC451 452 +#define INC450 451 +#define INC449 450 +#define INC448 449 +#define INC447 448 +#define INC446 447 +#define INC445 446 +#define INC444 445 +#define INC443 444 +#define INC442 443 +#define INC441 442 +#define INC440 441 +#define INC439 440 +#define INC438 439 +#define INC437 438 +#define INC436 437 +#define INC435 436 +#define INC434 435 +#define INC433 434 +#define INC432 433 +#define INC431 432 +#define INC430 431 +#define INC429 430 +#define INC428 429 +#define INC427 428 +#define INC426 427 +#define INC425 426 +#define INC424 425 +#define INC423 424 +#define INC422 423 +#define INC421 422 +#define INC420 421 +#define INC419 420 +#define INC418 419 +#define INC417 418 +#define INC416 417 +#define INC415 416 +#define INC414 415 +#define INC413 414 +#define INC412 413 +#define INC411 412 +#define INC410 411 +#define INC409 410 +#define INC408 409 +#define INC407 408 +#define INC406 407 +#define INC405 406 +#define INC404 405 +#define INC403 404 +#define INC402 403 +#define INC401 402 +#define INC400 401 +#define INC399 400 +#define INC398 399 +#define INC397 398 +#define INC396 397 +#define INC395 396 +#define INC394 395 +#define INC393 394 +#define INC392 393 +#define INC391 392 +#define INC390 391 +#define INC389 390 +#define INC388 389 +#define INC387 388 +#define INC386 387 +#define INC385 386 +#define INC384 385 +#define INC383 384 +#define INC382 383 +#define INC381 382 +#define INC380 381 +#define INC379 380 +#define INC378 379 +#define INC377 378 +#define INC376 377 +#define INC375 376 +#define INC374 375 +#define INC373 374 +#define INC372 373 +#define INC371 372 +#define INC370 371 +#define INC369 370 +#define INC368 369 +#define INC367 368 +#define INC366 367 +#define INC365 366 +#define INC364 365 +#define INC363 364 +#define INC362 363 +#define INC361 362 +#define INC360 361 +#define INC359 360 +#define INC358 359 +#define INC357 358 +#define INC356 357 +#define INC355 356 +#define INC354 355 +#define INC353 354 +#define INC352 353 +#define INC351 352 +#define INC350 351 +#define INC349 350 +#define INC348 349 +#define INC347 348 +#define INC346 347 +#define INC345 346 +#define INC344 345 +#define INC343 344 +#define INC342 343 +#define INC341 342 +#define INC340 341 +#define INC339 340 +#define INC338 339 +#define INC337 338 +#define INC336 337 +#define INC335 336 +#define INC334 335 +#define INC333 334 +#define INC332 333 +#define INC331 332 +#define INC330 331 +#define INC329 330 +#define INC328 329 +#define INC327 328 +#define INC326 327 +#define INC325 326 +#define INC324 325 +#define INC323 324 +#define INC322 323 +#define INC321 322 +#define INC320 321 +#define INC319 320 +#define INC318 319 +#define INC317 318 +#define INC316 317 +#define INC315 316 +#define INC314 315 +#define INC313 314 +#define INC312 313 +#define INC311 312 +#define INC310 311 +#define INC309 310 +#define INC308 309 +#define INC307 308 +#define INC306 307 +#define INC305 306 +#define INC304 305 +#define INC303 304 +#define INC302 303 +#define INC301 302 +#define INC300 301 +#define INC299 300 +#define INC298 299 +#define INC297 298 +#define INC296 297 +#define INC295 296 +#define INC294 295 +#define INC293 294 +#define INC292 293 +#define INC291 292 +#define INC290 291 +#define INC289 290 +#define INC288 289 +#define INC287 288 +#define INC286 287 +#define INC285 286 +#define INC284 285 +#define INC283 284 +#define INC282 283 +#define INC281 282 +#define INC280 281 +#define INC279 280 +#define INC278 279 +#define INC277 278 +#define INC276 277 +#define INC275 276 +#define INC274 275 +#define INC273 274 +#define INC272 273 +#define INC271 272 +#define INC270 271 +#define INC269 270 +#define INC268 269 +#define INC267 268 +#define INC266 267 +#define INC265 266 +#define INC264 265 +#define INC263 264 +#define INC262 263 +#define INC261 262 +#define INC260 261 +#define INC259 260 +#define INC258 259 +#define INC257 258 +#define INC256 257 +#define INC255 256 +#define INC254 255 +#define INC253 254 +#define INC252 253 +#define INC251 252 +#define INC250 251 +#define INC249 250 +#define INC248 249 +#define INC247 248 +#define INC246 247 +#define INC245 246 +#define INC244 245 +#define INC243 244 +#define INC242 243 +#define INC241 242 +#define INC240 241 +#define INC239 240 +#define INC238 239 +#define INC237 238 +#define INC236 237 +#define INC235 236 +#define INC234 235 +#define INC233 234 +#define INC232 233 +#define INC231 232 +#define INC230 231 +#define INC229 230 +#define INC228 229 +#define INC227 228 +#define INC226 227 +#define INC225 226 +#define INC224 225 +#define INC223 224 +#define INC222 223 +#define INC221 222 +#define INC220 221 +#define INC219 220 +#define INC218 219 +#define INC217 218 +#define INC216 217 +#define INC215 216 +#define INC214 215 +#define INC213 214 +#define INC212 213 +#define INC211 212 +#define INC210 211 +#define INC209 210 +#define INC208 209 +#define INC207 208 +#define INC206 207 +#define INC205 206 +#define INC204 205 +#define INC203 204 +#define INC202 203 +#define INC201 202 +#define INC200 201 +#define INC199 200 +#define INC198 199 +#define INC197 198 +#define INC196 197 +#define INC195 196 +#define INC194 195 +#define INC193 194 +#define INC192 193 +#define INC191 192 +#define INC190 191 +#define INC189 190 +#define INC188 189 +#define INC187 188 +#define INC186 187 +#define INC185 186 +#define INC184 185 +#define INC183 184 +#define INC182 183 +#define INC181 182 +#define INC180 181 +#define INC179 180 +#define INC178 179 +#define INC177 178 +#define INC176 177 +#define INC175 176 +#define INC174 175 +#define INC173 174 +#define INC172 173 +#define INC171 172 +#define INC170 171 +#define INC169 170 +#define INC168 169 +#define INC167 168 +#define INC166 167 +#define INC165 166 +#define INC164 165 +#define INC163 164 +#define INC162 163 +#define INC161 162 +#define INC160 161 +#define INC159 160 +#define INC158 159 +#define INC157 158 +#define INC156 157 +#define INC155 156 +#define INC154 155 +#define INC153 154 +#define INC152 153 +#define INC151 152 +#define INC150 151 +#define INC149 150 +#define INC148 149 +#define INC147 148 +#define INC146 147 +#define INC145 146 +#define INC144 145 +#define INC143 144 +#define INC142 143 +#define INC141 142 +#define INC140 141 +#define INC139 140 +#define INC138 139 +#define INC137 138 +#define INC136 137 +#define INC135 136 +#define INC134 135 +#define INC133 134 +#define INC132 133 +#define INC131 132 +#define INC130 131 +#define INC129 130 +#define INC128 129 +#define INC127 128 +#define INC126 127 +#define INC125 126 +#define INC124 125 +#define INC123 124 +#define INC122 123 +#define INC121 122 +#define INC120 121 +#define INC119 120 +#define INC118 119 +#define INC117 118 +#define INC116 117 +#define INC115 116 +#define INC114 115 +#define INC113 114 +#define INC112 113 +#define INC111 112 +#define INC110 111 +#define INC109 110 +#define INC108 109 +#define INC107 108 +#define INC106 107 +#define INC105 106 +#define INC104 105 +#define INC103 104 +#define INC102 103 +#define INC101 102 +#define INC100 101 +#define INC99 100 +#define INC98 99 +#define INC97 98 +#define INC96 97 +#define INC95 96 +#define INC94 95 +#define INC93 94 +#define INC92 93 +#define INC91 92 +#define INC90 91 +#define INC89 90 +#define INC88 89 +#define INC87 88 +#define INC86 87 +#define INC85 86 +#define INC84 85 +#define INC83 84 +#define INC82 83 +#define INC81 82 +#define INC80 81 +#define INC79 80 +#define INC78 79 +#define INC77 78 +#define INC76 77 +#define INC75 76 +#define INC74 75 +#define INC73 74 +#define INC72 73 +#define INC71 72 +#define INC70 71 +#define INC69 70 +#define INC68 69 +#define INC67 68 +#define INC66 67 +#define INC65 66 +#define INC64 65 +#define INC63 64 +#define INC62 63 +#define INC61 62 +#define INC60 61 +#define INC59 60 +#define INC58 59 +#define INC57 58 +#define INC56 57 +#define INC55 56 +#define INC54 55 +#define INC53 54 +#define INC52 53 +#define INC51 52 +#define INC50 51 +#define INC49 50 +#define INC48 49 +#define INC47 48 +#define INC46 47 +#define INC45 46 +#define INC44 45 +#define INC43 44 +#define INC42 43 +#define INC41 42 +#define INC40 41 +#define INC39 40 +#define INC38 39 +#define INC37 38 +#define INC36 37 +#define INC35 36 +#define INC34 35 +#define INC33 34 +#define INC32 33 +#define INC31 32 +#define INC30 31 +#define INC29 30 +#define INC28 29 +#define INC27 28 +#define INC26 27 +#define INC25 26 +#define INC24 25 +#define INC23 24 +#define INC22 23 +#define INC21 22 +#define INC20 21 +#define INC19 20 +#define INC18 19 +#define INC17 18 +#define INC16 17 +#define INC15 16 +#define INC14 15 +#define INC13 14 +#define INC12 13 +#define INC11 12 +#define INC10 11 +#define INC9 10 +#define INC8 9 +#define INC7 8 +#define INC6 7 +#define INC5 6 +#define INC4 5 +#define INC3 4 +#define INC2 3 +#define INC1 2 +#define INC0 1 + +#define DIV2(x) C2(DIV2_,x) + +#define DIV2_1024 512 +#define DIV2_1023 511 +#define DIV2_1022 511 +#define DIV2_1021 510 +#define DIV2_1020 510 +#define DIV2_1019 509 +#define DIV2_1018 509 +#define DIV2_1017 508 +#define DIV2_1016 508 +#define DIV2_1015 507 +#define DIV2_1014 507 +#define DIV2_1013 506 +#define DIV2_1012 506 +#define DIV2_1011 505 +#define DIV2_1010 505 +#define DIV2_1009 504 +#define DIV2_1008 504 +#define DIV2_1007 503 +#define DIV2_1006 503 +#define DIV2_1005 502 +#define DIV2_1004 502 +#define DIV2_1003 501 +#define DIV2_1002 501 +#define DIV2_1001 500 +#define DIV2_1000 500 +#define DIV2_999 499 +#define DIV2_998 499 +#define DIV2_997 498 +#define DIV2_996 498 +#define DIV2_995 497 +#define DIV2_994 497 +#define DIV2_993 496 +#define DIV2_992 496 +#define DIV2_991 495 +#define DIV2_990 495 +#define DIV2_989 494 +#define DIV2_988 494 +#define DIV2_987 493 +#define DIV2_986 493 +#define DIV2_985 492 +#define DIV2_984 492 +#define DIV2_983 491 +#define DIV2_982 491 +#define DIV2_981 490 +#define DIV2_980 490 +#define DIV2_979 489 +#define DIV2_978 489 +#define DIV2_977 488 +#define DIV2_976 488 +#define DIV2_975 487 +#define DIV2_974 487 +#define DIV2_973 486 +#define DIV2_972 486 +#define DIV2_971 485 +#define DIV2_970 485 +#define DIV2_969 484 +#define DIV2_968 484 +#define DIV2_967 483 +#define DIV2_966 483 +#define DIV2_965 482 +#define DIV2_964 482 +#define DIV2_963 481 +#define DIV2_962 481 +#define DIV2_961 480 +#define DIV2_960 480 +#define DIV2_959 479 +#define DIV2_958 479 +#define DIV2_957 478 +#define DIV2_956 478 +#define DIV2_955 477 +#define DIV2_954 477 +#define DIV2_953 476 +#define DIV2_952 476 +#define DIV2_951 475 +#define DIV2_950 475 +#define DIV2_949 474 +#define DIV2_948 474 +#define DIV2_947 473 +#define DIV2_946 473 +#define DIV2_945 472 +#define DIV2_944 472 +#define DIV2_943 471 +#define DIV2_942 471 +#define DIV2_941 470 +#define DIV2_940 470 +#define DIV2_939 469 +#define DIV2_938 469 +#define DIV2_937 468 +#define DIV2_936 468 +#define DIV2_935 467 +#define DIV2_934 467 +#define DIV2_933 466 +#define DIV2_932 466 +#define DIV2_931 465 +#define DIV2_930 465 +#define DIV2_929 464 +#define DIV2_928 464 +#define DIV2_927 463 +#define DIV2_926 463 +#define DIV2_925 462 +#define DIV2_924 462 +#define DIV2_923 461 +#define DIV2_922 461 +#define DIV2_921 460 +#define DIV2_920 460 +#define DIV2_919 459 +#define DIV2_918 459 +#define DIV2_917 458 +#define DIV2_916 458 +#define DIV2_915 457 +#define DIV2_914 457 +#define DIV2_913 456 +#define DIV2_912 456 +#define DIV2_911 455 +#define DIV2_910 455 +#define DIV2_909 454 +#define DIV2_908 454 +#define DIV2_907 453 +#define DIV2_906 453 +#define DIV2_905 452 +#define DIV2_904 452 +#define DIV2_903 451 +#define DIV2_902 451 +#define DIV2_901 450 +#define DIV2_900 450 +#define DIV2_899 449 +#define DIV2_898 449 +#define DIV2_897 448 +#define DIV2_896 448 +#define DIV2_895 447 +#define DIV2_894 447 +#define DIV2_893 446 +#define DIV2_892 446 +#define DIV2_891 445 +#define DIV2_890 445 +#define DIV2_889 444 +#define DIV2_888 444 +#define DIV2_887 443 +#define DIV2_886 443 +#define DIV2_885 442 +#define DIV2_884 442 +#define DIV2_883 441 +#define DIV2_882 441 +#define DIV2_881 440 +#define DIV2_880 440 +#define DIV2_879 439 +#define DIV2_878 439 +#define DIV2_877 438 +#define DIV2_876 438 +#define DIV2_875 437 +#define DIV2_874 437 +#define DIV2_873 436 +#define DIV2_872 436 +#define DIV2_871 435 +#define DIV2_870 435 +#define DIV2_869 434 +#define DIV2_868 434 +#define DIV2_867 433 +#define DIV2_866 433 +#define DIV2_865 432 +#define DIV2_864 432 +#define DIV2_863 431 +#define DIV2_862 431 +#define DIV2_861 430 +#define DIV2_860 430 +#define DIV2_859 429 +#define DIV2_858 429 +#define DIV2_857 428 +#define DIV2_856 428 +#define DIV2_855 427 +#define DIV2_854 427 +#define DIV2_853 426 +#define DIV2_852 426 +#define DIV2_851 425 +#define DIV2_850 425 +#define DIV2_849 424 +#define DIV2_848 424 +#define DIV2_847 423 +#define DIV2_846 423 +#define DIV2_845 422 +#define DIV2_844 422 +#define DIV2_843 421 +#define DIV2_842 421 +#define DIV2_841 420 +#define DIV2_840 420 +#define DIV2_839 419 +#define DIV2_838 419 +#define DIV2_837 418 +#define DIV2_836 418 +#define DIV2_835 417 +#define DIV2_834 417 +#define DIV2_833 416 +#define DIV2_832 416 +#define DIV2_831 415 +#define DIV2_830 415 +#define DIV2_829 414 +#define DIV2_828 414 +#define DIV2_827 413 +#define DIV2_826 413 +#define DIV2_825 412 +#define DIV2_824 412 +#define DIV2_823 411 +#define DIV2_822 411 +#define DIV2_821 410 +#define DIV2_820 410 +#define DIV2_819 409 +#define DIV2_818 409 +#define DIV2_817 408 +#define DIV2_816 408 +#define DIV2_815 407 +#define DIV2_814 407 +#define DIV2_813 406 +#define DIV2_812 406 +#define DIV2_811 405 +#define DIV2_810 405 +#define DIV2_809 404 +#define DIV2_808 404 +#define DIV2_807 403 +#define DIV2_806 403 +#define DIV2_805 402 +#define DIV2_804 402 +#define DIV2_803 401 +#define DIV2_802 401 +#define DIV2_801 400 +#define DIV2_800 400 +#define DIV2_799 399 +#define DIV2_798 399 +#define DIV2_797 398 +#define DIV2_796 398 +#define DIV2_795 397 +#define DIV2_794 397 +#define DIV2_793 396 +#define DIV2_792 396 +#define DIV2_791 395 +#define DIV2_790 395 +#define DIV2_789 394 +#define DIV2_788 394 +#define DIV2_787 393 +#define DIV2_786 393 +#define DIV2_785 392 +#define DIV2_784 392 +#define DIV2_783 391 +#define DIV2_782 391 +#define DIV2_781 390 +#define DIV2_780 390 +#define DIV2_779 389 +#define DIV2_778 389 +#define DIV2_777 388 +#define DIV2_776 388 +#define DIV2_775 387 +#define DIV2_774 387 +#define DIV2_773 386 +#define DIV2_772 386 +#define DIV2_771 385 +#define DIV2_770 385 +#define DIV2_769 384 +#define DIV2_768 384 +#define DIV2_767 383 +#define DIV2_766 383 +#define DIV2_765 382 +#define DIV2_764 382 +#define DIV2_763 381 +#define DIV2_762 381 +#define DIV2_761 380 +#define DIV2_760 380 +#define DIV2_759 379 +#define DIV2_758 379 +#define DIV2_757 378 +#define DIV2_756 378 +#define DIV2_755 377 +#define DIV2_754 377 +#define DIV2_753 376 +#define DIV2_752 376 +#define DIV2_751 375 +#define DIV2_750 375 +#define DIV2_749 374 +#define DIV2_748 374 +#define DIV2_747 373 +#define DIV2_746 373 +#define DIV2_745 372 +#define DIV2_744 372 +#define DIV2_743 371 +#define DIV2_742 371 +#define DIV2_741 370 +#define DIV2_740 370 +#define DIV2_739 369 +#define DIV2_738 369 +#define DIV2_737 368 +#define DIV2_736 368 +#define DIV2_735 367 +#define DIV2_734 367 +#define DIV2_733 366 +#define DIV2_732 366 +#define DIV2_731 365 +#define DIV2_730 365 +#define DIV2_729 364 +#define DIV2_728 364 +#define DIV2_727 363 +#define DIV2_726 363 +#define DIV2_725 362 +#define DIV2_724 362 +#define DIV2_723 361 +#define DIV2_722 361 +#define DIV2_721 360 +#define DIV2_720 360 +#define DIV2_719 359 +#define DIV2_718 359 +#define DIV2_717 358 +#define DIV2_716 358 +#define DIV2_715 357 +#define DIV2_714 357 +#define DIV2_713 356 +#define DIV2_712 356 +#define DIV2_711 355 +#define DIV2_710 355 +#define DIV2_709 354 +#define DIV2_708 354 +#define DIV2_707 353 +#define DIV2_706 353 +#define DIV2_705 352 +#define DIV2_704 352 +#define DIV2_703 351 +#define DIV2_702 351 +#define DIV2_701 350 +#define DIV2_700 350 +#define DIV2_699 349 +#define DIV2_698 349 +#define DIV2_697 348 +#define DIV2_696 348 +#define DIV2_695 347 +#define DIV2_694 347 +#define DIV2_693 346 +#define DIV2_692 346 +#define DIV2_691 345 +#define DIV2_690 345 +#define DIV2_689 344 +#define DIV2_688 344 +#define DIV2_687 343 +#define DIV2_686 343 +#define DIV2_685 342 +#define DIV2_684 342 +#define DIV2_683 341 +#define DIV2_682 341 +#define DIV2_681 340 +#define DIV2_680 340 +#define DIV2_679 339 +#define DIV2_678 339 +#define DIV2_677 338 +#define DIV2_676 338 +#define DIV2_675 337 +#define DIV2_674 337 +#define DIV2_673 336 +#define DIV2_672 336 +#define DIV2_671 335 +#define DIV2_670 335 +#define DIV2_669 334 +#define DIV2_668 334 +#define DIV2_667 333 +#define DIV2_666 333 +#define DIV2_665 332 +#define DIV2_664 332 +#define DIV2_663 331 +#define DIV2_662 331 +#define DIV2_661 330 +#define DIV2_660 330 +#define DIV2_659 329 +#define DIV2_658 329 +#define DIV2_657 328 +#define DIV2_656 328 +#define DIV2_655 327 +#define DIV2_654 327 +#define DIV2_653 326 +#define DIV2_652 326 +#define DIV2_651 325 +#define DIV2_650 325 +#define DIV2_649 324 +#define DIV2_648 324 +#define DIV2_647 323 +#define DIV2_646 323 +#define DIV2_645 322 +#define DIV2_644 322 +#define DIV2_643 321 +#define DIV2_642 321 +#define DIV2_641 320 +#define DIV2_640 320 +#define DIV2_639 319 +#define DIV2_638 319 +#define DIV2_637 318 +#define DIV2_636 318 +#define DIV2_635 317 +#define DIV2_634 317 +#define DIV2_633 316 +#define DIV2_632 316 +#define DIV2_631 315 +#define DIV2_630 315 +#define DIV2_629 314 +#define DIV2_628 314 +#define DIV2_627 313 +#define DIV2_626 313 +#define DIV2_625 312 +#define DIV2_624 312 +#define DIV2_623 311 +#define DIV2_622 311 +#define DIV2_621 310 +#define DIV2_620 310 +#define DIV2_619 309 +#define DIV2_618 309 +#define DIV2_617 308 +#define DIV2_616 308 +#define DIV2_615 307 +#define DIV2_614 307 +#define DIV2_613 306 +#define DIV2_612 306 +#define DIV2_611 305 +#define DIV2_610 305 +#define DIV2_609 304 +#define DIV2_608 304 +#define DIV2_607 303 +#define DIV2_606 303 +#define DIV2_605 302 +#define DIV2_604 302 +#define DIV2_603 301 +#define DIV2_602 301 +#define DIV2_601 300 +#define DIV2_600 300 +#define DIV2_599 299 +#define DIV2_598 299 +#define DIV2_597 298 +#define DIV2_596 298 +#define DIV2_595 297 +#define DIV2_594 297 +#define DIV2_593 296 +#define DIV2_592 296 +#define DIV2_591 295 +#define DIV2_590 295 +#define DIV2_589 294 +#define DIV2_588 294 +#define DIV2_587 293 +#define DIV2_586 293 +#define DIV2_585 292 +#define DIV2_584 292 +#define DIV2_583 291 +#define DIV2_582 291 +#define DIV2_581 290 +#define DIV2_580 290 +#define DIV2_579 289 +#define DIV2_578 289 +#define DIV2_577 288 +#define DIV2_576 288 +#define DIV2_575 287 +#define DIV2_574 287 +#define DIV2_573 286 +#define DIV2_572 286 +#define DIV2_571 285 +#define DIV2_570 285 +#define DIV2_569 284 +#define DIV2_568 284 +#define DIV2_567 283 +#define DIV2_566 283 +#define DIV2_565 282 +#define DIV2_564 282 +#define DIV2_563 281 +#define DIV2_562 281 +#define DIV2_561 280 +#define DIV2_560 280 +#define DIV2_559 279 +#define DIV2_558 279 +#define DIV2_557 278 +#define DIV2_556 278 +#define DIV2_555 277 +#define DIV2_554 277 +#define DIV2_553 276 +#define DIV2_552 276 +#define DIV2_551 275 +#define DIV2_550 275 +#define DIV2_549 274 +#define DIV2_548 274 +#define DIV2_547 273 +#define DIV2_546 273 +#define DIV2_545 272 +#define DIV2_544 272 +#define DIV2_543 271 +#define DIV2_542 271 +#define DIV2_541 270 +#define DIV2_540 270 +#define DIV2_539 269 +#define DIV2_538 269 +#define DIV2_537 268 +#define DIV2_536 268 +#define DIV2_535 267 +#define DIV2_534 267 +#define DIV2_533 266 +#define DIV2_532 266 +#define DIV2_531 265 +#define DIV2_530 265 +#define DIV2_529 264 +#define DIV2_528 264 +#define DIV2_527 263 +#define DIV2_526 263 +#define DIV2_525 262 +#define DIV2_524 262 +#define DIV2_523 261 +#define DIV2_522 261 +#define DIV2_521 260 +#define DIV2_520 260 +#define DIV2_519 259 +#define DIV2_518 259 +#define DIV2_517 258 +#define DIV2_516 258 +#define DIV2_515 257 +#define DIV2_514 257 +#define DIV2_513 256 +#define DIV2_512 256 +#define DIV2_511 255 +#define DIV2_510 255 +#define DIV2_509 254 +#define DIV2_508 254 +#define DIV2_507 253 +#define DIV2_506 253 +#define DIV2_505 252 +#define DIV2_504 252 +#define DIV2_503 251 +#define DIV2_502 251 +#define DIV2_501 250 +#define DIV2_500 250 +#define DIV2_499 249 +#define DIV2_498 249 +#define DIV2_497 248 +#define DIV2_496 248 +#define DIV2_495 247 +#define DIV2_494 247 +#define DIV2_493 246 +#define DIV2_492 246 +#define DIV2_491 245 +#define DIV2_490 245 +#define DIV2_489 244 +#define DIV2_488 244 +#define DIV2_487 243 +#define DIV2_486 243 +#define DIV2_485 242 +#define DIV2_484 242 +#define DIV2_483 241 +#define DIV2_482 241 +#define DIV2_481 240 +#define DIV2_480 240 +#define DIV2_479 239 +#define DIV2_478 239 +#define DIV2_477 238 +#define DIV2_476 238 +#define DIV2_475 237 +#define DIV2_474 237 +#define DIV2_473 236 +#define DIV2_472 236 +#define DIV2_471 235 +#define DIV2_470 235 +#define DIV2_469 234 +#define DIV2_468 234 +#define DIV2_467 233 +#define DIV2_466 233 +#define DIV2_465 232 +#define DIV2_464 232 +#define DIV2_463 231 +#define DIV2_462 231 +#define DIV2_461 230 +#define DIV2_460 230 +#define DIV2_459 229 +#define DIV2_458 229 +#define DIV2_457 228 +#define DIV2_456 228 +#define DIV2_455 227 +#define DIV2_454 227 +#define DIV2_453 226 +#define DIV2_452 226 +#define DIV2_451 225 +#define DIV2_450 225 +#define DIV2_449 224 +#define DIV2_448 224 +#define DIV2_447 223 +#define DIV2_446 223 +#define DIV2_445 222 +#define DIV2_444 222 +#define DIV2_443 221 +#define DIV2_442 221 +#define DIV2_441 220 +#define DIV2_440 220 +#define DIV2_439 219 +#define DIV2_438 219 +#define DIV2_437 218 +#define DIV2_436 218 +#define DIV2_435 217 +#define DIV2_434 217 +#define DIV2_433 216 +#define DIV2_432 216 +#define DIV2_431 215 +#define DIV2_430 215 +#define DIV2_429 214 +#define DIV2_428 214 +#define DIV2_427 213 +#define DIV2_426 213 +#define DIV2_425 212 +#define DIV2_424 212 +#define DIV2_423 211 +#define DIV2_422 211 +#define DIV2_421 210 +#define DIV2_420 210 +#define DIV2_419 209 +#define DIV2_418 209 +#define DIV2_417 208 +#define DIV2_416 208 +#define DIV2_415 207 +#define DIV2_414 207 +#define DIV2_413 206 +#define DIV2_412 206 +#define DIV2_411 205 +#define DIV2_410 205 +#define DIV2_409 204 +#define DIV2_408 204 +#define DIV2_407 203 +#define DIV2_406 203 +#define DIV2_405 202 +#define DIV2_404 202 +#define DIV2_403 201 +#define DIV2_402 201 +#define DIV2_401 200 +#define DIV2_400 200 +#define DIV2_399 199 +#define DIV2_398 199 +#define DIV2_397 198 +#define DIV2_396 198 +#define DIV2_395 197 +#define DIV2_394 197 +#define DIV2_393 196 +#define DIV2_392 196 +#define DIV2_391 195 +#define DIV2_390 195 +#define DIV2_389 194 +#define DIV2_388 194 +#define DIV2_387 193 +#define DIV2_386 193 +#define DIV2_385 192 +#define DIV2_384 192 +#define DIV2_383 191 +#define DIV2_382 191 +#define DIV2_381 190 +#define DIV2_380 190 +#define DIV2_379 189 +#define DIV2_378 189 +#define DIV2_377 188 +#define DIV2_376 188 +#define DIV2_375 187 +#define DIV2_374 187 +#define DIV2_373 186 +#define DIV2_372 186 +#define DIV2_371 185 +#define DIV2_370 185 +#define DIV2_369 184 +#define DIV2_368 184 +#define DIV2_367 183 +#define DIV2_366 183 +#define DIV2_365 182 +#define DIV2_364 182 +#define DIV2_363 181 +#define DIV2_362 181 +#define DIV2_361 180 +#define DIV2_360 180 +#define DIV2_359 179 +#define DIV2_358 179 +#define DIV2_357 178 +#define DIV2_356 178 +#define DIV2_355 177 +#define DIV2_354 177 +#define DIV2_353 176 +#define DIV2_352 176 +#define DIV2_351 175 +#define DIV2_350 175 +#define DIV2_349 174 +#define DIV2_348 174 +#define DIV2_347 173 +#define DIV2_346 173 +#define DIV2_345 172 +#define DIV2_344 172 +#define DIV2_343 171 +#define DIV2_342 171 +#define DIV2_341 170 +#define DIV2_340 170 +#define DIV2_339 169 +#define DIV2_338 169 +#define DIV2_337 168 +#define DIV2_336 168 +#define DIV2_335 167 +#define DIV2_334 167 +#define DIV2_333 166 +#define DIV2_332 166 +#define DIV2_331 165 +#define DIV2_330 165 +#define DIV2_329 164 +#define DIV2_328 164 +#define DIV2_327 163 +#define DIV2_326 163 +#define DIV2_325 162 +#define DIV2_324 162 +#define DIV2_323 161 +#define DIV2_322 161 +#define DIV2_321 160 +#define DIV2_320 160 +#define DIV2_319 159 +#define DIV2_318 159 +#define DIV2_317 158 +#define DIV2_316 158 +#define DIV2_315 157 +#define DIV2_314 157 +#define DIV2_313 156 +#define DIV2_312 156 +#define DIV2_311 155 +#define DIV2_310 155 +#define DIV2_309 154 +#define DIV2_308 154 +#define DIV2_307 153 +#define DIV2_306 153 +#define DIV2_305 152 +#define DIV2_304 152 +#define DIV2_303 151 +#define DIV2_302 151 +#define DIV2_301 150 +#define DIV2_300 150 +#define DIV2_299 149 +#define DIV2_298 149 +#define DIV2_297 148 +#define DIV2_296 148 +#define DIV2_295 147 +#define DIV2_294 147 +#define DIV2_293 146 +#define DIV2_292 146 +#define DIV2_291 145 +#define DIV2_290 145 +#define DIV2_289 144 +#define DIV2_288 144 +#define DIV2_287 143 +#define DIV2_286 143 +#define DIV2_285 142 +#define DIV2_284 142 +#define DIV2_283 141 +#define DIV2_282 141 +#define DIV2_281 140 +#define DIV2_280 140 +#define DIV2_279 139 +#define DIV2_278 139 +#define DIV2_277 138 +#define DIV2_276 138 +#define DIV2_275 137 +#define DIV2_274 137 +#define DIV2_273 136 +#define DIV2_272 136 +#define DIV2_271 135 +#define DIV2_270 135 +#define DIV2_269 134 +#define DIV2_268 134 +#define DIV2_267 133 +#define DIV2_266 133 +#define DIV2_265 132 +#define DIV2_264 132 +#define DIV2_263 131 +#define DIV2_262 131 +#define DIV2_261 130 +#define DIV2_260 130 +#define DIV2_259 129 +#define DIV2_258 129 +#define DIV2_257 128 +#define DIV2_256 128 +#define DIV2_255 127 +#define DIV2_254 127 +#define DIV2_253 126 +#define DIV2_252 126 +#define DIV2_251 125 +#define DIV2_250 125 +#define DIV2_249 124 +#define DIV2_248 124 +#define DIV2_247 123 +#define DIV2_246 123 +#define DIV2_245 122 +#define DIV2_244 122 +#define DIV2_243 121 +#define DIV2_242 121 +#define DIV2_241 120 +#define DIV2_240 120 +#define DIV2_239 119 +#define DIV2_238 119 +#define DIV2_237 118 +#define DIV2_236 118 +#define DIV2_235 117 +#define DIV2_234 117 +#define DIV2_233 116 +#define DIV2_232 116 +#define DIV2_231 115 +#define DIV2_230 115 +#define DIV2_229 114 +#define DIV2_228 114 +#define DIV2_227 113 +#define DIV2_226 113 +#define DIV2_225 112 +#define DIV2_224 112 +#define DIV2_223 111 +#define DIV2_222 111 +#define DIV2_221 110 +#define DIV2_220 110 +#define DIV2_219 109 +#define DIV2_218 109 +#define DIV2_217 108 +#define DIV2_216 108 +#define DIV2_215 107 +#define DIV2_214 107 +#define DIV2_213 106 +#define DIV2_212 106 +#define DIV2_211 105 +#define DIV2_210 105 +#define DIV2_209 104 +#define DIV2_208 104 +#define DIV2_207 103 +#define DIV2_206 103 +#define DIV2_205 102 +#define DIV2_204 102 +#define DIV2_203 101 +#define DIV2_202 101 +#define DIV2_201 100 +#define DIV2_200 100 +#define DIV2_199 99 +#define DIV2_198 99 +#define DIV2_197 98 +#define DIV2_196 98 +#define DIV2_195 97 +#define DIV2_194 97 +#define DIV2_193 96 +#define DIV2_192 96 +#define DIV2_191 95 +#define DIV2_190 95 +#define DIV2_189 94 +#define DIV2_188 94 +#define DIV2_187 93 +#define DIV2_186 93 +#define DIV2_185 92 +#define DIV2_184 92 +#define DIV2_183 91 +#define DIV2_182 91 +#define DIV2_181 90 +#define DIV2_180 90 +#define DIV2_179 89 +#define DIV2_178 89 +#define DIV2_177 88 +#define DIV2_176 88 +#define DIV2_175 87 +#define DIV2_174 87 +#define DIV2_173 86 +#define DIV2_172 86 +#define DIV2_171 85 +#define DIV2_170 85 +#define DIV2_169 84 +#define DIV2_168 84 +#define DIV2_167 83 +#define DIV2_166 83 +#define DIV2_165 82 +#define DIV2_164 82 +#define DIV2_163 81 +#define DIV2_162 81 +#define DIV2_161 80 +#define DIV2_160 80 +#define DIV2_159 79 +#define DIV2_158 79 +#define DIV2_157 78 +#define DIV2_156 78 +#define DIV2_155 77 +#define DIV2_154 77 +#define DIV2_153 76 +#define DIV2_152 76 +#define DIV2_151 75 +#define DIV2_150 75 +#define DIV2_149 74 +#define DIV2_148 74 +#define DIV2_147 73 +#define DIV2_146 73 +#define DIV2_145 72 +#define DIV2_144 72 +#define DIV2_143 71 +#define DIV2_142 71 +#define DIV2_141 70 +#define DIV2_140 70 +#define DIV2_139 69 +#define DIV2_138 69 +#define DIV2_137 68 +#define DIV2_136 68 +#define DIV2_135 67 +#define DIV2_134 67 +#define DIV2_133 66 +#define DIV2_132 66 +#define DIV2_131 65 +#define DIV2_130 65 +#define DIV2_129 64 +#define DIV2_128 64 +#define DIV2_127 63 +#define DIV2_126 63 +#define DIV2_125 62 +#define DIV2_124 62 +#define DIV2_123 61 +#define DIV2_122 61 +#define DIV2_121 60 +#define DIV2_120 60 +#define DIV2_119 59 +#define DIV2_118 59 +#define DIV2_117 58 +#define DIV2_116 58 +#define DIV2_115 57 +#define DIV2_114 57 +#define DIV2_113 56 +#define DIV2_112 56 +#define DIV2_111 55 +#define DIV2_110 55 +#define DIV2_109 54 +#define DIV2_108 54 +#define DIV2_107 53 +#define DIV2_106 53 +#define DIV2_105 52 +#define DIV2_104 52 +#define DIV2_103 51 +#define DIV2_102 51 +#define DIV2_101 50 +#define DIV2_100 50 +#define DIV2_99 49 +#define DIV2_98 49 +#define DIV2_97 48 +#define DIV2_96 48 +#define DIV2_95 47 +#define DIV2_94 47 +#define DIV2_93 46 +#define DIV2_92 46 +#define DIV2_91 45 +#define DIV2_90 45 +#define DIV2_89 44 +#define DIV2_88 44 +#define DIV2_87 43 +#define DIV2_86 43 +#define DIV2_85 42 +#define DIV2_84 42 +#define DIV2_83 41 +#define DIV2_82 41 +#define DIV2_81 40 +#define DIV2_80 40 +#define DIV2_79 39 +#define DIV2_78 39 +#define DIV2_77 38 +#define DIV2_76 38 +#define DIV2_75 37 +#define DIV2_74 37 +#define DIV2_73 36 +#define DIV2_72 36 +#define DIV2_71 35 +#define DIV2_70 35 +#define DIV2_69 34 +#define DIV2_68 34 +#define DIV2_67 33 +#define DIV2_66 33 +#define DIV2_65 32 +#define DIV2_64 32 +#define DIV2_63 31 +#define DIV2_62 31 +#define DIV2_61 30 +#define DIV2_60 30 +#define DIV2_59 29 +#define DIV2_58 29 +#define DIV2_57 28 +#define DIV2_56 28 +#define DIV2_55 27 +#define DIV2_54 27 +#define DIV2_53 26 +#define DIV2_52 26 +#define DIV2_51 25 +#define DIV2_50 25 +#define DIV2_49 24 +#define DIV2_48 24 +#define DIV2_47 23 +#define DIV2_46 23 +#define DIV2_45 22 +#define DIV2_44 22 +#define DIV2_43 21 +#define DIV2_42 21 +#define DIV2_41 20 +#define DIV2_40 20 +#define DIV2_39 19 +#define DIV2_38 19 +#define DIV2_37 18 +#define DIV2_36 18 +#define DIV2_35 17 +#define DIV2_34 17 +#define DIV2_33 16 +#define DIV2_32 16 +#define DIV2_31 15 +#define DIV2_30 15 +#define DIV2_29 14 +#define DIV2_28 14 +#define DIV2_27 13 +#define DIV2_26 13 +#define DIV2_25 12 +#define DIV2_24 12 +#define DIV2_23 11 +#define DIV2_22 11 +#define DIV2_21 10 +#define DIV2_20 10 +#define DIV2_19 9 +#define DIV2_18 9 +#define DIV2_17 8 +#define DIV2_16 8 +#define DIV2_15 7 +#define DIV2_14 7 +#define DIV2_13 6 +#define DIV2_12 6 +#define DIV2_11 5 +#define DIV2_10 5 +#define DIV2_9 4 +#define DIV2_8 4 +#define DIV2_7 3 +#define DIV2_6 3 +#define DIV2_5 2 +#define DIV2_4 2 +#define DIV2_3 1 +#define DIV2_2 1 +#define DIV2_1 0 +#define DIV2_0 0 + +#define MOD2(x) C2(MOD2_,x) +#define MOD2_1024 0 +#define MOD2_1023 1 +#define MOD2_1022 0 +#define MOD2_1021 1 +#define MOD2_1020 0 +#define MOD2_1019 1 +#define MOD2_1018 0 +#define MOD2_1017 1 +#define MOD2_1016 0 +#define MOD2_1015 1 +#define MOD2_1014 0 +#define MOD2_1013 1 +#define MOD2_1012 0 +#define MOD2_1011 1 +#define MOD2_1010 0 +#define MOD2_1009 1 +#define MOD2_1008 0 +#define MOD2_1007 1 +#define MOD2_1006 0 +#define MOD2_1005 1 +#define MOD2_1004 0 +#define MOD2_1003 1 +#define MOD2_1002 0 +#define MOD2_1001 1 +#define MOD2_1000 0 +#define MOD2_999 1 +#define MOD2_998 0 +#define MOD2_997 1 +#define MOD2_996 0 +#define MOD2_995 1 +#define MOD2_994 0 +#define MOD2_993 1 +#define MOD2_992 0 +#define MOD2_991 1 +#define MOD2_990 0 +#define MOD2_989 1 +#define MOD2_988 0 +#define MOD2_987 1 +#define MOD2_986 0 +#define MOD2_985 1 +#define MOD2_984 0 +#define MOD2_983 1 +#define MOD2_982 0 +#define MOD2_981 1 +#define MOD2_980 0 +#define MOD2_979 1 +#define MOD2_978 0 +#define MOD2_977 1 +#define MOD2_976 0 +#define MOD2_975 1 +#define MOD2_974 0 +#define MOD2_973 1 +#define MOD2_972 0 +#define MOD2_971 1 +#define MOD2_970 0 +#define MOD2_969 1 +#define MOD2_968 0 +#define MOD2_967 1 +#define MOD2_966 0 +#define MOD2_965 1 +#define MOD2_964 0 +#define MOD2_963 1 +#define MOD2_962 0 +#define MOD2_961 1 +#define MOD2_960 0 +#define MOD2_959 1 +#define MOD2_958 0 +#define MOD2_957 1 +#define MOD2_956 0 +#define MOD2_955 1 +#define MOD2_954 0 +#define MOD2_953 1 +#define MOD2_952 0 +#define MOD2_951 1 +#define MOD2_950 0 +#define MOD2_949 1 +#define MOD2_948 0 +#define MOD2_947 1 +#define MOD2_946 0 +#define MOD2_945 1 +#define MOD2_944 0 +#define MOD2_943 1 +#define MOD2_942 0 +#define MOD2_941 1 +#define MOD2_940 0 +#define MOD2_939 1 +#define MOD2_938 0 +#define MOD2_937 1 +#define MOD2_936 0 +#define MOD2_935 1 +#define MOD2_934 0 +#define MOD2_933 1 +#define MOD2_932 0 +#define MOD2_931 1 +#define MOD2_930 0 +#define MOD2_929 1 +#define MOD2_928 0 +#define MOD2_927 1 +#define MOD2_926 0 +#define MOD2_925 1 +#define MOD2_924 0 +#define MOD2_923 1 +#define MOD2_922 0 +#define MOD2_921 1 +#define MOD2_920 0 +#define MOD2_919 1 +#define MOD2_918 0 +#define MOD2_917 1 +#define MOD2_916 0 +#define MOD2_915 1 +#define MOD2_914 0 +#define MOD2_913 1 +#define MOD2_912 0 +#define MOD2_911 1 +#define MOD2_910 0 +#define MOD2_909 1 +#define MOD2_908 0 +#define MOD2_907 1 +#define MOD2_906 0 +#define MOD2_905 1 +#define MOD2_904 0 +#define MOD2_903 1 +#define MOD2_902 0 +#define MOD2_901 1 +#define MOD2_900 0 +#define MOD2_899 1 +#define MOD2_898 0 +#define MOD2_897 1 +#define MOD2_896 0 +#define MOD2_895 1 +#define MOD2_894 0 +#define MOD2_893 1 +#define MOD2_892 0 +#define MOD2_891 1 +#define MOD2_890 0 +#define MOD2_889 1 +#define MOD2_888 0 +#define MOD2_887 1 +#define MOD2_886 0 +#define MOD2_885 1 +#define MOD2_884 0 +#define MOD2_883 1 +#define MOD2_882 0 +#define MOD2_881 1 +#define MOD2_880 0 +#define MOD2_879 1 +#define MOD2_878 0 +#define MOD2_877 1 +#define MOD2_876 0 +#define MOD2_875 1 +#define MOD2_874 0 +#define MOD2_873 1 +#define MOD2_872 0 +#define MOD2_871 1 +#define MOD2_870 0 +#define MOD2_869 1 +#define MOD2_868 0 +#define MOD2_867 1 +#define MOD2_866 0 +#define MOD2_865 1 +#define MOD2_864 0 +#define MOD2_863 1 +#define MOD2_862 0 +#define MOD2_861 1 +#define MOD2_860 0 +#define MOD2_859 1 +#define MOD2_858 0 +#define MOD2_857 1 +#define MOD2_856 0 +#define MOD2_855 1 +#define MOD2_854 0 +#define MOD2_853 1 +#define MOD2_852 0 +#define MOD2_851 1 +#define MOD2_850 0 +#define MOD2_849 1 +#define MOD2_848 0 +#define MOD2_847 1 +#define MOD2_846 0 +#define MOD2_845 1 +#define MOD2_844 0 +#define MOD2_843 1 +#define MOD2_842 0 +#define MOD2_841 1 +#define MOD2_840 0 +#define MOD2_839 1 +#define MOD2_838 0 +#define MOD2_837 1 +#define MOD2_836 0 +#define MOD2_835 1 +#define MOD2_834 0 +#define MOD2_833 1 +#define MOD2_832 0 +#define MOD2_831 1 +#define MOD2_830 0 +#define MOD2_829 1 +#define MOD2_828 0 +#define MOD2_827 1 +#define MOD2_826 0 +#define MOD2_825 1 +#define MOD2_824 0 +#define MOD2_823 1 +#define MOD2_822 0 +#define MOD2_821 1 +#define MOD2_820 0 +#define MOD2_819 1 +#define MOD2_818 0 +#define MOD2_817 1 +#define MOD2_816 0 +#define MOD2_815 1 +#define MOD2_814 0 +#define MOD2_813 1 +#define MOD2_812 0 +#define MOD2_811 1 +#define MOD2_810 0 +#define MOD2_809 1 +#define MOD2_808 0 +#define MOD2_807 1 +#define MOD2_806 0 +#define MOD2_805 1 +#define MOD2_804 0 +#define MOD2_803 1 +#define MOD2_802 0 +#define MOD2_801 1 +#define MOD2_800 0 +#define MOD2_799 1 +#define MOD2_798 0 +#define MOD2_797 1 +#define MOD2_796 0 +#define MOD2_795 1 +#define MOD2_794 0 +#define MOD2_793 1 +#define MOD2_792 0 +#define MOD2_791 1 +#define MOD2_790 0 +#define MOD2_789 1 +#define MOD2_788 0 +#define MOD2_787 1 +#define MOD2_786 0 +#define MOD2_785 1 +#define MOD2_784 0 +#define MOD2_783 1 +#define MOD2_782 0 +#define MOD2_781 1 +#define MOD2_780 0 +#define MOD2_779 1 +#define MOD2_778 0 +#define MOD2_777 1 +#define MOD2_776 0 +#define MOD2_775 1 +#define MOD2_774 0 +#define MOD2_773 1 +#define MOD2_772 0 +#define MOD2_771 1 +#define MOD2_770 0 +#define MOD2_769 1 +#define MOD2_768 0 +#define MOD2_767 1 +#define MOD2_766 0 +#define MOD2_765 1 +#define MOD2_764 0 +#define MOD2_763 1 +#define MOD2_762 0 +#define MOD2_761 1 +#define MOD2_760 0 +#define MOD2_759 1 +#define MOD2_758 0 +#define MOD2_757 1 +#define MOD2_756 0 +#define MOD2_755 1 +#define MOD2_754 0 +#define MOD2_753 1 +#define MOD2_752 0 +#define MOD2_751 1 +#define MOD2_750 0 +#define MOD2_749 1 +#define MOD2_748 0 +#define MOD2_747 1 +#define MOD2_746 0 +#define MOD2_745 1 +#define MOD2_744 0 +#define MOD2_743 1 +#define MOD2_742 0 +#define MOD2_741 1 +#define MOD2_740 0 +#define MOD2_739 1 +#define MOD2_738 0 +#define MOD2_737 1 +#define MOD2_736 0 +#define MOD2_735 1 +#define MOD2_734 0 +#define MOD2_733 1 +#define MOD2_732 0 +#define MOD2_731 1 +#define MOD2_730 0 +#define MOD2_729 1 +#define MOD2_728 0 +#define MOD2_727 1 +#define MOD2_726 0 +#define MOD2_725 1 +#define MOD2_724 0 +#define MOD2_723 1 +#define MOD2_722 0 +#define MOD2_721 1 +#define MOD2_720 0 +#define MOD2_719 1 +#define MOD2_718 0 +#define MOD2_717 1 +#define MOD2_716 0 +#define MOD2_715 1 +#define MOD2_714 0 +#define MOD2_713 1 +#define MOD2_712 0 +#define MOD2_711 1 +#define MOD2_710 0 +#define MOD2_709 1 +#define MOD2_708 0 +#define MOD2_707 1 +#define MOD2_706 0 +#define MOD2_705 1 +#define MOD2_704 0 +#define MOD2_703 1 +#define MOD2_702 0 +#define MOD2_701 1 +#define MOD2_700 0 +#define MOD2_699 1 +#define MOD2_698 0 +#define MOD2_697 1 +#define MOD2_696 0 +#define MOD2_695 1 +#define MOD2_694 0 +#define MOD2_693 1 +#define MOD2_692 0 +#define MOD2_691 1 +#define MOD2_690 0 +#define MOD2_689 1 +#define MOD2_688 0 +#define MOD2_687 1 +#define MOD2_686 0 +#define MOD2_685 1 +#define MOD2_684 0 +#define MOD2_683 1 +#define MOD2_682 0 +#define MOD2_681 1 +#define MOD2_680 0 +#define MOD2_679 1 +#define MOD2_678 0 +#define MOD2_677 1 +#define MOD2_676 0 +#define MOD2_675 1 +#define MOD2_674 0 +#define MOD2_673 1 +#define MOD2_672 0 +#define MOD2_671 1 +#define MOD2_670 0 +#define MOD2_669 1 +#define MOD2_668 0 +#define MOD2_667 1 +#define MOD2_666 0 +#define MOD2_665 1 +#define MOD2_664 0 +#define MOD2_663 1 +#define MOD2_662 0 +#define MOD2_661 1 +#define MOD2_660 0 +#define MOD2_659 1 +#define MOD2_658 0 +#define MOD2_657 1 +#define MOD2_656 0 +#define MOD2_655 1 +#define MOD2_654 0 +#define MOD2_653 1 +#define MOD2_652 0 +#define MOD2_651 1 +#define MOD2_650 0 +#define MOD2_649 1 +#define MOD2_648 0 +#define MOD2_647 1 +#define MOD2_646 0 +#define MOD2_645 1 +#define MOD2_644 0 +#define MOD2_643 1 +#define MOD2_642 0 +#define MOD2_641 1 +#define MOD2_640 0 +#define MOD2_639 1 +#define MOD2_638 0 +#define MOD2_637 1 +#define MOD2_636 0 +#define MOD2_635 1 +#define MOD2_634 0 +#define MOD2_633 1 +#define MOD2_632 0 +#define MOD2_631 1 +#define MOD2_630 0 +#define MOD2_629 1 +#define MOD2_628 0 +#define MOD2_627 1 +#define MOD2_626 0 +#define MOD2_625 1 +#define MOD2_624 0 +#define MOD2_623 1 +#define MOD2_622 0 +#define MOD2_621 1 +#define MOD2_620 0 +#define MOD2_619 1 +#define MOD2_618 0 +#define MOD2_617 1 +#define MOD2_616 0 +#define MOD2_615 1 +#define MOD2_614 0 +#define MOD2_613 1 +#define MOD2_612 0 +#define MOD2_611 1 +#define MOD2_610 0 +#define MOD2_609 1 +#define MOD2_608 0 +#define MOD2_607 1 +#define MOD2_606 0 +#define MOD2_605 1 +#define MOD2_604 0 +#define MOD2_603 1 +#define MOD2_602 0 +#define MOD2_601 1 +#define MOD2_600 0 +#define MOD2_599 1 +#define MOD2_598 0 +#define MOD2_597 1 +#define MOD2_596 0 +#define MOD2_595 1 +#define MOD2_594 0 +#define MOD2_593 1 +#define MOD2_592 0 +#define MOD2_591 1 +#define MOD2_590 0 +#define MOD2_589 1 +#define MOD2_588 0 +#define MOD2_587 1 +#define MOD2_586 0 +#define MOD2_585 1 +#define MOD2_584 0 +#define MOD2_583 1 +#define MOD2_582 0 +#define MOD2_581 1 +#define MOD2_580 0 +#define MOD2_579 1 +#define MOD2_578 0 +#define MOD2_577 1 +#define MOD2_576 0 +#define MOD2_575 1 +#define MOD2_574 0 +#define MOD2_573 1 +#define MOD2_572 0 +#define MOD2_571 1 +#define MOD2_570 0 +#define MOD2_569 1 +#define MOD2_568 0 +#define MOD2_567 1 +#define MOD2_566 0 +#define MOD2_565 1 +#define MOD2_564 0 +#define MOD2_563 1 +#define MOD2_562 0 +#define MOD2_561 1 +#define MOD2_560 0 +#define MOD2_559 1 +#define MOD2_558 0 +#define MOD2_557 1 +#define MOD2_556 0 +#define MOD2_555 1 +#define MOD2_554 0 +#define MOD2_553 1 +#define MOD2_552 0 +#define MOD2_551 1 +#define MOD2_550 0 +#define MOD2_549 1 +#define MOD2_548 0 +#define MOD2_547 1 +#define MOD2_546 0 +#define MOD2_545 1 +#define MOD2_544 0 +#define MOD2_543 1 +#define MOD2_542 0 +#define MOD2_541 1 +#define MOD2_540 0 +#define MOD2_539 1 +#define MOD2_538 0 +#define MOD2_537 1 +#define MOD2_536 0 +#define MOD2_535 1 +#define MOD2_534 0 +#define MOD2_533 1 +#define MOD2_532 0 +#define MOD2_531 1 +#define MOD2_530 0 +#define MOD2_529 1 +#define MOD2_528 0 +#define MOD2_527 1 +#define MOD2_526 0 +#define MOD2_525 1 +#define MOD2_524 0 +#define MOD2_523 1 +#define MOD2_522 0 +#define MOD2_521 1 +#define MOD2_520 0 +#define MOD2_519 1 +#define MOD2_518 0 +#define MOD2_517 1 +#define MOD2_516 0 +#define MOD2_515 1 +#define MOD2_514 0 +#define MOD2_513 1 +#define MOD2_512 0 +#define MOD2_511 1 +#define MOD2_510 0 +#define MOD2_509 1 +#define MOD2_508 0 +#define MOD2_507 1 +#define MOD2_506 0 +#define MOD2_505 1 +#define MOD2_504 0 +#define MOD2_503 1 +#define MOD2_502 0 +#define MOD2_501 1 +#define MOD2_500 0 +#define MOD2_499 1 +#define MOD2_498 0 +#define MOD2_497 1 +#define MOD2_496 0 +#define MOD2_495 1 +#define MOD2_494 0 +#define MOD2_493 1 +#define MOD2_492 0 +#define MOD2_491 1 +#define MOD2_490 0 +#define MOD2_489 1 +#define MOD2_488 0 +#define MOD2_487 1 +#define MOD2_486 0 +#define MOD2_485 1 +#define MOD2_484 0 +#define MOD2_483 1 +#define MOD2_482 0 +#define MOD2_481 1 +#define MOD2_480 0 +#define MOD2_479 1 +#define MOD2_478 0 +#define MOD2_477 1 +#define MOD2_476 0 +#define MOD2_475 1 +#define MOD2_474 0 +#define MOD2_473 1 +#define MOD2_472 0 +#define MOD2_471 1 +#define MOD2_470 0 +#define MOD2_469 1 +#define MOD2_468 0 +#define MOD2_467 1 +#define MOD2_466 0 +#define MOD2_465 1 +#define MOD2_464 0 +#define MOD2_463 1 +#define MOD2_462 0 +#define MOD2_461 1 +#define MOD2_460 0 +#define MOD2_459 1 +#define MOD2_458 0 +#define MOD2_457 1 +#define MOD2_456 0 +#define MOD2_455 1 +#define MOD2_454 0 +#define MOD2_453 1 +#define MOD2_452 0 +#define MOD2_451 1 +#define MOD2_450 0 +#define MOD2_449 1 +#define MOD2_448 0 +#define MOD2_447 1 +#define MOD2_446 0 +#define MOD2_445 1 +#define MOD2_444 0 +#define MOD2_443 1 +#define MOD2_442 0 +#define MOD2_441 1 +#define MOD2_440 0 +#define MOD2_439 1 +#define MOD2_438 0 +#define MOD2_437 1 +#define MOD2_436 0 +#define MOD2_435 1 +#define MOD2_434 0 +#define MOD2_433 1 +#define MOD2_432 0 +#define MOD2_431 1 +#define MOD2_430 0 +#define MOD2_429 1 +#define MOD2_428 0 +#define MOD2_427 1 +#define MOD2_426 0 +#define MOD2_425 1 +#define MOD2_424 0 +#define MOD2_423 1 +#define MOD2_422 0 +#define MOD2_421 1 +#define MOD2_420 0 +#define MOD2_419 1 +#define MOD2_418 0 +#define MOD2_417 1 +#define MOD2_416 0 +#define MOD2_415 1 +#define MOD2_414 0 +#define MOD2_413 1 +#define MOD2_412 0 +#define MOD2_411 1 +#define MOD2_410 0 +#define MOD2_409 1 +#define MOD2_408 0 +#define MOD2_407 1 +#define MOD2_406 0 +#define MOD2_405 1 +#define MOD2_404 0 +#define MOD2_403 1 +#define MOD2_402 0 +#define MOD2_401 1 +#define MOD2_400 0 +#define MOD2_399 1 +#define MOD2_398 0 +#define MOD2_397 1 +#define MOD2_396 0 +#define MOD2_395 1 +#define MOD2_394 0 +#define MOD2_393 1 +#define MOD2_392 0 +#define MOD2_391 1 +#define MOD2_390 0 +#define MOD2_389 1 +#define MOD2_388 0 +#define MOD2_387 1 +#define MOD2_386 0 +#define MOD2_385 1 +#define MOD2_384 0 +#define MOD2_383 1 +#define MOD2_382 0 +#define MOD2_381 1 +#define MOD2_380 0 +#define MOD2_379 1 +#define MOD2_378 0 +#define MOD2_377 1 +#define MOD2_376 0 +#define MOD2_375 1 +#define MOD2_374 0 +#define MOD2_373 1 +#define MOD2_372 0 +#define MOD2_371 1 +#define MOD2_370 0 +#define MOD2_369 1 +#define MOD2_368 0 +#define MOD2_367 1 +#define MOD2_366 0 +#define MOD2_365 1 +#define MOD2_364 0 +#define MOD2_363 1 +#define MOD2_362 0 +#define MOD2_361 1 +#define MOD2_360 0 +#define MOD2_359 1 +#define MOD2_358 0 +#define MOD2_357 1 +#define MOD2_356 0 +#define MOD2_355 1 +#define MOD2_354 0 +#define MOD2_353 1 +#define MOD2_352 0 +#define MOD2_351 1 +#define MOD2_350 0 +#define MOD2_349 1 +#define MOD2_348 0 +#define MOD2_347 1 +#define MOD2_346 0 +#define MOD2_345 1 +#define MOD2_344 0 +#define MOD2_343 1 +#define MOD2_342 0 +#define MOD2_341 1 +#define MOD2_340 0 +#define MOD2_339 1 +#define MOD2_338 0 +#define MOD2_337 1 +#define MOD2_336 0 +#define MOD2_335 1 +#define MOD2_334 0 +#define MOD2_333 1 +#define MOD2_332 0 +#define MOD2_331 1 +#define MOD2_330 0 +#define MOD2_329 1 +#define MOD2_328 0 +#define MOD2_327 1 +#define MOD2_326 0 +#define MOD2_325 1 +#define MOD2_324 0 +#define MOD2_323 1 +#define MOD2_322 0 +#define MOD2_321 1 +#define MOD2_320 0 +#define MOD2_319 1 +#define MOD2_318 0 +#define MOD2_317 1 +#define MOD2_316 0 +#define MOD2_315 1 +#define MOD2_314 0 +#define MOD2_313 1 +#define MOD2_312 0 +#define MOD2_311 1 +#define MOD2_310 0 +#define MOD2_309 1 +#define MOD2_308 0 +#define MOD2_307 1 +#define MOD2_306 0 +#define MOD2_305 1 +#define MOD2_304 0 +#define MOD2_303 1 +#define MOD2_302 0 +#define MOD2_301 1 +#define MOD2_300 0 +#define MOD2_299 1 +#define MOD2_298 0 +#define MOD2_297 1 +#define MOD2_296 0 +#define MOD2_295 1 +#define MOD2_294 0 +#define MOD2_293 1 +#define MOD2_292 0 +#define MOD2_291 1 +#define MOD2_290 0 +#define MOD2_289 1 +#define MOD2_288 0 +#define MOD2_287 1 +#define MOD2_286 0 +#define MOD2_285 1 +#define MOD2_284 0 +#define MOD2_283 1 +#define MOD2_282 0 +#define MOD2_281 1 +#define MOD2_280 0 +#define MOD2_279 1 +#define MOD2_278 0 +#define MOD2_277 1 +#define MOD2_276 0 +#define MOD2_275 1 +#define MOD2_274 0 +#define MOD2_273 1 +#define MOD2_272 0 +#define MOD2_271 1 +#define MOD2_270 0 +#define MOD2_269 1 +#define MOD2_268 0 +#define MOD2_267 1 +#define MOD2_266 0 +#define MOD2_265 1 +#define MOD2_264 0 +#define MOD2_263 1 +#define MOD2_262 0 +#define MOD2_261 1 +#define MOD2_260 0 +#define MOD2_259 1 +#define MOD2_258 0 +#define MOD2_257 1 +#define MOD2_256 0 +#define MOD2_255 1 +#define MOD2_254 0 +#define MOD2_253 1 +#define MOD2_252 0 +#define MOD2_251 1 +#define MOD2_250 0 +#define MOD2_249 1 +#define MOD2_248 0 +#define MOD2_247 1 +#define MOD2_246 0 +#define MOD2_245 1 +#define MOD2_244 0 +#define MOD2_243 1 +#define MOD2_242 0 +#define MOD2_241 1 +#define MOD2_240 0 +#define MOD2_239 1 +#define MOD2_238 0 +#define MOD2_237 1 +#define MOD2_236 0 +#define MOD2_235 1 +#define MOD2_234 0 +#define MOD2_233 1 +#define MOD2_232 0 +#define MOD2_231 1 +#define MOD2_230 0 +#define MOD2_229 1 +#define MOD2_228 0 +#define MOD2_227 1 +#define MOD2_226 0 +#define MOD2_225 1 +#define MOD2_224 0 +#define MOD2_223 1 +#define MOD2_222 0 +#define MOD2_221 1 +#define MOD2_220 0 +#define MOD2_219 1 +#define MOD2_218 0 +#define MOD2_217 1 +#define MOD2_216 0 +#define MOD2_215 1 +#define MOD2_214 0 +#define MOD2_213 1 +#define MOD2_212 0 +#define MOD2_211 1 +#define MOD2_210 0 +#define MOD2_209 1 +#define MOD2_208 0 +#define MOD2_207 1 +#define MOD2_206 0 +#define MOD2_205 1 +#define MOD2_204 0 +#define MOD2_203 1 +#define MOD2_202 0 +#define MOD2_201 1 +#define MOD2_200 0 +#define MOD2_199 1 +#define MOD2_198 0 +#define MOD2_197 1 +#define MOD2_196 0 +#define MOD2_195 1 +#define MOD2_194 0 +#define MOD2_193 1 +#define MOD2_192 0 +#define MOD2_191 1 +#define MOD2_190 0 +#define MOD2_189 1 +#define MOD2_188 0 +#define MOD2_187 1 +#define MOD2_186 0 +#define MOD2_185 1 +#define MOD2_184 0 +#define MOD2_183 1 +#define MOD2_182 0 +#define MOD2_181 1 +#define MOD2_180 0 +#define MOD2_179 1 +#define MOD2_178 0 +#define MOD2_177 1 +#define MOD2_176 0 +#define MOD2_175 1 +#define MOD2_174 0 +#define MOD2_173 1 +#define MOD2_172 0 +#define MOD2_171 1 +#define MOD2_170 0 +#define MOD2_169 1 +#define MOD2_168 0 +#define MOD2_167 1 +#define MOD2_166 0 +#define MOD2_165 1 +#define MOD2_164 0 +#define MOD2_163 1 +#define MOD2_162 0 +#define MOD2_161 1 +#define MOD2_160 0 +#define MOD2_159 1 +#define MOD2_158 0 +#define MOD2_157 1 +#define MOD2_156 0 +#define MOD2_155 1 +#define MOD2_154 0 +#define MOD2_153 1 +#define MOD2_152 0 +#define MOD2_151 1 +#define MOD2_150 0 +#define MOD2_149 1 +#define MOD2_148 0 +#define MOD2_147 1 +#define MOD2_146 0 +#define MOD2_145 1 +#define MOD2_144 0 +#define MOD2_143 1 +#define MOD2_142 0 +#define MOD2_141 1 +#define MOD2_140 0 +#define MOD2_139 1 +#define MOD2_138 0 +#define MOD2_137 1 +#define MOD2_136 0 +#define MOD2_135 1 +#define MOD2_134 0 +#define MOD2_133 1 +#define MOD2_132 0 +#define MOD2_131 1 +#define MOD2_130 0 +#define MOD2_129 1 +#define MOD2_128 0 +#define MOD2_127 1 +#define MOD2_126 0 +#define MOD2_125 1 +#define MOD2_124 0 +#define MOD2_123 1 +#define MOD2_122 0 +#define MOD2_121 1 +#define MOD2_120 0 +#define MOD2_119 1 +#define MOD2_118 0 +#define MOD2_117 1 +#define MOD2_116 0 +#define MOD2_115 1 +#define MOD2_114 0 +#define MOD2_113 1 +#define MOD2_112 0 +#define MOD2_111 1 +#define MOD2_110 0 +#define MOD2_109 1 +#define MOD2_108 0 +#define MOD2_107 1 +#define MOD2_106 0 +#define MOD2_105 1 +#define MOD2_104 0 +#define MOD2_103 1 +#define MOD2_102 0 +#define MOD2_101 1 +#define MOD2_100 0 +#define MOD2_99 1 +#define MOD2_98 0 +#define MOD2_97 1 +#define MOD2_96 0 +#define MOD2_95 1 +#define MOD2_94 0 +#define MOD2_93 1 +#define MOD2_92 0 +#define MOD2_91 1 +#define MOD2_90 0 +#define MOD2_89 1 +#define MOD2_88 0 +#define MOD2_87 1 +#define MOD2_86 0 +#define MOD2_85 1 +#define MOD2_84 0 +#define MOD2_83 1 +#define MOD2_82 0 +#define MOD2_81 1 +#define MOD2_80 0 +#define MOD2_79 1 +#define MOD2_78 0 +#define MOD2_77 1 +#define MOD2_76 0 +#define MOD2_75 1 +#define MOD2_74 0 +#define MOD2_73 1 +#define MOD2_72 0 +#define MOD2_71 1 +#define MOD2_70 0 +#define MOD2_69 1 +#define MOD2_68 0 +#define MOD2_67 1 +#define MOD2_66 0 +#define MOD2_65 1 +#define MOD2_64 0 +#define MOD2_63 1 +#define MOD2_62 0 +#define MOD2_61 1 +#define MOD2_60 0 +#define MOD2_59 1 +#define MOD2_58 0 +#define MOD2_57 1 +#define MOD2_56 0 +#define MOD2_55 1 +#define MOD2_54 0 +#define MOD2_53 1 +#define MOD2_52 0 +#define MOD2_51 1 +#define MOD2_50 0 +#define MOD2_49 1 +#define MOD2_48 0 +#define MOD2_47 1 +#define MOD2_46 0 +#define MOD2_45 1 +#define MOD2_44 0 +#define MOD2_43 1 +#define MOD2_42 0 +#define MOD2_41 1 +#define MOD2_40 0 +#define MOD2_39 1 +#define MOD2_38 0 +#define MOD2_37 1 +#define MOD2_36 0 +#define MOD2_35 1 +#define MOD2_34 0 +#define MOD2_33 1 +#define MOD2_32 0 +#define MOD2_31 1 +#define MOD2_30 0 +#define MOD2_29 1 +#define MOD2_28 0 +#define MOD2_27 1 +#define MOD2_26 0 +#define MOD2_25 1 +#define MOD2_24 0 +#define MOD2_23 1 +#define MOD2_22 0 +#define MOD2_21 1 +#define MOD2_20 0 +#define MOD2_19 1 +#define MOD2_18 0 +#define MOD2_17 1 +#define MOD2_16 0 +#define MOD2_15 1 +#define MOD2_14 0 +#define MOD2_13 1 +#define MOD2_12 0 +#define MOD2_11 1 +#define MOD2_10 0 +#define MOD2_9 1 +#define MOD2_8 0 +#define MOD2_7 1 +#define MOD2_6 0 +#define MOD2_5 1 +#define MOD2_4 0 +#define MOD2_3 1 +#define MOD2_2 0 +#define MOD2_1 1 +#define MOD2_0 0 + +#define THE_NTH_ARG(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124, ... ) P124 + +#define _TRIGGER_PARENTHESIS_(...) , + +#define LPAREN ( + +#ifdef _MSC_VER +#define COUNT_1_OR_MORE_ARG(...) THE_NTH_ARG LPAREN __VA_ARGS__, \ +123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define MORE_THAN_1_ARG(...) THE_NTH_ARG LPAREN __VA_ARGS__, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0) +#else +#define COUNT_1_OR_MORE_ARG(...) THE_NTH_ARG (__VA_ARGS__, \ +123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define MORE_THAN_1_ARG(...) THE_NTH_ARG(__VA_ARGS__, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0) +#endif + +#define COUNT_ARG(...) C2(COUNT_ARG_, ISEMPTY(__VA_ARGS__))(__VA_ARGS__) +#define COUNT_ARG_1(...) 0 +#define COUNT_ARG_0(...) C1(COUNT_1_OR_MORE_ARG(__VA_ARGS__)) + +#define ISEMPTY(...) C5(DISPTACH_EMPTY_, MORE_THAN_1_ARG(_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()), MORE_THAN_1_ARG(__VA_ARGS__), MORE_THAN_1_ARG(__VA_ARGS__ ()), MORE_THAN_1_ARG(_TRIGGER_PARENTHESIS_ __VA_ARGS__)) +#define DISPTACH_EMPTY_1000 1 +#define DISPTACH_EMPTY_0000 0 +#define DISPTACH_EMPTY_1100 0 +#define DISPTACH_EMPTY_1111 0 +#define DISPTACH_EMPTY_1001 0 +#define DISPTACH_EMPTY_1010 0 + + +#define C2_(x,y) x##y + +#define C2(x,y) C2_(x,y) + +#define C3(x,y,z) C2(x, C2(y,z)) + +#define C4(x,y,z, u) C2(C2(x,y), C2(z,u)) + +#define C5(x,y,z,u, v) C2(C4(x,y, z, u), v) + +#define C1_(x) x + +#define C1(x) C1_(x) + +#define C2STRING(x,y) x y + +#define C3STRING(x,y,z) x y z + +#define C4STRING(x,y,z,u) x y z u + +#define C5STRING(x,y,z,u,v) x y z u v + + +#define FOR_EACH_1_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P1) \ +FOR_EACH_1_123(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + +#define FOR_EACH_1_123(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(P1) \ +FOR_EACH_1_122(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + +#define FOR_EACH_1_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P1) \ +FOR_EACH_1_121(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_1_121(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(P1) \ +FOR_EACH_1_120(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + +#define FOR_EACH_1_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P1) \ +FOR_EACH_1_119(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_1_119(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(P1) \ +FOR_EACH_1_118(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + +#define FOR_EACH_1_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P1) \ +FOR_EACH_1_117(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_1_117(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(P1) \ +FOR_EACH_1_116(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + +#define FOR_EACH_1_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P1) \ +FOR_EACH_1_115(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_1_115(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(P1) \ +FOR_EACH_1_114(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + +#define FOR_EACH_1_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P1) \ +FOR_EACH_1_113(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_1_113(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(P1) \ +FOR_EACH_1_112(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + +#define FOR_EACH_1_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P1) \ +FOR_EACH_1_111(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_1_111(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(P1) \ +FOR_EACH_1_110(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + +#define FOR_EACH_1_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P1) \ +FOR_EACH_1_109(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_1_109(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(P1) \ +FOR_EACH_1_108(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + +#define FOR_EACH_1_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P1) \ +FOR_EACH_1_107(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_1_107(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(P1) \ +FOR_EACH_1_106(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + +#define FOR_EACH_1_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P1) \ +FOR_EACH_1_105(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_1_105(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(P1) \ +FOR_EACH_1_104(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + +#define FOR_EACH_1_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P1) \ +FOR_EACH_1_103(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_1_103(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(P1) \ +FOR_EACH_1_102(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + +#define FOR_EACH_1_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P1) \ +FOR_EACH_1_101(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_1_101(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(P1) \ +FOR_EACH_1_100(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + +#define FOR_EACH_1_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P1) \ +FOR_EACH_1_99(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_1_99(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(P1) \ +FOR_EACH_1_98(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + +#define FOR_EACH_1_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P1) \ +FOR_EACH_1_97(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_1_97(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(P1) \ +FOR_EACH_1_96(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + +#define FOR_EACH_1_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P1) \ +FOR_EACH_1_95(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_1_95(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(P1) \ +FOR_EACH_1_94(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + +#define FOR_EACH_1_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P1) \ +FOR_EACH_1_93(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_1_93(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(P1) \ +FOR_EACH_1_92(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + +#define FOR_EACH_1_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P1) \ +FOR_EACH_1_91(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_1_91(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(P1) \ +FOR_EACH_1_90(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + +#define FOR_EACH_1_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P1) \ +FOR_EACH_1_89(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_1_89(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(P1) \ +FOR_EACH_1_88(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + +#define FOR_EACH_1_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P1) \ +FOR_EACH_1_87(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_1_87(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(P1) \ +FOR_EACH_1_86(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + +#define FOR_EACH_1_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P1) \ +FOR_EACH_1_85(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_1_85(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(P1) \ +FOR_EACH_1_84(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + +#define FOR_EACH_1_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P1) \ +FOR_EACH_1_83(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_1_83(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(P1) \ +FOR_EACH_1_82(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + +#define FOR_EACH_1_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P1) \ +FOR_EACH_1_81(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_1_81(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(P1) \ +FOR_EACH_1_80(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + +#define FOR_EACH_1_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P1) \ +FOR_EACH_1_79(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_1_79(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(P1) \ +FOR_EACH_1_78(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + +#define FOR_EACH_1_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P1) \ +FOR_EACH_1_77(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_1_77(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(P1) \ +FOR_EACH_1_76(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + +#define FOR_EACH_1_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P1) \ +FOR_EACH_1_75(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_1_75(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(P1) \ +FOR_EACH_1_74(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + +#define FOR_EACH_1_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P1) \ +FOR_EACH_1_73(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_1_73(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(P1) \ +FOR_EACH_1_72(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + +#define FOR_EACH_1_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P1) \ +FOR_EACH_1_71(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_1_71(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(P1) \ +FOR_EACH_1_70(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + +#define FOR_EACH_1_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P1) \ +FOR_EACH_1_69(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_1_69(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(P1) \ +FOR_EACH_1_68(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + +#define FOR_EACH_1_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P1) \ +FOR_EACH_1_67(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_1_67(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(P1) \ +FOR_EACH_1_66(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + +#define FOR_EACH_1_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P1) \ +FOR_EACH_1_65(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_1_65(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(P1) \ +FOR_EACH_1_64(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + +#define FOR_EACH_1_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P1) \ +FOR_EACH_1_63(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_1_63(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(P1) \ +FOR_EACH_1_62(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + +#define FOR_EACH_1_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P1) \ +FOR_EACH_1_61(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_1_61(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(P1) \ +FOR_EACH_1_60(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + +#define FOR_EACH_1_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P1) \ +FOR_EACH_1_59(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_1_59(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(P1) \ +FOR_EACH_1_58(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + +#define FOR_EACH_1_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P1) \ +FOR_EACH_1_57(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_1_57(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(P1) \ +FOR_EACH_1_56(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + +#define FOR_EACH_1_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P1) \ +FOR_EACH_1_55(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_1_55(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(P1) \ +FOR_EACH_1_54(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + +#define FOR_EACH_1_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P1) \ +FOR_EACH_1_53(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_1_53(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(P1) \ +FOR_EACH_1_52(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + +#define FOR_EACH_1_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P1) \ +FOR_EACH_1_51(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_1_51(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(P1) \ +FOR_EACH_1_50(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + +#define FOR_EACH_1_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P1) \ +FOR_EACH_1_49(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_1_49(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(P1) \ +FOR_EACH_1_48(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + +#define FOR_EACH_1_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P1) \ +FOR_EACH_1_47(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_1_47(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(P1) \ +FOR_EACH_1_46(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + +#define FOR_EACH_1_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P1) \ +FOR_EACH_1_45(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_1_45(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(P1) \ +FOR_EACH_1_44(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + +#define FOR_EACH_1_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P1) \ +FOR_EACH_1_43(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_1_43(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(P1) \ +FOR_EACH_1_42(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + +#define FOR_EACH_1_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P1) \ +FOR_EACH_1_41(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_1_41(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(P1) \ +FOR_EACH_1_40(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + +#define FOR_EACH_1_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P1) \ +FOR_EACH_1_39(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_1_39(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(P1) \ +FOR_EACH_1_38(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + +#define FOR_EACH_1_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P1) \ +FOR_EACH_1_37(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_1_37(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(P1) \ +FOR_EACH_1_36(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + +#define FOR_EACH_1_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P1) \ +FOR_EACH_1_35(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_1_35(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(P1) \ +FOR_EACH_1_34(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + +#define FOR_EACH_1_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P1) \ +FOR_EACH_1_33(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_1_33(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(P1) \ +FOR_EACH_1_32(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + +#define FOR_EACH_1_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P1) \ +FOR_EACH_1_31(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_1_31(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(P1) \ +FOR_EACH_1_30(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + +#define FOR_EACH_1_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P1) \ +FOR_EACH_1_29(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_1_29(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(P1) \ +FOR_EACH_1_28(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + +#define FOR_EACH_1_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P1) \ +FOR_EACH_1_27(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_1_27(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(P1) \ +FOR_EACH_1_26(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + +#define FOR_EACH_1_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P1) \ +FOR_EACH_1_25(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_1_25(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(P1) \ +FOR_EACH_1_24(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + +#define FOR_EACH_1_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P1) \ +FOR_EACH_1_23(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_1_23(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(P1) \ +FOR_EACH_1_22(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + +#define FOR_EACH_1_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P1) \ +FOR_EACH_1_21(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_1_21(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(P1) \ +FOR_EACH_1_20(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + +#define FOR_EACH_1_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P1) \ +FOR_EACH_1_19(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_1_19(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(P1) \ +FOR_EACH_1_18(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + +#define FOR_EACH_1_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P1) \ +FOR_EACH_1_17(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_1_17(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(P1) \ +FOR_EACH_1_16(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + +#define FOR_EACH_1_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P1) \ +FOR_EACH_1_15(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_1_15(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(P1) \ +FOR_EACH_1_14(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + +#define FOR_EACH_1_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P1) \ +FOR_EACH_1_13(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_1_13(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(P1) \ +FOR_EACH_1_12(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + +#define FOR_EACH_1_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P1) \ +FOR_EACH_1_11(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_1_11(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(P1) \ +FOR_EACH_1_10(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + +#define FOR_EACH_1_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P1) \ +FOR_EACH_1_9(X, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_1_9(X, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(P1) \ +FOR_EACH_1_8(X, P2, P3, P4, P5, P6, P7, P8, P9) + +#define FOR_EACH_1_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P1) \ +FOR_EACH_1_7(X, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_1_7(X, P1, P2, P3, P4, P5, P6, P7) \ +X(P1) \ +FOR_EACH_1_6(X, P2, P3, P4, P5, P6, P7) + +#define FOR_EACH_1_6(X, P1, P2, P3, P4, P5, P6) \ +X(P1) \ +FOR_EACH_1_5(X, P2, P3, P4, P5, P6) + +#define FOR_EACH_1_5(X, P1, P2, P3, P4, P5) \ +X(P1) \ +FOR_EACH_1_4(X, P2, P3, P4, P5) + +#define FOR_EACH_1_4(X, P1, P2, P3, P4) \ +X(P1) \ +FOR_EACH_1_3(X, P2, P3, P4) + +#define FOR_EACH_1_3(X, P1, P2, P3) \ +X(P1) \ +FOR_EACH_1_2(X, P2, P3) + +#define FOR_EACH_1_2(X, P1, P2) \ +X(P1) \ +FOR_EACH_1_1(X, P2) + +#define FOR_EACH_1_1(X, P1) \ +X(P1) + +#ifdef _MSC_VER +#define FOR_EACH_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_,C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_,C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#define FOR_EACH_1_KEEP_1_124(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_123(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_1_KEEP_1_123(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_122(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + + +#define FOR_EACH_1_KEEP_1_122(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_121(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_1_KEEP_1_121(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_120(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + + +#define FOR_EACH_1_KEEP_1_120(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_119(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_1_KEEP_1_119(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_118(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + + +#define FOR_EACH_1_KEEP_1_118(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_117(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_1_KEEP_1_117(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_116(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + + +#define FOR_EACH_1_KEEP_1_116(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_115(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_1_KEEP_1_115(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_114(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + + +#define FOR_EACH_1_KEEP_1_114(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_113(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_1_KEEP_1_113(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_112(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + + +#define FOR_EACH_1_KEEP_1_112(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_111(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_1_KEEP_1_111(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_110(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + + +#define FOR_EACH_1_KEEP_1_110(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_109(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_1_KEEP_1_109(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_108(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + + +#define FOR_EACH_1_KEEP_1_108(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_107(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_1_KEEP_1_107(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_106(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + + +#define FOR_EACH_1_KEEP_1_106(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_105(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_1_KEEP_1_105(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_104(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + + +#define FOR_EACH_1_KEEP_1_104(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_103(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_1_KEEP_1_103(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_102(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + + +#define FOR_EACH_1_KEEP_1_102(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_101(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_1_KEEP_1_101(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_100(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + + +#define FOR_EACH_1_KEEP_1_100(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_99(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_1_KEEP_1_99(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_98(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + + +#define FOR_EACH_1_KEEP_1_98(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_97(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_1_KEEP_1_97(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_96(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + + +#define FOR_EACH_1_KEEP_1_96(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_95(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_1_KEEP_1_95(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_94(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + + +#define FOR_EACH_1_KEEP_1_94(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_93(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_1_KEEP_1_93(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_92(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + + +#define FOR_EACH_1_KEEP_1_92(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_91(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_1_KEEP_1_91(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_90(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + + +#define FOR_EACH_1_KEEP_1_90(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_89(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_1_KEEP_1_89(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_88(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + + +#define FOR_EACH_1_KEEP_1_88(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_87(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_1_KEEP_1_87(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_86(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + + +#define FOR_EACH_1_KEEP_1_86(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_85(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_1_KEEP_1_85(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_84(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + + +#define FOR_EACH_1_KEEP_1_84(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_83(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_1_KEEP_1_83(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_82(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + + +#define FOR_EACH_1_KEEP_1_82(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_81(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_1_KEEP_1_81(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_80(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + + +#define FOR_EACH_1_KEEP_1_80(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_79(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_1_KEEP_1_79(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_78(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + + +#define FOR_EACH_1_KEEP_1_78(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_77(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_1_KEEP_1_77(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_76(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + + +#define FOR_EACH_1_KEEP_1_76(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_75(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_1_KEEP_1_75(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_74(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + + +#define FOR_EACH_1_KEEP_1_74(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_73(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_1_KEEP_1_73(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_72(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + + +#define FOR_EACH_1_KEEP_1_72(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_71(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_1_KEEP_1_71(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_70(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + + +#define FOR_EACH_1_KEEP_1_70(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_69(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_1_KEEP_1_69(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_68(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + + +#define FOR_EACH_1_KEEP_1_68(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_67(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_1_KEEP_1_67(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_66(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + + +#define FOR_EACH_1_KEEP_1_66(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_65(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_1_KEEP_1_65(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_64(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + + +#define FOR_EACH_1_KEEP_1_64(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_63(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_1_KEEP_1_63(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_62(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + + +#define FOR_EACH_1_KEEP_1_62(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_61(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_1_KEEP_1_61(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_60(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + + +#define FOR_EACH_1_KEEP_1_60(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_59(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_1_KEEP_1_59(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_58(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + + +#define FOR_EACH_1_KEEP_1_58(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_57(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_1_KEEP_1_57(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_56(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + + +#define FOR_EACH_1_KEEP_1_56(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_55(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_1_KEEP_1_55(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_54(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + + +#define FOR_EACH_1_KEEP_1_54(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_53(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_1_KEEP_1_53(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_52(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + + +#define FOR_EACH_1_KEEP_1_52(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_51(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_1_KEEP_1_51(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_50(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + + +#define FOR_EACH_1_KEEP_1_50(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_49(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_1_KEEP_1_49(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_48(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + + +#define FOR_EACH_1_KEEP_1_48(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_47(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_1_KEEP_1_47(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_46(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + + +#define FOR_EACH_1_KEEP_1_46(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_45(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_1_KEEP_1_45(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_44(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + + +#define FOR_EACH_1_KEEP_1_44(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_43(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_1_KEEP_1_43(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_42(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + + +#define FOR_EACH_1_KEEP_1_42(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_41(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_1_KEEP_1_41(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_40(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + + +#define FOR_EACH_1_KEEP_1_40(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_39(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_1_KEEP_1_39(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_38(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + + +#define FOR_EACH_1_KEEP_1_38(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_37(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_1_KEEP_1_37(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_36(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + + +#define FOR_EACH_1_KEEP_1_36(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_35(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_1_KEEP_1_35(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_34(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + + +#define FOR_EACH_1_KEEP_1_34(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_33(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_1_KEEP_1_33(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_32(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + + +#define FOR_EACH_1_KEEP_1_32(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_31(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_1_KEEP_1_31(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_30(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + + +#define FOR_EACH_1_KEEP_1_30(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_29(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_1_KEEP_1_29(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_28(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + + +#define FOR_EACH_1_KEEP_1_28(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_27(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_1_KEEP_1_27(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_26(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + + +#define FOR_EACH_1_KEEP_1_26(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_25(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_1_KEEP_1_25(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_24(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + + +#define FOR_EACH_1_KEEP_1_24(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_23(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_1_KEEP_1_23(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_22(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + + +#define FOR_EACH_1_KEEP_1_22(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_21(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_1_KEEP_1_21(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_20(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + + +#define FOR_EACH_1_KEEP_1_20(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_19(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_1_KEEP_1_19(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_18(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + + +#define FOR_EACH_1_KEEP_1_18(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_17(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_1_KEEP_1_17(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_16(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + + +#define FOR_EACH_1_KEEP_1_16(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_15(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_1_KEEP_1_15(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_14(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + + +#define FOR_EACH_1_KEEP_1_14(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_13(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_1_KEEP_1_13(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_12(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + + +#define FOR_EACH_1_KEEP_1_12(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_11(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_1_KEEP_1_11(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_10(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + + +#define FOR_EACH_1_KEEP_1_10(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_9(X, keep, P2, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_1_KEEP_1_9(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_8(X, keep, P2, P3, P4, P5, P6, P7, P8, P9) + + +#define FOR_EACH_1_KEEP_1_8(X, keep, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_7(X, keep, P2, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_1_KEEP_1_7(X, keep, P1, P2, P3, P4, P5, P6, P7) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_6(X, keep, P2, P3, P4, P5, P6, P7) + + +#define FOR_EACH_1_KEEP_1_6(X, keep, P1, P2, P3, P4, P5, P6) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_5(X, keep, P2, P3, P4, P5, P6) + + +#define FOR_EACH_1_KEEP_1_5(X, keep, P1, P2, P3, P4, P5) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_4(X, keep, P2, P3, P4, P5) + + +#define FOR_EACH_1_KEEP_1_4(X, keep, P1, P2, P3, P4) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_3(X, keep, P2, P3, P4) + + +#define FOR_EACH_1_KEEP_1_3(X, keep, P1, P2, P3) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_2(X, keep, P2, P3) + + +#define FOR_EACH_1_KEEP_1_2(X, keep, P1, P2) \ +X(keep, P1) \ +FOR_EACH_1_KEEP_1_1(X, keep, P2) + + + +#define FOR_EACH_1_KEEP_1_1(X, keep, P1) \ +X(keep, P1) + +#ifdef _MSC_VER +#define FOR_EACH_1_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_1_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#define FOR_EACH_2_KEEP_1_124(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_122(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_KEEP_1_122(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_120(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_2_KEEP_1_120(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_118(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_2_KEEP_1_118(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_116(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_2_KEEP_1_116(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_114(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_2_KEEP_1_114(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_112(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_2_KEEP_1_112(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_110(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_2_KEEP_1_110(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_108(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_2_KEEP_1_108(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_106(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_2_KEEP_1_106(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_104(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_2_KEEP_1_104(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_102(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_2_KEEP_1_102(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_100(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_2_KEEP_1_100(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_98(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_2_KEEP_1_98(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_96(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_2_KEEP_1_96(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_94(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_2_KEEP_1_94(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_92(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_2_KEEP_1_92(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_90(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_2_KEEP_1_90(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_88(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_2_KEEP_1_88(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_86(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_2_KEEP_1_86(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_84(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_2_KEEP_1_84(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_82(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_2_KEEP_1_82(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_80(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_2_KEEP_1_80(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_78(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_2_KEEP_1_78(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_76(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_2_KEEP_1_76(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_74(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_2_KEEP_1_74(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_72(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_2_KEEP_1_72(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_70(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_2_KEEP_1_70(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_68(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_2_KEEP_1_68(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_66(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_2_KEEP_1_66(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_64(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_2_KEEP_1_64(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_62(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_2_KEEP_1_62(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_60(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_2_KEEP_1_60(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_58(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_2_KEEP_1_58(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_56(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_2_KEEP_1_56(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_54(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_2_KEEP_1_54(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_52(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_2_KEEP_1_52(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_50(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_2_KEEP_1_50(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_48(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_2_KEEP_1_48(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_46(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_2_KEEP_1_46(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_44(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_2_KEEP_1_44(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_42(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_2_KEEP_1_42(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_40(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_2_KEEP_1_40(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_38(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_2_KEEP_1_38(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_36(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_2_KEEP_1_36(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_34(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_2_KEEP_1_34(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_32(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_2_KEEP_1_32(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_30(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_2_KEEP_1_30(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_28(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_2_KEEP_1_28(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_26(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_2_KEEP_1_26(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_24(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_2_KEEP_1_24(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_22(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_2_KEEP_1_22(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_20(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_2_KEEP_1_20(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_18(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_2_KEEP_1_18(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_16(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_2_KEEP_1_16(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_14(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_2_KEEP_1_14(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_12(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_2_KEEP_1_12(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_10(X, keep, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_2_KEEP_1_10(X, keep, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_8(X, keep, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_2_KEEP_1_8(X, keep, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_6(X, keep, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_2_KEEP_1_6(X, keep, P1, P2, P3, P4, P5, P6) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_4(X, keep, P3, P4, P5, P6) + + +#define FOR_EACH_2_KEEP_1_4(X, keep, P1, P2, P3, P4) \ +X(keep, P1, P2) \ +FOR_EACH_2_KEEP_1_2(X, keep, P3, P4) + + + +#define FOR_EACH_2_KEEP_1_1(...) + +#define FOR_EACH_2_KEEP_1_0(...) + +#define FOR_EACH_2_KEEP_1_2(X, keep, P1, P2) \ + X(keep, P1, P2) \ + +#ifdef _MSC_VER +#define FOR_EACH_2_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2_KEEP_1(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_1_, C2(DEC,C1(COUNT_ARG(__VA_ARGS__)))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + + +#define FOR_EACH_2_KEEP_2_124(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_122(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_KEEP_2_122(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_120(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_2_KEEP_2_120(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_118(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + + +#define FOR_EACH_2_KEEP_2_118(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_116(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + + +#define FOR_EACH_2_KEEP_2_116(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_114(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + + +#define FOR_EACH_2_KEEP_2_114(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_112(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + + +#define FOR_EACH_2_KEEP_2_112(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_110(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + + +#define FOR_EACH_2_KEEP_2_110(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_108(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + + +#define FOR_EACH_2_KEEP_2_108(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_106(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + + +#define FOR_EACH_2_KEEP_2_106(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_104(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + + +#define FOR_EACH_2_KEEP_2_104(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_102(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + + +#define FOR_EACH_2_KEEP_2_102(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_100(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + + +#define FOR_EACH_2_KEEP_2_100(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_98(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + + +#define FOR_EACH_2_KEEP_2_98(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_96(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + + +#define FOR_EACH_2_KEEP_2_96(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_94(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + + +#define FOR_EACH_2_KEEP_2_94(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_92(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + + +#define FOR_EACH_2_KEEP_2_92(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_90(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + + +#define FOR_EACH_2_KEEP_2_90(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_88(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + + +#define FOR_EACH_2_KEEP_2_88(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_86(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + + +#define FOR_EACH_2_KEEP_2_86(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_84(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + + +#define FOR_EACH_2_KEEP_2_84(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_82(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + + +#define FOR_EACH_2_KEEP_2_82(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_80(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + + +#define FOR_EACH_2_KEEP_2_80(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_78(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + + +#define FOR_EACH_2_KEEP_2_78(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_76(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + + +#define FOR_EACH_2_KEEP_2_76(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_74(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + + +#define FOR_EACH_2_KEEP_2_74(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_72(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + + +#define FOR_EACH_2_KEEP_2_72(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_70(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + + +#define FOR_EACH_2_KEEP_2_70(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_68(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + + +#define FOR_EACH_2_KEEP_2_68(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_66(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + + +#define FOR_EACH_2_KEEP_2_66(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_64(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + + +#define FOR_EACH_2_KEEP_2_64(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_62(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + + +#define FOR_EACH_2_KEEP_2_62(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_60(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + + +#define FOR_EACH_2_KEEP_2_60(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_58(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + + +#define FOR_EACH_2_KEEP_2_58(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_56(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + + +#define FOR_EACH_2_KEEP_2_56(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_54(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + + +#define FOR_EACH_2_KEEP_2_54(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_52(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + + +#define FOR_EACH_2_KEEP_2_52(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_50(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + + +#define FOR_EACH_2_KEEP_2_50(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_48(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + + +#define FOR_EACH_2_KEEP_2_48(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_46(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + + +#define FOR_EACH_2_KEEP_2_46(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_44(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + + +#define FOR_EACH_2_KEEP_2_44(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_42(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + + +#define FOR_EACH_2_KEEP_2_42(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_40(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + + +#define FOR_EACH_2_KEEP_2_40(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_38(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + + +#define FOR_EACH_2_KEEP_2_38(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_36(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + + +#define FOR_EACH_2_KEEP_2_36(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_34(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + + +#define FOR_EACH_2_KEEP_2_34(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_32(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + + +#define FOR_EACH_2_KEEP_2_32(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_30(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + + +#define FOR_EACH_2_KEEP_2_30(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_28(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + + +#define FOR_EACH_2_KEEP_2_28(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_26(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + + +#define FOR_EACH_2_KEEP_2_26(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_24(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + + +#define FOR_EACH_2_KEEP_2_24(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_22(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + + +#define FOR_EACH_2_KEEP_2_22(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_20(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + + +#define FOR_EACH_2_KEEP_2_20(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_18(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + + +#define FOR_EACH_2_KEEP_2_18(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_16(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + + +#define FOR_EACH_2_KEEP_2_16(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_14(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + + +#define FOR_EACH_2_KEEP_2_14(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_12(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + + +#define FOR_EACH_2_KEEP_2_12(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_10(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + + +#define FOR_EACH_2_KEEP_2_10(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_8(X, keep1, keep2, P3, P4, P5, P6, P7, P8, P9, P10) + + +#define FOR_EACH_2_KEEP_2_8(X, keep1, keep2, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_6(X, keep1, keep2, P3, P4, P5, P6, P7, P8) + + +#define FOR_EACH_2_KEEP_2_6(X, keep1, keep2, P1, P2, P3, P4, P5, P6) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_4(X, keep1, keep2, P3, P4, P5, P6) + + +#define FOR_EACH_2_KEEP_2_4(X, keep1, keep2, P1, P2, P3, P4) \ +X(keep1, keep2, P1, P2) \ +FOR_EACH_2_KEEP_2_2(X, keep1, keep2, P3, P4) + + + +#define FOR_EACH_2_KEEP_2_1(...) + +#define FOR_EACH_2_KEEP_2_0(...) + +#define FOR_EACH_2_KEEP_2_2(X, keep1, keep2, P1, P2) \ + X(keep1, keep2, P1, P2) \ + +#ifdef _MSC_VER +#define FOR_EACH_2_KEEP_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_2_, C2(DEC,C2(DEC,C1(COUNT_ARG(__VA_ARGS__))))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2_KEEP_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_KEEP_2_, C2(DEC, C2(DEC,C1(COUNT_ARG(__VA_ARGS__))))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + + +#define FOR_EACH_2_0(...) + +#define FOR_EACH_2_2(X, P1, P2) \ +X(P1, P2) + +#define FOR_EACH_2_4(X, P1, P2, P3, P4) \ +X(P1, P2) \ +FOR_EACH_2_2(X, P3, P4) + +#define FOR_EACH_2_6(X, P1, P2, P3, P4, P5, P6) \ +X(P1, P2) \ +FOR_EACH_2_4(X, P3, P4, P5, P6) + +#define FOR_EACH_2_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P1, P2) \ +FOR_EACH_2_6(X, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P1, P2) \ +FOR_EACH_2_8(X, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P1, P2) \ +FOR_EACH_2_10(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P1, P2) \ +FOR_EACH_2_12(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P1, P2) \ +FOR_EACH_2_14(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P1, P2) \ +FOR_EACH_2_16(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P1, P2) \ +FOR_EACH_2_18(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P1, P2) \ +FOR_EACH_2_20(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P1, P2) \ +FOR_EACH_2_22(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P1, P2) \ +FOR_EACH_2_24(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P1, P2) \ +FOR_EACH_2_26(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P1, P2) \ +FOR_EACH_2_28(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P1, P2) \ +FOR_EACH_2_30(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P1, P2) \ +FOR_EACH_2_32(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P1, P2) \ +FOR_EACH_2_34(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P1, P2) \ +FOR_EACH_2_36(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P1, P2) \ +FOR_EACH_2_38(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P1, P2) \ +FOR_EACH_2_40(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P1, P2) \ +FOR_EACH_2_42(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P1, P2) \ +FOR_EACH_2_44(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P1, P2) \ +FOR_EACH_2_46(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P1, P2) \ +FOR_EACH_2_48(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P1, P2) \ +FOR_EACH_2_50(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P1, P2) \ +FOR_EACH_2_52(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P1, P2) \ +FOR_EACH_2_54(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P1, P2) \ +FOR_EACH_2_56(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P1, P2) \ +FOR_EACH_2_58(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P1, P2) \ +FOR_EACH_2_60(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P1, P2) \ +FOR_EACH_2_62(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P1, P2) \ +FOR_EACH_2_64(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P1, P2) \ +FOR_EACH_2_66(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P1, P2) \ +FOR_EACH_2_68(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P1, P2) \ +FOR_EACH_2_70(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P1, P2) \ +FOR_EACH_2_72(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P1, P2) \ +FOR_EACH_2_74(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P1, P2) \ +FOR_EACH_2_76(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P1, P2) \ +FOR_EACH_2_78(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P1, P2) \ +FOR_EACH_2_80(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P1, P2) \ +FOR_EACH_2_82(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P1, P2) \ +FOR_EACH_2_84(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P1, P2) \ +FOR_EACH_2_86(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P1, P2) \ +FOR_EACH_2_88(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P1, P2) \ +FOR_EACH_2_90(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P1, P2) \ +FOR_EACH_2_92(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P1, P2) \ +FOR_EACH_2_94(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P1, P2) \ +FOR_EACH_2_96(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P1, P2) \ +FOR_EACH_2_98(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P1, P2) \ +FOR_EACH_2_100(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P1, P2) \ +FOR_EACH_2_102(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P1, P2) \ +FOR_EACH_2_104(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P1, P2) \ +FOR_EACH_2_106(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P1, P2) \ +FOR_EACH_2_108(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P1, P2) \ +FOR_EACH_2_110(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P1, P2) \ +FOR_EACH_2_112(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P1, P2) \ +FOR_EACH_2_114(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P1, P2) \ +FOR_EACH_2_116(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P1, P2) \ +FOR_EACH_2_118(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P1, P2) \ +FOR_EACH_2_120(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_2_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P1, P2) \ +FOR_EACH_2_122(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_REVERSE_0(...) + +#define FOR_EACH_2_REVERSE_2(X, P1, P2) \ +X(P1, P2) + +#define FOR_EACH_2_REVERSE_4(X, P1, P2, P3, P4) \ +X(P3, P4) \ +FOR_EACH_2_REVERSE_2(X, P1, P2) + +#define FOR_EACH_2_REVERSE_6(X, P1, P2, P3, P4, P5, P6) \ +X(P5, P6) \ +FOR_EACH_2_REVERSE_4(X, P1, P2, P3, P4) + +#define FOR_EACH_2_REVERSE_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(P7, P8) \ +FOR_EACH_2_REVERSE_6(X, P1, P2, P3, P4, P5, P6) + +#define FOR_EACH_2_REVERSE_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(P9, P10) \ +FOR_EACH_2_REVERSE_8(X, P1, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_REVERSE_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(P11, P12) \ +FOR_EACH_2_REVERSE_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_REVERSE_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(P13, P14) \ +FOR_EACH_2_REVERSE_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_REVERSE_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(P15, P16) \ +FOR_EACH_2_REVERSE_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_REVERSE_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(P17, P18) \ +FOR_EACH_2_REVERSE_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_REVERSE_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(P19, P20) \ +FOR_EACH_2_REVERSE_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_REVERSE_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(P21, P22) \ +FOR_EACH_2_REVERSE_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_REVERSE_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(P23, P24) \ +FOR_EACH_2_REVERSE_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_REVERSE_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(P25, P26) \ +FOR_EACH_2_REVERSE_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_REVERSE_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(P27, P28) \ +FOR_EACH_2_REVERSE_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_REVERSE_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(P29, P30) \ +FOR_EACH_2_REVERSE_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_REVERSE_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(P31, P32) \ +FOR_EACH_2_REVERSE_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_REVERSE_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(P33, P34) \ +FOR_EACH_2_REVERSE_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_REVERSE_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(P35, P36) \ +FOR_EACH_2_REVERSE_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_REVERSE_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(P37, P38) \ +FOR_EACH_2_REVERSE_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_REVERSE_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(P39, P40) \ +FOR_EACH_2_REVERSE_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_REVERSE_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(P41, P42) \ +FOR_EACH_2_REVERSE_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_REVERSE_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(P43, P44) \ +FOR_EACH_2_REVERSE_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_REVERSE_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(P45, P46) \ +FOR_EACH_2_REVERSE_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_REVERSE_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(P47, P48) \ +FOR_EACH_2_REVERSE_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_REVERSE_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(P49, P50) \ +FOR_EACH_2_REVERSE_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_REVERSE_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(P51, P52) \ +FOR_EACH_2_REVERSE_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_REVERSE_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(P53, P54) \ +FOR_EACH_2_REVERSE_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_REVERSE_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(P55, P56) \ +FOR_EACH_2_REVERSE_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_REVERSE_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(P57, P58) \ +FOR_EACH_2_REVERSE_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_REVERSE_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(P59, P60) \ +FOR_EACH_2_REVERSE_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_REVERSE_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(P61, P62) \ +FOR_EACH_2_REVERSE_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_REVERSE_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(P63, P64) \ +FOR_EACH_2_REVERSE_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_REVERSE_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(P65, P66) \ +FOR_EACH_2_REVERSE_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_REVERSE_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(P67, P68) \ +FOR_EACH_2_REVERSE_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_REVERSE_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(P69, P70) \ +FOR_EACH_2_REVERSE_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_REVERSE_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(P71, P72) \ +FOR_EACH_2_REVERSE_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_REVERSE_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(P73, P74) \ +FOR_EACH_2_REVERSE_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_REVERSE_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(P75, P76) \ +FOR_EACH_2_REVERSE_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_REVERSE_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(P77, P78) \ +FOR_EACH_2_REVERSE_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_REVERSE_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(P79, P80) \ +FOR_EACH_2_REVERSE_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_REVERSE_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(P81, P82) \ +FOR_EACH_2_REVERSE_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_REVERSE_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(P83, P84) \ +FOR_EACH_2_REVERSE_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_REVERSE_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(P85, P86) \ +FOR_EACH_2_REVERSE_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_REVERSE_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(P87, P88) \ +FOR_EACH_2_REVERSE_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_REVERSE_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(P89, P90) \ +FOR_EACH_2_REVERSE_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_REVERSE_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(P91, P92) \ +FOR_EACH_2_REVERSE_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_REVERSE_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(P93, P94) \ +FOR_EACH_2_REVERSE_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_REVERSE_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(P95, P96) \ +FOR_EACH_2_REVERSE_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_REVERSE_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(P97, P98) \ +FOR_EACH_2_REVERSE_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_REVERSE_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(P99, P100) \ +FOR_EACH_2_REVERSE_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_REVERSE_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(P101, P102) \ +FOR_EACH_2_REVERSE_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_REVERSE_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(P103, P104) \ +FOR_EACH_2_REVERSE_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_REVERSE_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(P105, P106) \ +FOR_EACH_2_REVERSE_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_REVERSE_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(P107, P108) \ +FOR_EACH_2_REVERSE_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_REVERSE_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(P109, P110) \ +FOR_EACH_2_REVERSE_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_REVERSE_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(P111, P112) \ +FOR_EACH_2_REVERSE_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_REVERSE_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(P113, P114) \ +FOR_EACH_2_REVERSE_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_REVERSE_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(P115, P116) \ +FOR_EACH_2_REVERSE_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_REVERSE_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(P117, P118) \ +FOR_EACH_2_REVERSE_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_REVERSE_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(P119, P120) \ +FOR_EACH_2_REVERSE_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_REVERSE_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(P121, P122) \ +FOR_EACH_2_REVERSE_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_REVERSE_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(P123, P124) \ +FOR_EACH_2_REVERSE_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + + +#define FOR_EACH_1_COUNTED_0(...) + +#define FOR_EACH_1_COUNTED_1(X, P1) \ + X(1, P1) + +#define FOR_EACH_1_COUNTED_2(X, P1, P2) \ +X(2, P1) \ +FOR_EACH_1_COUNTED_1(X, P2) + +#define FOR_EACH_1_COUNTED_3(X, P1, P2, P3) \ +X(3, P1) \ +FOR_EACH_1_COUNTED_2(X, P2, P3) + +#define FOR_EACH_1_COUNTED_4(X, P1, P2, P3, P4) \ +X(4, P1) \ +FOR_EACH_1_COUNTED_3(X, P2, P3, P4) + +#define FOR_EACH_1_COUNTED_5(X, P1, P2, P3, P4, P5) \ +X(5, P1) \ +FOR_EACH_1_COUNTED_4(X, P2, P3, P4, P5) + +#define FOR_EACH_1_COUNTED_6(X, P1, P2, P3, P4, P5, P6) \ +X(6, P1) \ +FOR_EACH_1_COUNTED_5(X, P2, P3, P4, P5, P6) + +#define FOR_EACH_1_COUNTED_7(X, P1, P2, P3, P4, P5, P6, P7) \ +X(7, P1) \ +FOR_EACH_1_COUNTED_6(X, P2, P3, P4, P5, P6, P7) + +#define FOR_EACH_1_COUNTED_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(8, P1) \ +FOR_EACH_1_COUNTED_7(X, P2, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_1_COUNTED_9(X, P1, P2, P3, P4, P5, P6, P7, P8, P9) \ +X(9, P1) \ +FOR_EACH_1_COUNTED_8(X, P2, P3, P4, P5, P6, P7, P8, P9) + +#define FOR_EACH_1_COUNTED_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(10, P1) \ +FOR_EACH_1_COUNTED_9(X, P2, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_1_COUNTED_11(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) \ +X(11, P1) \ +FOR_EACH_1_COUNTED_10(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) + +#define FOR_EACH_1_COUNTED_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(12, P1) \ +FOR_EACH_1_COUNTED_11(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_1_COUNTED_13(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) \ +X(13, P1) \ +FOR_EACH_1_COUNTED_12(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) + +#define FOR_EACH_1_COUNTED_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(14, P1) \ +FOR_EACH_1_COUNTED_13(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_1_COUNTED_15(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) \ +X(15, P1) \ +FOR_EACH_1_COUNTED_14(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) + +#define FOR_EACH_1_COUNTED_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(16, P1) \ +FOR_EACH_1_COUNTED_15(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_1_COUNTED_17(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) \ +X(17, P1) \ +FOR_EACH_1_COUNTED_16(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) + +#define FOR_EACH_1_COUNTED_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(18, P1) \ +FOR_EACH_1_COUNTED_17(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_1_COUNTED_19(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) \ +X(19, P1) \ +FOR_EACH_1_COUNTED_18(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) + +#define FOR_EACH_1_COUNTED_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(20, P1) \ +FOR_EACH_1_COUNTED_19(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_1_COUNTED_21(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) \ +X(21, P1) \ +FOR_EACH_1_COUNTED_20(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) + +#define FOR_EACH_1_COUNTED_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(22, P1) \ +FOR_EACH_1_COUNTED_21(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_1_COUNTED_23(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) \ +X(23, P1) \ +FOR_EACH_1_COUNTED_22(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23) + +#define FOR_EACH_1_COUNTED_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(24, P1) \ +FOR_EACH_1_COUNTED_23(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_1_COUNTED_25(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) \ +X(25, P1) \ +FOR_EACH_1_COUNTED_24(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25) + +#define FOR_EACH_1_COUNTED_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(26, P1) \ +FOR_EACH_1_COUNTED_25(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_1_COUNTED_27(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) \ +X(27, P1) \ +FOR_EACH_1_COUNTED_26(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27) + +#define FOR_EACH_1_COUNTED_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(28, P1) \ +FOR_EACH_1_COUNTED_27(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_1_COUNTED_29(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) \ +X(29, P1) \ +FOR_EACH_1_COUNTED_28(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29) + +#define FOR_EACH_1_COUNTED_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(30, P1) \ +FOR_EACH_1_COUNTED_29(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_1_COUNTED_31(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) \ +X(31, P1) \ +FOR_EACH_1_COUNTED_30(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31) + +#define FOR_EACH_1_COUNTED_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(32, P1) \ +FOR_EACH_1_COUNTED_31(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_1_COUNTED_33(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) \ +X(33, P1) \ +FOR_EACH_1_COUNTED_32(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33) + +#define FOR_EACH_1_COUNTED_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(34, P1) \ +FOR_EACH_1_COUNTED_33(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_1_COUNTED_35(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) \ +X(35, P1) \ +FOR_EACH_1_COUNTED_34(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35) + +#define FOR_EACH_1_COUNTED_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(36, P1) \ +FOR_EACH_1_COUNTED_35(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_1_COUNTED_37(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) \ +X(37, P1) \ +FOR_EACH_1_COUNTED_36(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37) + +#define FOR_EACH_1_COUNTED_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(38, P1) \ +FOR_EACH_1_COUNTED_37(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_1_COUNTED_39(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) \ +X(39, P1) \ +FOR_EACH_1_COUNTED_38(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39) + +#define FOR_EACH_1_COUNTED_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(40, P1) \ +FOR_EACH_1_COUNTED_39(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_1_COUNTED_41(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) \ +X(41, P1) \ +FOR_EACH_1_COUNTED_40(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41) + +#define FOR_EACH_1_COUNTED_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(42, P1) \ +FOR_EACH_1_COUNTED_41(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_1_COUNTED_43(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) \ +X(43, P1) \ +FOR_EACH_1_COUNTED_42(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43) + +#define FOR_EACH_1_COUNTED_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(44, P1) \ +FOR_EACH_1_COUNTED_43(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_1_COUNTED_45(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) \ +X(45, P1) \ +FOR_EACH_1_COUNTED_44(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45) + +#define FOR_EACH_1_COUNTED_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(46, P1) \ +FOR_EACH_1_COUNTED_45(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_1_COUNTED_47(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) \ +X(47, P1) \ +FOR_EACH_1_COUNTED_46(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47) + +#define FOR_EACH_1_COUNTED_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(48, P1) \ +FOR_EACH_1_COUNTED_47(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_1_COUNTED_49(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) \ +X(49, P1) \ +FOR_EACH_1_COUNTED_48(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49) + +#define FOR_EACH_1_COUNTED_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(50, P1) \ +FOR_EACH_1_COUNTED_49(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_1_COUNTED_51(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) \ +X(51, P1) \ +FOR_EACH_1_COUNTED_50(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51) + +#define FOR_EACH_1_COUNTED_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(52, P1) \ +FOR_EACH_1_COUNTED_51(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_1_COUNTED_53(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) \ +X(53, P1) \ +FOR_EACH_1_COUNTED_52(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53) + +#define FOR_EACH_1_COUNTED_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(54, P1) \ +FOR_EACH_1_COUNTED_53(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_1_COUNTED_55(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) \ +X(55, P1) \ +FOR_EACH_1_COUNTED_54(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55) + +#define FOR_EACH_1_COUNTED_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(56, P1) \ +FOR_EACH_1_COUNTED_55(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_1_COUNTED_57(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) \ +X(57, P1) \ +FOR_EACH_1_COUNTED_56(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57) + +#define FOR_EACH_1_COUNTED_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(58, P1) \ +FOR_EACH_1_COUNTED_57(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_1_COUNTED_59(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) \ +X(59, P1) \ +FOR_EACH_1_COUNTED_58(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59) + +#define FOR_EACH_1_COUNTED_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(60, P1) \ +FOR_EACH_1_COUNTED_59(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_1_COUNTED_61(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) \ +X(61, P1) \ +FOR_EACH_1_COUNTED_60(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61) + +#define FOR_EACH_1_COUNTED_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(62, P1) \ +FOR_EACH_1_COUNTED_61(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_1_COUNTED_63(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) \ +X(63, P1) \ +FOR_EACH_1_COUNTED_62(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63) + +#define FOR_EACH_1_COUNTED_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(64, P1) \ +FOR_EACH_1_COUNTED_63(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_1_COUNTED_65(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) \ +X(65, P1) \ +FOR_EACH_1_COUNTED_64(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65) + +#define FOR_EACH_1_COUNTED_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(66, P1) \ +FOR_EACH_1_COUNTED_65(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_1_COUNTED_67(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) \ +X(67, P1) \ +FOR_EACH_1_COUNTED_66(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67) + +#define FOR_EACH_1_COUNTED_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(68, P1) \ +FOR_EACH_1_COUNTED_67(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_1_COUNTED_69(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) \ +X(69, P1) \ +FOR_EACH_1_COUNTED_68(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69) + +#define FOR_EACH_1_COUNTED_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(70, P1) \ +FOR_EACH_1_COUNTED_69(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_1_COUNTED_71(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) \ +X(71, P1) \ +FOR_EACH_1_COUNTED_70(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71) + +#define FOR_EACH_1_COUNTED_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(72, P1) \ +FOR_EACH_1_COUNTED_71(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_1_COUNTED_73(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) \ +X(73, P1) \ +FOR_EACH_1_COUNTED_72(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73) + +#define FOR_EACH_1_COUNTED_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(74, P1) \ +FOR_EACH_1_COUNTED_73(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_1_COUNTED_75(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) \ +X(75, P1) \ +FOR_EACH_1_COUNTED_74(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75) + +#define FOR_EACH_1_COUNTED_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(76, P1) \ +FOR_EACH_1_COUNTED_75(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_1_COUNTED_77(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) \ +X(77, P1) \ +FOR_EACH_1_COUNTED_76(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77) + +#define FOR_EACH_1_COUNTED_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(78, P1) \ +FOR_EACH_1_COUNTED_77(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_1_COUNTED_79(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) \ +X(79, P1) \ +FOR_EACH_1_COUNTED_78(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79) + +#define FOR_EACH_1_COUNTED_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(80, P1) \ +FOR_EACH_1_COUNTED_79(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_1_COUNTED_81(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) \ +X(81, P1) \ +FOR_EACH_1_COUNTED_80(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81) + +#define FOR_EACH_1_COUNTED_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(82, P1) \ +FOR_EACH_1_COUNTED_81(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_1_COUNTED_83(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) \ +X(83, P1) \ +FOR_EACH_1_COUNTED_82(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83) + +#define FOR_EACH_1_COUNTED_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(84, P1) \ +FOR_EACH_1_COUNTED_83(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_1_COUNTED_85(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) \ +X(85, P1) \ +FOR_EACH_1_COUNTED_84(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85) + +#define FOR_EACH_1_COUNTED_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(86, P1) \ +FOR_EACH_1_COUNTED_85(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_1_COUNTED_87(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) \ +X(87, P1) \ +FOR_EACH_1_COUNTED_86(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87) + +#define FOR_EACH_1_COUNTED_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(88, P1) \ +FOR_EACH_1_COUNTED_87(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_1_COUNTED_89(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) \ +X(89, P1) \ +FOR_EACH_1_COUNTED_88(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89) + +#define FOR_EACH_1_COUNTED_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(90, P1) \ +FOR_EACH_1_COUNTED_89(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_1_COUNTED_91(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) \ +X(91, P1) \ +FOR_EACH_1_COUNTED_90(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91) + +#define FOR_EACH_1_COUNTED_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(92, P1) \ +FOR_EACH_1_COUNTED_91(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_1_COUNTED_93(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) \ +X(93, P1) \ +FOR_EACH_1_COUNTED_92(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93) + +#define FOR_EACH_1_COUNTED_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(94, P1) \ +FOR_EACH_1_COUNTED_93(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_1_COUNTED_95(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) \ +X(95, P1) \ +FOR_EACH_1_COUNTED_94(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95) + +#define FOR_EACH_1_COUNTED_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(96, P1) \ +FOR_EACH_1_COUNTED_95(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_1_COUNTED_97(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) \ +X(97, P1) \ +FOR_EACH_1_COUNTED_96(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97) + +#define FOR_EACH_1_COUNTED_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(98, P1) \ +FOR_EACH_1_COUNTED_97(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_1_COUNTED_99(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) \ +X(99, P1) \ +FOR_EACH_1_COUNTED_98(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99) + +#define FOR_EACH_1_COUNTED_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(100, P1) \ +FOR_EACH_1_COUNTED_99(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_1_COUNTED_101(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) \ +X(101, P1) \ +FOR_EACH_1_COUNTED_100(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101) + +#define FOR_EACH_1_COUNTED_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(102, P1) \ +FOR_EACH_1_COUNTED_101(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_1_COUNTED_103(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) \ +X(103, P1) \ +FOR_EACH_1_COUNTED_102(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103) + +#define FOR_EACH_1_COUNTED_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(104, P1) \ +FOR_EACH_1_COUNTED_103(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_1_COUNTED_105(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) \ +X(105, P1) \ +FOR_EACH_1_COUNTED_104(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105) + +#define FOR_EACH_1_COUNTED_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(106, P1) \ +FOR_EACH_1_COUNTED_105(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_1_COUNTED_107(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) \ +X(107, P1) \ +FOR_EACH_1_COUNTED_106(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107) + +#define FOR_EACH_1_COUNTED_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(108, P1) \ +FOR_EACH_1_COUNTED_107(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_1_COUNTED_109(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) \ +X(109, P1) \ +FOR_EACH_1_COUNTED_108(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109) + +#define FOR_EACH_1_COUNTED_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(110, P1) \ +FOR_EACH_1_COUNTED_109(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_1_COUNTED_111(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) \ +X(111, P1) \ +FOR_EACH_1_COUNTED_110(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111) + +#define FOR_EACH_1_COUNTED_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(112, P1) \ +FOR_EACH_1_COUNTED_111(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_1_COUNTED_113(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) \ +X(113, P1) \ +FOR_EACH_1_COUNTED_112(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113) + +#define FOR_EACH_1_COUNTED_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(114, P1) \ +FOR_EACH_1_COUNTED_113(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_1_COUNTED_115(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) \ +X(115, P1) \ +FOR_EACH_1_COUNTED_114(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115) + +#define FOR_EACH_1_COUNTED_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(116, P1) \ +FOR_EACH_1_COUNTED_115(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_1_COUNTED_117(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) \ +X(117, P1) \ +FOR_EACH_1_COUNTED_116(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117) + +#define FOR_EACH_1_COUNTED_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(118, P1) \ +FOR_EACH_1_COUNTED_117(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_1_COUNTED_119(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) \ +X(119, P1) \ +FOR_EACH_1_COUNTED_118(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119) + +#define FOR_EACH_1_COUNTED_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(120, P1) \ +FOR_EACH_1_COUNTED_119(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_1_COUNTED_121(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) \ +X(121, P1) \ +FOR_EACH_1_COUNTED_120(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121) + +#define FOR_EACH_1_COUNTED_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(122, P1) \ +FOR_EACH_1_COUNTED_121(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_1_COUNTED_123(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) \ +X(123, P1) \ +FOR_EACH_1_COUNTED_122(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123) + +#define FOR_EACH_1_COUNTED_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(124, P1) \ +FOR_EACH_1_COUNTED_123(X, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#define FOR_EACH_2_COUNTED_0(...) + +#define FOR_EACH_2_COUNTED_2(X, P1, P2) \ + X(2, P1, P2) + +#define FOR_EACH_2_COUNTED_4(X, P1, P2, P3, P4) \ +X(4, P1, P2) \ +FOR_EACH_2_COUNTED_2(X, P3, P4) + +#define FOR_EACH_2_COUNTED_6(X, P1, P2, P3, P4, P5, P6) \ +X(6, P1, P2) \ +FOR_EACH_2_COUNTED_4(X, P3, P4, P5, P6) + +#define FOR_EACH_2_COUNTED_8(X, P1, P2, P3, P4, P5, P6, P7, P8) \ +X(8, P1, P2) \ +FOR_EACH_2_COUNTED_6(X, P3, P4, P5, P6, P7, P8) + +#define FOR_EACH_2_COUNTED_10(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \ +X(10, P1, P2) \ +FOR_EACH_2_COUNTED_8(X, P3, P4, P5, P6, P7, P8, P9, P10) + +#define FOR_EACH_2_COUNTED_12(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) \ +X(12, P1, P2) \ +FOR_EACH_2_COUNTED_10(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) + +#define FOR_EACH_2_COUNTED_14(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) \ +X(14, P1, P2) \ +FOR_EACH_2_COUNTED_12(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) + +#define FOR_EACH_2_COUNTED_16(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) \ +X(16, P1, P2) \ +FOR_EACH_2_COUNTED_14(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) + +#define FOR_EACH_2_COUNTED_18(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) \ +X(18, P1, P2) \ +FOR_EACH_2_COUNTED_16(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) + +#define FOR_EACH_2_COUNTED_20(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) \ +X(20, P1, P2) \ +FOR_EACH_2_COUNTED_18(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) + +#define FOR_EACH_2_COUNTED_22(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) \ +X(22, P1, P2) \ +FOR_EACH_2_COUNTED_20(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) + +#define FOR_EACH_2_COUNTED_24(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) \ +X(24, P1, P2) \ +FOR_EACH_2_COUNTED_22(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24) + +#define FOR_EACH_2_COUNTED_26(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) \ +X(26, P1, P2) \ +FOR_EACH_2_COUNTED_24(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26) + +#define FOR_EACH_2_COUNTED_28(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) \ +X(28, P1, P2) \ +FOR_EACH_2_COUNTED_26(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28) + +#define FOR_EACH_2_COUNTED_30(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) \ +X(30, P1, P2) \ +FOR_EACH_2_COUNTED_28(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30) + +#define FOR_EACH_2_COUNTED_32(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) \ +X(32, P1, P2) \ +FOR_EACH_2_COUNTED_30(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32) + +#define FOR_EACH_2_COUNTED_34(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) \ +X(34, P1, P2) \ +FOR_EACH_2_COUNTED_32(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34) + +#define FOR_EACH_2_COUNTED_36(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) \ +X(36, P1, P2) \ +FOR_EACH_2_COUNTED_34(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36) + +#define FOR_EACH_2_COUNTED_38(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) \ +X(38, P1, P2) \ +FOR_EACH_2_COUNTED_36(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38) + +#define FOR_EACH_2_COUNTED_40(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) \ +X(40, P1, P2) \ +FOR_EACH_2_COUNTED_38(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40) + +#define FOR_EACH_2_COUNTED_42(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) \ +X(42, P1, P2) \ +FOR_EACH_2_COUNTED_40(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42) + +#define FOR_EACH_2_COUNTED_44(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) \ +X(44, P1, P2) \ +FOR_EACH_2_COUNTED_42(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44) + +#define FOR_EACH_2_COUNTED_46(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) \ +X(46, P1, P2) \ +FOR_EACH_2_COUNTED_44(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46) + +#define FOR_EACH_2_COUNTED_48(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) \ +X(48, P1, P2) \ +FOR_EACH_2_COUNTED_46(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48) + +#define FOR_EACH_2_COUNTED_50(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) \ +X(50, P1, P2) \ +FOR_EACH_2_COUNTED_48(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50) + +#define FOR_EACH_2_COUNTED_52(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) \ +X(52, P1, P2) \ +FOR_EACH_2_COUNTED_50(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52) + +#define FOR_EACH_2_COUNTED_54(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) \ +X(54, P1, P2) \ +FOR_EACH_2_COUNTED_52(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54) + +#define FOR_EACH_2_COUNTED_56(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) \ +X(56, P1, P2) \ +FOR_EACH_2_COUNTED_54(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56) + +#define FOR_EACH_2_COUNTED_58(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) \ +X(58, P1, P2) \ +FOR_EACH_2_COUNTED_56(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58) + +#define FOR_EACH_2_COUNTED_60(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) \ +X(60, P1, P2) \ +FOR_EACH_2_COUNTED_58(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60) + +#define FOR_EACH_2_COUNTED_62(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) \ +X(62, P1, P2) \ +FOR_EACH_2_COUNTED_60(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62) + +#define FOR_EACH_2_COUNTED_64(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) \ +X(64, P1, P2) \ +FOR_EACH_2_COUNTED_62(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64) + +#define FOR_EACH_2_COUNTED_66(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) \ +X(66, P1, P2) \ +FOR_EACH_2_COUNTED_64(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66) + +#define FOR_EACH_2_COUNTED_68(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) \ +X(68, P1, P2) \ +FOR_EACH_2_COUNTED_66(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68) + +#define FOR_EACH_2_COUNTED_70(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) \ +X(70, P1, P2) \ +FOR_EACH_2_COUNTED_68(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70) + +#define FOR_EACH_2_COUNTED_72(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) \ +X(72, P1, P2) \ +FOR_EACH_2_COUNTED_70(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72) + +#define FOR_EACH_2_COUNTED_74(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) \ +X(74, P1, P2) \ +FOR_EACH_2_COUNTED_72(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74) + +#define FOR_EACH_2_COUNTED_76(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) \ +X(76, P1, P2) \ +FOR_EACH_2_COUNTED_74(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76) + +#define FOR_EACH_2_COUNTED_78(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) \ +X(78, P1, P2) \ +FOR_EACH_2_COUNTED_76(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78) + +#define FOR_EACH_2_COUNTED_80(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) \ +X(80, P1, P2) \ +FOR_EACH_2_COUNTED_78(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80) + +#define FOR_EACH_2_COUNTED_82(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) \ +X(82, P1, P2) \ +FOR_EACH_2_COUNTED_80(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82) + +#define FOR_EACH_2_COUNTED_84(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) \ +X(84, P1, P2) \ +FOR_EACH_2_COUNTED_82(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84) + +#define FOR_EACH_2_COUNTED_86(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) \ +X(86, P1, P2) \ +FOR_EACH_2_COUNTED_84(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86) + +#define FOR_EACH_2_COUNTED_88(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) \ +X(88, P1, P2) \ +FOR_EACH_2_COUNTED_86(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88) + +#define FOR_EACH_2_COUNTED_90(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) \ +X(90, P1, P2) \ +FOR_EACH_2_COUNTED_88(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90) + +#define FOR_EACH_2_COUNTED_92(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) \ +X(92, P1, P2) \ +FOR_EACH_2_COUNTED_90(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92) + +#define FOR_EACH_2_COUNTED_94(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) \ +X(94, P1, P2) \ +FOR_EACH_2_COUNTED_92(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94) + +#define FOR_EACH_2_COUNTED_96(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) \ +X(96, P1, P2) \ +FOR_EACH_2_COUNTED_94(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96) + +#define FOR_EACH_2_COUNTED_98(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) \ +X(98, P1, P2) \ +FOR_EACH_2_COUNTED_96(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98) + +#define FOR_EACH_2_COUNTED_100(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) \ +X(100, P1, P2) \ +FOR_EACH_2_COUNTED_98(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100) + +#define FOR_EACH_2_COUNTED_102(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) \ +X(102, P1, P2) \ +FOR_EACH_2_COUNTED_100(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102) + +#define FOR_EACH_2_COUNTED_104(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) \ +X(104, P1, P2) \ +FOR_EACH_2_COUNTED_102(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104) + +#define FOR_EACH_2_COUNTED_106(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) \ +X(106, P1, P2) \ +FOR_EACH_2_COUNTED_104(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106) + +#define FOR_EACH_2_COUNTED_108(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) \ +X(108, P1, P2) \ +FOR_EACH_2_COUNTED_106(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108) + +#define FOR_EACH_2_COUNTED_110(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) \ +X(110, P1, P2) \ +FOR_EACH_2_COUNTED_108(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110) + +#define FOR_EACH_2_COUNTED_112(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) \ +X(112, P1, P2) \ +FOR_EACH_2_COUNTED_110(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112) + +#define FOR_EACH_2_COUNTED_114(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) \ +X(114, P1, P2) \ +FOR_EACH_2_COUNTED_112(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114) + +#define FOR_EACH_2_COUNTED_116(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) \ +X(116, P1, P2) \ +FOR_EACH_2_COUNTED_114(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116) + +#define FOR_EACH_2_COUNTED_118(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) \ +X(118, P1, P2) \ +FOR_EACH_2_COUNTED_116(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118) + +#define FOR_EACH_2_COUNTED_120(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) \ +X(120, P1, P2) \ +FOR_EACH_2_COUNTED_118(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120) + +#define FOR_EACH_2_COUNTED_122(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) \ +X(122, P1, P2) \ +FOR_EACH_2_COUNTED_120(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122) + +#define FOR_EACH_2_COUNTED_124(X, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) \ +X(124, P1, P2) \ +FOR_EACH_2_COUNTED_122(X, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, P36, P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54, P55, P56, P57, P58, P59, P60, P61, P62, P63, P64, P65, P66, P67, P68, P69, P70, P71, P72, P73, P74, P75, P76, P77, P78, P79, P80, P81, P82, P83, P84, P85, P86, P87, P88, P89, P90, P91, P92, P93, P94, P95, P96, P97, P98, P99, P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, P120, P121, P122, P123, P124) + + +#ifdef _MSC_VER +#define FOR_EACH_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +/*the COUNTED breed of FOR_EACH macro invokes a macro with 3 parameters: 1st being the count of invocation. For example. +FOR_EACH_2_COUNTER(MACRO, a,b,c,d,e,f) will result in +MACRO(6, a,b) +MACRO(4, c,d) +MACRO(2, e,f) +This macro exists because we need a "stop condition" in outputting COMMA... when calling a function f(a,b,c,d) cannot be f(a,b,c,d,) <=doesn't compile (as opposed to enum definition) +*/ +#define FOR_EACH_2_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_1_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) + +/*FOR_EACH_2_REVERSE acts just like FOR_EACH_2, but in reverse order. Example: +FOR_EACH_2_REVERSE(X,a,b,c,d,e,f) => X(e,f) X(c,d) X (a, b) in this order */ +#define FOR_EACH_2_REVERSE(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_REVERSE_, C1(COUNT_ARG(__VA_ARGS__))) LPAREN MACRO_TO_INVOKE, __VA_ARGS__) +#else +#define FOR_EACH_2(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_2_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_1_COUNTED(MACRO_TO_INVOKE, ...) C2(FOR_EACH_1_COUNTED_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#define FOR_EACH_2_REVERSE(MACRO_TO_INVOKE, ...) C2(FOR_EACH_2_REVERSE_, C1(COUNT_ARG(__VA_ARGS__))) ( MACRO_TO_INVOKE, __VA_ARGS__) +#endif + +#ifdef _MSC_VER +#define EXPAND_OR_C1(x) x +#else +#define EXPAND_OR_C1(...) __VA_ARGS__ +#endif + +#define EXPAND_ARGS(...) __VA_ARGS__ +#define EXPAND_TWICE(...) EXPAND_ARGS(__VA_ARGS__) + +#define DO_0(MACRO, ...) \ +MACRO(0, __VA_ARGS__) + +#define DO_1(MACRO, ...) \ +MACRO(1, __VA_ARGS__) \ +DO_0(MACRO, __VA_ARGS__) + + +#define DO_2(MACRO, ...) \ +MACRO(2, __VA_ARGS__) \ +DO_1(MACRO, __VA_ARGS__) + + +#define DO_3(MACRO, ...) \ +MACRO(3, __VA_ARGS__) \ +DO_2(MACRO, __VA_ARGS__) + + +#define DO_4(MACRO, ...) \ +MACRO(4, __VA_ARGS__) \ +DO_3(MACRO, __VA_ARGS__) + + +#define DO_5(MACRO, ...) \ +MACRO(5, __VA_ARGS__) \ +DO_4(MACRO, __VA_ARGS__) + + +#define DO_6(MACRO, ...) \ +MACRO(6, __VA_ARGS__) \ +DO_5(MACRO, __VA_ARGS__) + + +#define DO_7(MACRO, ...) \ +MACRO(7, __VA_ARGS__) \ +DO_6(MACRO, __VA_ARGS__) + + +#define DO_8(MACRO, ...) \ +MACRO(8, __VA_ARGS__) \ +DO_7(MACRO, __VA_ARGS__) + + +#define DO_9(MACRO, ...) \ +MACRO(9, __VA_ARGS__) \ +DO_8(MACRO, __VA_ARGS__) + + +#define DO_10(MACRO, ...) \ +MACRO(10, __VA_ARGS__) \ +DO_9(MACRO, __VA_ARGS__) + + +#define DO_11(MACRO, ...) \ +MACRO(11, __VA_ARGS__) \ +DO_10(MACRO, __VA_ARGS__) + + +#define DO_12(MACRO, ...) \ +MACRO(12, __VA_ARGS__) \ +DO_11(MACRO, __VA_ARGS__) + + +#define DO_13(MACRO, ...) \ +MACRO(13, __VA_ARGS__) \ +DO_12(MACRO, __VA_ARGS__) + + +#define DO_14(MACRO, ...) \ +MACRO(14, __VA_ARGS__) \ +DO_13(MACRO, __VA_ARGS__) + + +#define DO_15(MACRO, ...) \ +MACRO(15, __VA_ARGS__) \ +DO_14(MACRO, __VA_ARGS__) + + +#define DO_16(MACRO, ...) \ +MACRO(16, __VA_ARGS__) \ +DO_15(MACRO, __VA_ARGS__) + + +#define DO_17(MACRO, ...) \ +MACRO(17, __VA_ARGS__) \ +DO_16(MACRO, __VA_ARGS__) + + +#define DO_18(MACRO, ...) \ +MACRO(18, __VA_ARGS__) \ +DO_17(MACRO, __VA_ARGS__) + + +#define DO_19(MACRO, ...) \ +MACRO(19, __VA_ARGS__) \ +DO_18(MACRO, __VA_ARGS__) + + +#define DO_20(MACRO, ...) \ +MACRO(20, __VA_ARGS__) \ +DO_19(MACRO, __VA_ARGS__) + + +#define DO_21(MACRO, ...) \ +MACRO(21, __VA_ARGS__) \ +DO_20(MACRO, __VA_ARGS__) + + +#define DO_22(MACRO, ...) \ +MACRO(22, __VA_ARGS__) \ +DO_21(MACRO, __VA_ARGS__) + + +#define DO_23(MACRO, ...) \ +MACRO(23, __VA_ARGS__) \ +DO_22(MACRO, __VA_ARGS__) + + +#define DO_24(MACRO, ...) \ +MACRO(24, __VA_ARGS__) \ +DO_23(MACRO, __VA_ARGS__) + + +#define DO_25(MACRO, ...) \ +MACRO(25, __VA_ARGS__) \ +DO_24(MACRO, __VA_ARGS__) + + +#define DO_26(MACRO, ...) \ +MACRO(26, __VA_ARGS__) \ +DO_25(MACRO, __VA_ARGS__) + + +#define DO_27(MACRO, ...) \ +MACRO(27, __VA_ARGS__) \ +DO_26(MACRO, __VA_ARGS__) + + +#define DO_28(MACRO, ...) \ +MACRO(28, __VA_ARGS__) \ +DO_27(MACRO, __VA_ARGS__) + + +#define DO_29(MACRO, ...) \ +MACRO(29, __VA_ARGS__) \ +DO_28(MACRO, __VA_ARGS__) + + +#define DO_30(MACRO, ...) \ +MACRO(30, __VA_ARGS__) \ +DO_29(MACRO, __VA_ARGS__) + + +#define DO_31(MACRO, ...) \ +MACRO(31, __VA_ARGS__) \ +DO_30(MACRO, __VA_ARGS__) + + +#define DO_32(MACRO, ...) \ +MACRO(32, __VA_ARGS__) \ +DO_31(MACRO, __VA_ARGS__) + + +#define DO_33(MACRO, ...) \ +MACRO(33, __VA_ARGS__) \ +DO_32(MACRO, __VA_ARGS__) + + +#define DO_34(MACRO, ...) \ +MACRO(34, __VA_ARGS__) \ +DO_33(MACRO, __VA_ARGS__) + + +#define DO_35(MACRO, ...) \ +MACRO(35, __VA_ARGS__) \ +DO_34(MACRO, __VA_ARGS__) + + +#define DO_36(MACRO, ...) \ +MACRO(36, __VA_ARGS__) \ +DO_35(MACRO, __VA_ARGS__) + + +#define DO_37(MACRO, ...) \ +MACRO(37, __VA_ARGS__) \ +DO_36(MACRO, __VA_ARGS__) + + +#define DO_38(MACRO, ...) \ +MACRO(38, __VA_ARGS__) \ +DO_37(MACRO, __VA_ARGS__) + + +#define DO_39(MACRO, ...) \ +MACRO(39, __VA_ARGS__) \ +DO_38(MACRO, __VA_ARGS__) + + +#define DO_40(MACRO, ...) \ +MACRO(40, __VA_ARGS__) \ +DO_39(MACRO, __VA_ARGS__) + + +#define DO_41(MACRO, ...) \ +MACRO(41, __VA_ARGS__) \ +DO_40(MACRO, __VA_ARGS__) + + +#define DO_42(MACRO, ...) \ +MACRO(42, __VA_ARGS__) \ +DO_41(MACRO, __VA_ARGS__) + + +#define DO_43(MACRO, ...) \ +MACRO(43, __VA_ARGS__) \ +DO_42(MACRO, __VA_ARGS__) + + +#define DO_44(MACRO, ...) \ +MACRO(44, __VA_ARGS__) \ +DO_43(MACRO, __VA_ARGS__) + + +#define DO_45(MACRO, ...) \ +MACRO(45, __VA_ARGS__) \ +DO_44(MACRO, __VA_ARGS__) + + +#define DO_46(MACRO, ...) \ +MACRO(46, __VA_ARGS__) \ +DO_45(MACRO, __VA_ARGS__) + + +#define DO_47(MACRO, ...) \ +MACRO(47, __VA_ARGS__) \ +DO_46(MACRO, __VA_ARGS__) + + +#define DO_48(MACRO, ...) \ +MACRO(48, __VA_ARGS__) \ +DO_47(MACRO, __VA_ARGS__) + + +#define DO_49(MACRO, ...) \ +MACRO(49, __VA_ARGS__) \ +DO_48(MACRO, __VA_ARGS__) + + +#define DO_50(MACRO, ...) \ +MACRO(50, __VA_ARGS__) \ +DO_49(MACRO, __VA_ARGS__) + + +#define DO_51(MACRO, ...) \ +MACRO(51, __VA_ARGS__) \ +DO_50(MACRO, __VA_ARGS__) + + +#define DO_52(MACRO, ...) \ +MACRO(52, __VA_ARGS__) \ +DO_51(MACRO, __VA_ARGS__) + + +#define DO_53(MACRO, ...) \ +MACRO(53, __VA_ARGS__) \ +DO_52(MACRO, __VA_ARGS__) + + +#define DO_54(MACRO, ...) \ +MACRO(54, __VA_ARGS__) \ +DO_53(MACRO, __VA_ARGS__) + + +#define DO_55(MACRO, ...) \ +MACRO(55, __VA_ARGS__) \ +DO_54(MACRO, __VA_ARGS__) + + +#define DO_56(MACRO, ...) \ +MACRO(56, __VA_ARGS__) \ +DO_55(MACRO, __VA_ARGS__) + + +#define DO_57(MACRO, ...) \ +MACRO(57, __VA_ARGS__) \ +DO_56(MACRO, __VA_ARGS__) + + +#define DO_58(MACRO, ...) \ +MACRO(58, __VA_ARGS__) \ +DO_57(MACRO, __VA_ARGS__) + + +#define DO_59(MACRO, ...) \ +MACRO(59, __VA_ARGS__) \ +DO_58(MACRO, __VA_ARGS__) + + +#define DO_60(MACRO, ...) \ +MACRO(60, __VA_ARGS__) \ +DO_59(MACRO, __VA_ARGS__) + + +#define DO_61(MACRO, ...) \ +MACRO(61, __VA_ARGS__) \ +DO_60(MACRO, __VA_ARGS__) + + +#define DO_62(MACRO, ...) \ +MACRO(62, __VA_ARGS__) \ +DO_61(MACRO, __VA_ARGS__) + + +#define DO_63(MACRO, ...) \ +MACRO(63, __VA_ARGS__) \ +DO_62(MACRO, __VA_ARGS__) + + +#define DO_64(MACRO, ...) \ +MACRO(64, __VA_ARGS__) \ +DO_63(MACRO, __VA_ARGS__) + + +#define DO_65(MACRO, ...) \ +MACRO(65, __VA_ARGS__) \ +DO_64(MACRO, __VA_ARGS__) + + +#define DO_66(MACRO, ...) \ +MACRO(66, __VA_ARGS__) \ +DO_65(MACRO, __VA_ARGS__) + + +#define DO_67(MACRO, ...) \ +MACRO(67, __VA_ARGS__) \ +DO_66(MACRO, __VA_ARGS__) + + +#define DO_68(MACRO, ...) \ +MACRO(68, __VA_ARGS__) \ +DO_67(MACRO, __VA_ARGS__) + + +#define DO_69(MACRO, ...) \ +MACRO(69, __VA_ARGS__) \ +DO_68(MACRO, __VA_ARGS__) + + +#define DO_70(MACRO, ...) \ +MACRO(70, __VA_ARGS__) \ +DO_69(MACRO, __VA_ARGS__) + + +#define DO_71(MACRO, ...) \ +MACRO(71, __VA_ARGS__) \ +DO_70(MACRO, __VA_ARGS__) + + +#define DO_72(MACRO, ...) \ +MACRO(72, __VA_ARGS__) \ +DO_71(MACRO, __VA_ARGS__) + + +#define DO_73(MACRO, ...) \ +MACRO(73, __VA_ARGS__) \ +DO_72(MACRO, __VA_ARGS__) + + +#define DO_74(MACRO, ...) \ +MACRO(74, __VA_ARGS__) \ +DO_73(MACRO, __VA_ARGS__) + + +#define DO_75(MACRO, ...) \ +MACRO(75, __VA_ARGS__) \ +DO_74(MACRO, __VA_ARGS__) + + +#define DO_76(MACRO, ...) \ +MACRO(76, __VA_ARGS__) \ +DO_75(MACRO, __VA_ARGS__) + + +#define DO_77(MACRO, ...) \ +MACRO(77, __VA_ARGS__) \ +DO_76(MACRO, __VA_ARGS__) + + +#define DO_78(MACRO, ...) \ +MACRO(78, __VA_ARGS__) \ +DO_77(MACRO, __VA_ARGS__) + + +#define DO_79(MACRO, ...) \ +MACRO(79, __VA_ARGS__) \ +DO_78(MACRO, __VA_ARGS__) + + +#define DO_80(MACRO, ...) \ +MACRO(80, __VA_ARGS__) \ +DO_79(MACRO, __VA_ARGS__) + + +#define DO_81(MACRO, ...) \ +MACRO(81, __VA_ARGS__) \ +DO_80(MACRO, __VA_ARGS__) + + +#define DO_82(MACRO, ...) \ +MACRO(82, __VA_ARGS__) \ +DO_81(MACRO, __VA_ARGS__) + + +#define DO_83(MACRO, ...) \ +MACRO(83, __VA_ARGS__) \ +DO_82(MACRO, __VA_ARGS__) + + +#define DO_84(MACRO, ...) \ +MACRO(84, __VA_ARGS__) \ +DO_83(MACRO, __VA_ARGS__) + + +#define DO_85(MACRO, ...) \ +MACRO(85, __VA_ARGS__) \ +DO_84(MACRO, __VA_ARGS__) + + +#define DO_86(MACRO, ...) \ +MACRO(86, __VA_ARGS__) \ +DO_85(MACRO, __VA_ARGS__) + + +#define DO_87(MACRO, ...) \ +MACRO(87, __VA_ARGS__) \ +DO_86(MACRO, __VA_ARGS__) + + +#define DO_88(MACRO, ...) \ +MACRO(88, __VA_ARGS__) \ +DO_87(MACRO, __VA_ARGS__) + + +#define DO_89(MACRO, ...) \ +MACRO(89, __VA_ARGS__) \ +DO_88(MACRO, __VA_ARGS__) + + +#define DO_90(MACRO, ...) \ +MACRO(90, __VA_ARGS__) \ +DO_89(MACRO, __VA_ARGS__) + + +#define DO_91(MACRO, ...) \ +MACRO(91, __VA_ARGS__) \ +DO_90(MACRO, __VA_ARGS__) + + +#define DO_92(MACRO, ...) \ +MACRO(92, __VA_ARGS__) \ +DO_91(MACRO, __VA_ARGS__) + + +#define DO_93(MACRO, ...) \ +MACRO(93, __VA_ARGS__) \ +DO_92(MACRO, __VA_ARGS__) + + +#define DO_94(MACRO, ...) \ +MACRO(94, __VA_ARGS__) \ +DO_93(MACRO, __VA_ARGS__) + + +#define DO_95(MACRO, ...) \ +MACRO(95, __VA_ARGS__) \ +DO_94(MACRO, __VA_ARGS__) + + +#define DO_96(MACRO, ...) \ +MACRO(96, __VA_ARGS__) \ +DO_95(MACRO, __VA_ARGS__) + + +#define DO_97(MACRO, ...) \ +MACRO(97, __VA_ARGS__) \ +DO_96(MACRO, __VA_ARGS__) + + +#define DO_98(MACRO, ...) \ +MACRO(98, __VA_ARGS__) \ +DO_97(MACRO, __VA_ARGS__) + + +#define DO_99(MACRO, ...) \ +MACRO(99, __VA_ARGS__) \ +DO_98(MACRO, __VA_ARGS__) + + +#define DO_100(MACRO, ...) \ +MACRO(100, __VA_ARGS__) \ +DO_99(MACRO, __VA_ARGS__) + + +#define DO_101(MACRO, ...) \ +MACRO(101, __VA_ARGS__) \ +DO_100(MACRO, __VA_ARGS__) + + +#define DO_102(MACRO, ...) \ +MACRO(102, __VA_ARGS__) \ +DO_101(MACRO, __VA_ARGS__) + + +#define DO_103(MACRO, ...) \ +MACRO(103, __VA_ARGS__) \ +DO_102(MACRO, __VA_ARGS__) + + +#define DO_104(MACRO, ...) \ +MACRO(104, __VA_ARGS__) \ +DO_103(MACRO, __VA_ARGS__) + + +#define DO_105(MACRO, ...) \ +MACRO(105, __VA_ARGS__) \ +DO_104(MACRO, __VA_ARGS__) + + +#define DO_106(MACRO, ...) \ +MACRO(106, __VA_ARGS__) \ +DO_105(MACRO, __VA_ARGS__) + + +#define DO_107(MACRO, ...) \ +MACRO(107, __VA_ARGS__) \ +DO_106(MACRO, __VA_ARGS__) + + +#define DO_108(MACRO, ...) \ +MACRO(108, __VA_ARGS__) \ +DO_107(MACRO, __VA_ARGS__) + + +#define DO_109(MACRO, ...) \ +MACRO(109, __VA_ARGS__) \ +DO_108(MACRO, __VA_ARGS__) + + +#define DO_110(MACRO, ...) \ +MACRO(110, __VA_ARGS__) \ +DO_109(MACRO, __VA_ARGS__) + + +#define DO_111(MACRO, ...) \ +MACRO(111, __VA_ARGS__) \ +DO_110(MACRO, __VA_ARGS__) + + +#define DO_112(MACRO, ...) \ +MACRO(112, __VA_ARGS__) \ +DO_111(MACRO, __VA_ARGS__) + + +#define DO_113(MACRO, ...) \ +MACRO(113, __VA_ARGS__) \ +DO_112(MACRO, __VA_ARGS__) + + +#define DO_114(MACRO, ...) \ +MACRO(114, __VA_ARGS__) \ +DO_113(MACRO, __VA_ARGS__) + + +#define DO_115(MACRO, ...) \ +MACRO(115, __VA_ARGS__) \ +DO_114(MACRO, __VA_ARGS__) + + +#define DO_116(MACRO, ...) \ +MACRO(116, __VA_ARGS__) \ +DO_115(MACRO, __VA_ARGS__) + + +#define DO_117(MACRO, ...) \ +MACRO(117, __VA_ARGS__) \ +DO_116(MACRO, __VA_ARGS__) + + +#define DO_118(MACRO, ...) \ +MACRO(118, __VA_ARGS__) \ +DO_117(MACRO, __VA_ARGS__) + + +#define DO_119(MACRO, ...) \ +MACRO(119, __VA_ARGS__) \ +DO_118(MACRO, __VA_ARGS__) + + +#define DO_120(MACRO, ...) \ +MACRO(120, __VA_ARGS__) \ +DO_119(MACRO, __VA_ARGS__) + + +#define DO_121(MACRO, ...) \ +MACRO(121, __VA_ARGS__) \ +DO_120(MACRO, __VA_ARGS__) + + +#define DO_122(MACRO, ...) \ +MACRO(122, __VA_ARGS__) \ +DO_121(MACRO, __VA_ARGS__) + + +#define DO_123(MACRO, ...) \ +MACRO(123, __VA_ARGS__) \ +DO_122(MACRO, __VA_ARGS__) + + +#define DO_124(MACRO, ...) \ +MACRO(124, __VA_ARGS__) \ +DO_123(MACRO, __VA_ARGS__) + + +#define DO_125(MACRO, ...) \ +MACRO(125, __VA_ARGS__) \ +DO_124(MACRO, __VA_ARGS__) + + +#define DO_126(MACRO, ...) \ +MACRO(126, __VA_ARGS__) \ +DO_125(MACRO, __VA_ARGS__) + + +#define DO_127(MACRO, ...) \ +MACRO(127, __VA_ARGS__) \ +DO_126(MACRO, __VA_ARGS__) + + +#define DO_128(MACRO, ...) \ +MACRO(128, __VA_ARGS__) \ +DO_127(MACRO, __VA_ARGS__) + + +#define DO_129(MACRO, ...) \ +MACRO(129, __VA_ARGS__) \ +DO_128(MACRO, __VA_ARGS__) + + +#define DO_130(MACRO, ...) \ +MACRO(130, __VA_ARGS__) \ +DO_129(MACRO, __VA_ARGS__) + + +#define DO_131(MACRO, ...) \ +MACRO(131, __VA_ARGS__) \ +DO_130(MACRO, __VA_ARGS__) + + +#define DO_132(MACRO, ...) \ +MACRO(132, __VA_ARGS__) \ +DO_131(MACRO, __VA_ARGS__) + + +#define DO_133(MACRO, ...) \ +MACRO(133, __VA_ARGS__) \ +DO_132(MACRO, __VA_ARGS__) + + +#define DO_134(MACRO, ...) \ +MACRO(134, __VA_ARGS__) \ +DO_133(MACRO, __VA_ARGS__) + + +#define DO_135(MACRO, ...) \ +MACRO(135, __VA_ARGS__) \ +DO_134(MACRO, __VA_ARGS__) + + +#define DO_136(MACRO, ...) \ +MACRO(136, __VA_ARGS__) \ +DO_135(MACRO, __VA_ARGS__) + + +#define DO_137(MACRO, ...) \ +MACRO(137, __VA_ARGS__) \ +DO_136(MACRO, __VA_ARGS__) + + +#define DO_138(MACRO, ...) \ +MACRO(138, __VA_ARGS__) \ +DO_137(MACRO, __VA_ARGS__) + + +#define DO_139(MACRO, ...) \ +MACRO(139, __VA_ARGS__) \ +DO_138(MACRO, __VA_ARGS__) + + +#define DO_140(MACRO, ...) \ +MACRO(140, __VA_ARGS__) \ +DO_139(MACRO, __VA_ARGS__) + + +#define DO_141(MACRO, ...) \ +MACRO(141, __VA_ARGS__) \ +DO_140(MACRO, __VA_ARGS__) + + +#define DO_142(MACRO, ...) \ +MACRO(142, __VA_ARGS__) \ +DO_141(MACRO, __VA_ARGS__) + + +#define DO_143(MACRO, ...) \ +MACRO(143, __VA_ARGS__) \ +DO_142(MACRO, __VA_ARGS__) + + +#define DO_144(MACRO, ...) \ +MACRO(144, __VA_ARGS__) \ +DO_143(MACRO, __VA_ARGS__) + + +#define DO_145(MACRO, ...) \ +MACRO(145, __VA_ARGS__) \ +DO_144(MACRO, __VA_ARGS__) + + +#define DO_146(MACRO, ...) \ +MACRO(146, __VA_ARGS__) \ +DO_145(MACRO, __VA_ARGS__) + + +#define DO_147(MACRO, ...) \ +MACRO(147, __VA_ARGS__) \ +DO_146(MACRO, __VA_ARGS__) + + +#define DO_148(MACRO, ...) \ +MACRO(148, __VA_ARGS__) \ +DO_147(MACRO, __VA_ARGS__) + + +#define DO_149(MACRO, ...) \ +MACRO(149, __VA_ARGS__) \ +DO_148(MACRO, __VA_ARGS__) + + +#define DO_150(MACRO, ...) \ +MACRO(150, __VA_ARGS__) \ +DO_149(MACRO, __VA_ARGS__) + + +#define DO_151(MACRO, ...) \ +MACRO(151, __VA_ARGS__) \ +DO_150(MACRO, __VA_ARGS__) + + +#define DO_152(MACRO, ...) \ +MACRO(152, __VA_ARGS__) \ +DO_151(MACRO, __VA_ARGS__) + + +#define DO_153(MACRO, ...) \ +MACRO(153, __VA_ARGS__) \ +DO_152(MACRO, __VA_ARGS__) + + +#define DO_154(MACRO, ...) \ +MACRO(154, __VA_ARGS__) \ +DO_153(MACRO, __VA_ARGS__) + + +#define DO_155(MACRO, ...) \ +MACRO(155, __VA_ARGS__) \ +DO_154(MACRO, __VA_ARGS__) + + +#define DO_156(MACRO, ...) \ +MACRO(156, __VA_ARGS__) \ +DO_155(MACRO, __VA_ARGS__) + + +#define DO_157(MACRO, ...) \ +MACRO(157, __VA_ARGS__) \ +DO_156(MACRO, __VA_ARGS__) + + +#define DO_158(MACRO, ...) \ +MACRO(158, __VA_ARGS__) \ +DO_157(MACRO, __VA_ARGS__) + + +#define DO_159(MACRO, ...) \ +MACRO(159, __VA_ARGS__) \ +DO_158(MACRO, __VA_ARGS__) + + +#define DO_160(MACRO, ...) \ +MACRO(160, __VA_ARGS__) \ +DO_159(MACRO, __VA_ARGS__) + + +#define DO_161(MACRO, ...) \ +MACRO(161, __VA_ARGS__) \ +DO_160(MACRO, __VA_ARGS__) + + +#define DO_162(MACRO, ...) \ +MACRO(162, __VA_ARGS__) \ +DO_161(MACRO, __VA_ARGS__) + + +#define DO_163(MACRO, ...) \ +MACRO(163, __VA_ARGS__) \ +DO_162(MACRO, __VA_ARGS__) + + +#define DO_164(MACRO, ...) \ +MACRO(164, __VA_ARGS__) \ +DO_163(MACRO, __VA_ARGS__) + + +#define DO_165(MACRO, ...) \ +MACRO(165, __VA_ARGS__) \ +DO_164(MACRO, __VA_ARGS__) + + +#define DO_166(MACRO, ...) \ +MACRO(166, __VA_ARGS__) \ +DO_165(MACRO, __VA_ARGS__) + + +#define DO_167(MACRO, ...) \ +MACRO(167, __VA_ARGS__) \ +DO_166(MACRO, __VA_ARGS__) + + +#define DO_168(MACRO, ...) \ +MACRO(168, __VA_ARGS__) \ +DO_167(MACRO, __VA_ARGS__) + + +#define DO_169(MACRO, ...) \ +MACRO(169, __VA_ARGS__) \ +DO_168(MACRO, __VA_ARGS__) + + +#define DO_170(MACRO, ...) \ +MACRO(170, __VA_ARGS__) \ +DO_169(MACRO, __VA_ARGS__) + + +#define DO_171(MACRO, ...) \ +MACRO(171, __VA_ARGS__) \ +DO_170(MACRO, __VA_ARGS__) + + +#define DO_172(MACRO, ...) \ +MACRO(172, __VA_ARGS__) \ +DO_171(MACRO, __VA_ARGS__) + + +#define DO_173(MACRO, ...) \ +MACRO(173, __VA_ARGS__) \ +DO_172(MACRO, __VA_ARGS__) + + +#define DO_174(MACRO, ...) \ +MACRO(174, __VA_ARGS__) \ +DO_173(MACRO, __VA_ARGS__) + + +#define DO_175(MACRO, ...) \ +MACRO(175, __VA_ARGS__) \ +DO_174(MACRO, __VA_ARGS__) + + +#define DO_176(MACRO, ...) \ +MACRO(176, __VA_ARGS__) \ +DO_175(MACRO, __VA_ARGS__) + + +#define DO_177(MACRO, ...) \ +MACRO(177, __VA_ARGS__) \ +DO_176(MACRO, __VA_ARGS__) + + +#define DO_178(MACRO, ...) \ +MACRO(178, __VA_ARGS__) \ +DO_177(MACRO, __VA_ARGS__) + + +#define DO_179(MACRO, ...) \ +MACRO(179, __VA_ARGS__) \ +DO_178(MACRO, __VA_ARGS__) + + +#define DO_180(MACRO, ...) \ +MACRO(180, __VA_ARGS__) \ +DO_179(MACRO, __VA_ARGS__) + + +#define DO_181(MACRO, ...) \ +MACRO(181, __VA_ARGS__) \ +DO_180(MACRO, __VA_ARGS__) + + +#define DO_182(MACRO, ...) \ +MACRO(182, __VA_ARGS__) \ +DO_181(MACRO, __VA_ARGS__) + + +#define DO_183(MACRO, ...) \ +MACRO(183, __VA_ARGS__) \ +DO_182(MACRO, __VA_ARGS__) + + +#define DO_184(MACRO, ...) \ +MACRO(184, __VA_ARGS__) \ +DO_183(MACRO, __VA_ARGS__) + + +#define DO_185(MACRO, ...) \ +MACRO(185, __VA_ARGS__) \ +DO_184(MACRO, __VA_ARGS__) + + +#define DO_186(MACRO, ...) \ +MACRO(186, __VA_ARGS__) \ +DO_185(MACRO, __VA_ARGS__) + + +#define DO_187(MACRO, ...) \ +MACRO(187, __VA_ARGS__) \ +DO_186(MACRO, __VA_ARGS__) + + +#define DO_188(MACRO, ...) \ +MACRO(188, __VA_ARGS__) \ +DO_187(MACRO, __VA_ARGS__) + + +#define DO_189(MACRO, ...) \ +MACRO(189, __VA_ARGS__) \ +DO_188(MACRO, __VA_ARGS__) + + +#define DO_190(MACRO, ...) \ +MACRO(190, __VA_ARGS__) \ +DO_189(MACRO, __VA_ARGS__) + + +#define DO_191(MACRO, ...) \ +MACRO(191, __VA_ARGS__) \ +DO_190(MACRO, __VA_ARGS__) + + +#define DO_192(MACRO, ...) \ +MACRO(192, __VA_ARGS__) \ +DO_191(MACRO, __VA_ARGS__) + + +#define DO_193(MACRO, ...) \ +MACRO(193, __VA_ARGS__) \ +DO_192(MACRO, __VA_ARGS__) + + +#define DO_194(MACRO, ...) \ +MACRO(194, __VA_ARGS__) \ +DO_193(MACRO, __VA_ARGS__) + + +#define DO_195(MACRO, ...) \ +MACRO(195, __VA_ARGS__) \ +DO_194(MACRO, __VA_ARGS__) + + +#define DO_196(MACRO, ...) \ +MACRO(196, __VA_ARGS__) \ +DO_195(MACRO, __VA_ARGS__) + + +#define DO_197(MACRO, ...) \ +MACRO(197, __VA_ARGS__) \ +DO_196(MACRO, __VA_ARGS__) + + +#define DO_198(MACRO, ...) \ +MACRO(198, __VA_ARGS__) \ +DO_197(MACRO, __VA_ARGS__) + + +#define DO_199(MACRO, ...) \ +MACRO(199, __VA_ARGS__) \ +DO_198(MACRO, __VA_ARGS__) + + +#define DO_200(MACRO, ...) \ +MACRO(200, __VA_ARGS__) \ +DO_199(MACRO, __VA_ARGS__) + + +#define DO_201(MACRO, ...) \ +MACRO(201, __VA_ARGS__) \ +DO_200(MACRO, __VA_ARGS__) + + +#define DO_202(MACRO, ...) \ +MACRO(202, __VA_ARGS__) \ +DO_201(MACRO, __VA_ARGS__) + + +#define DO_203(MACRO, ...) \ +MACRO(203, __VA_ARGS__) \ +DO_202(MACRO, __VA_ARGS__) + + +#define DO_204(MACRO, ...) \ +MACRO(204, __VA_ARGS__) \ +DO_203(MACRO, __VA_ARGS__) + + +#define DO_205(MACRO, ...) \ +MACRO(205, __VA_ARGS__) \ +DO_204(MACRO, __VA_ARGS__) + + +#define DO_206(MACRO, ...) \ +MACRO(206, __VA_ARGS__) \ +DO_205(MACRO, __VA_ARGS__) + + +#define DO_207(MACRO, ...) \ +MACRO(207, __VA_ARGS__) \ +DO_206(MACRO, __VA_ARGS__) + + +#define DO_208(MACRO, ...) \ +MACRO(208, __VA_ARGS__) \ +DO_207(MACRO, __VA_ARGS__) + + +#define DO_209(MACRO, ...) \ +MACRO(209, __VA_ARGS__) \ +DO_208(MACRO, __VA_ARGS__) + + +#define DO_210(MACRO, ...) \ +MACRO(210, __VA_ARGS__) \ +DO_209(MACRO, __VA_ARGS__) + + +#define DO_211(MACRO, ...) \ +MACRO(211, __VA_ARGS__) \ +DO_210(MACRO, __VA_ARGS__) + + +#define DO_212(MACRO, ...) \ +MACRO(212, __VA_ARGS__) \ +DO_211(MACRO, __VA_ARGS__) + + +#define DO_213(MACRO, ...) \ +MACRO(213, __VA_ARGS__) \ +DO_212(MACRO, __VA_ARGS__) + + +#define DO_214(MACRO, ...) \ +MACRO(214, __VA_ARGS__) \ +DO_213(MACRO, __VA_ARGS__) + + +#define DO_215(MACRO, ...) \ +MACRO(215, __VA_ARGS__) \ +DO_214(MACRO, __VA_ARGS__) + + +#define DO_216(MACRO, ...) \ +MACRO(216, __VA_ARGS__) \ +DO_215(MACRO, __VA_ARGS__) + + +#define DO_217(MACRO, ...) \ +MACRO(217, __VA_ARGS__) \ +DO_216(MACRO, __VA_ARGS__) + + +#define DO_218(MACRO, ...) \ +MACRO(218, __VA_ARGS__) \ +DO_217(MACRO, __VA_ARGS__) + + +#define DO_219(MACRO, ...) \ +MACRO(219, __VA_ARGS__) \ +DO_218(MACRO, __VA_ARGS__) + + +#define DO_220(MACRO, ...) \ +MACRO(220, __VA_ARGS__) \ +DO_219(MACRO, __VA_ARGS__) + + +#define DO_221(MACRO, ...) \ +MACRO(221, __VA_ARGS__) \ +DO_220(MACRO, __VA_ARGS__) + + +#define DO_222(MACRO, ...) \ +MACRO(222, __VA_ARGS__) \ +DO_221(MACRO, __VA_ARGS__) + + +#define DO_223(MACRO, ...) \ +MACRO(223, __VA_ARGS__) \ +DO_222(MACRO, __VA_ARGS__) + + +#define DO_224(MACRO, ...) \ +MACRO(224, __VA_ARGS__) \ +DO_223(MACRO, __VA_ARGS__) + + +#define DO_225(MACRO, ...) \ +MACRO(225, __VA_ARGS__) \ +DO_224(MACRO, __VA_ARGS__) + + +#define DO_226(MACRO, ...) \ +MACRO(226, __VA_ARGS__) \ +DO_225(MACRO, __VA_ARGS__) + + +#define DO_227(MACRO, ...) \ +MACRO(227, __VA_ARGS__) \ +DO_226(MACRO, __VA_ARGS__) + + +#define DO_228(MACRO, ...) \ +MACRO(228, __VA_ARGS__) \ +DO_227(MACRO, __VA_ARGS__) + + +#define DO_229(MACRO, ...) \ +MACRO(229, __VA_ARGS__) \ +DO_228(MACRO, __VA_ARGS__) + + +#define DO_230(MACRO, ...) \ +MACRO(230, __VA_ARGS__) \ +DO_229(MACRO, __VA_ARGS__) + + +#define DO_231(MACRO, ...) \ +MACRO(231, __VA_ARGS__) \ +DO_230(MACRO, __VA_ARGS__) + + +#define DO_232(MACRO, ...) \ +MACRO(232, __VA_ARGS__) \ +DO_231(MACRO, __VA_ARGS__) + + +#define DO_233(MACRO, ...) \ +MACRO(233, __VA_ARGS__) \ +DO_232(MACRO, __VA_ARGS__) + + +#define DO_234(MACRO, ...) \ +MACRO(234, __VA_ARGS__) \ +DO_233(MACRO, __VA_ARGS__) + + +#define DO_235(MACRO, ...) \ +MACRO(235, __VA_ARGS__) \ +DO_234(MACRO, __VA_ARGS__) + + +#define DO_236(MACRO, ...) \ +MACRO(236, __VA_ARGS__) \ +DO_235(MACRO, __VA_ARGS__) + + +#define DO_237(MACRO, ...) \ +MACRO(237, __VA_ARGS__) \ +DO_236(MACRO, __VA_ARGS__) + + +#define DO_238(MACRO, ...) \ +MACRO(238, __VA_ARGS__) \ +DO_237(MACRO, __VA_ARGS__) + + +#define DO_239(MACRO, ...) \ +MACRO(239, __VA_ARGS__) \ +DO_238(MACRO, __VA_ARGS__) + + +#define DO_240(MACRO, ...) \ +MACRO(240, __VA_ARGS__) \ +DO_239(MACRO, __VA_ARGS__) + + +#define DO_241(MACRO, ...) \ +MACRO(241, __VA_ARGS__) \ +DO_240(MACRO, __VA_ARGS__) + + +#define DO_242(MACRO, ...) \ +MACRO(242, __VA_ARGS__) \ +DO_241(MACRO, __VA_ARGS__) + + +#define DO_243(MACRO, ...) \ +MACRO(243, __VA_ARGS__) \ +DO_242(MACRO, __VA_ARGS__) + + +#define DO_244(MACRO, ...) \ +MACRO(244, __VA_ARGS__) \ +DO_243(MACRO, __VA_ARGS__) + + +#define DO_245(MACRO, ...) \ +MACRO(245, __VA_ARGS__) \ +DO_244(MACRO, __VA_ARGS__) + + +#define DO_246(MACRO, ...) \ +MACRO(246, __VA_ARGS__) \ +DO_245(MACRO, __VA_ARGS__) + + +#define DO_247(MACRO, ...) \ +MACRO(247, __VA_ARGS__) \ +DO_246(MACRO, __VA_ARGS__) + + +#define DO_248(MACRO, ...) \ +MACRO(248, __VA_ARGS__) \ +DO_247(MACRO, __VA_ARGS__) + + +#define DO_249(MACRO, ...) \ +MACRO(249, __VA_ARGS__) \ +DO_248(MACRO, __VA_ARGS__) + + +#define DO_250(MACRO, ...) \ +MACRO(250, __VA_ARGS__) \ +DO_249(MACRO, __VA_ARGS__) + + +#define DO_251(MACRO, ...) \ +MACRO(251, __VA_ARGS__) \ +DO_250(MACRO, __VA_ARGS__) + + +#define DO_252(MACRO, ...) \ +MACRO(252, __VA_ARGS__) \ +DO_251(MACRO, __VA_ARGS__) + + +#define DO_253(MACRO, ...) \ +MACRO(253, __VA_ARGS__) \ +DO_252(MACRO, __VA_ARGS__) + + +#define DO_254(MACRO, ...) \ +MACRO(254, __VA_ARGS__) \ +DO_253(MACRO, __VA_ARGS__) + + +#define DO_255(MACRO, ...) \ +MACRO(255, __VA_ARGS__) \ +DO_254(MACRO, __VA_ARGS__) + + +#define DO_256(MACRO, ...) \ +MACRO(256, __VA_ARGS__) \ +DO_255(MACRO, __VA_ARGS__) + + +#define DO_257(MACRO, ...) \ +MACRO(257, __VA_ARGS__) \ +DO_256(MACRO, __VA_ARGS__) + + +#define DO_258(MACRO, ...) \ +MACRO(258, __VA_ARGS__) \ +DO_257(MACRO, __VA_ARGS__) + + +#define DO_259(MACRO, ...) \ +MACRO(259, __VA_ARGS__) \ +DO_258(MACRO, __VA_ARGS__) + + +#define DO_260(MACRO, ...) \ +MACRO(260, __VA_ARGS__) \ +DO_259(MACRO, __VA_ARGS__) + + +#define DO_261(MACRO, ...) \ +MACRO(261, __VA_ARGS__) \ +DO_260(MACRO, __VA_ARGS__) + + +#define DO_262(MACRO, ...) \ +MACRO(262, __VA_ARGS__) \ +DO_261(MACRO, __VA_ARGS__) + + +#define DO_263(MACRO, ...) \ +MACRO(263, __VA_ARGS__) \ +DO_262(MACRO, __VA_ARGS__) + + +#define DO_264(MACRO, ...) \ +MACRO(264, __VA_ARGS__) \ +DO_263(MACRO, __VA_ARGS__) + + +#define DO_265(MACRO, ...) \ +MACRO(265, __VA_ARGS__) \ +DO_264(MACRO, __VA_ARGS__) + + +#define DO_266(MACRO, ...) \ +MACRO(266, __VA_ARGS__) \ +DO_265(MACRO, __VA_ARGS__) + + +#define DO_267(MACRO, ...) \ +MACRO(267, __VA_ARGS__) \ +DO_266(MACRO, __VA_ARGS__) + + +#define DO_268(MACRO, ...) \ +MACRO(268, __VA_ARGS__) \ +DO_267(MACRO, __VA_ARGS__) + + +#define DO_269(MACRO, ...) \ +MACRO(269, __VA_ARGS__) \ +DO_268(MACRO, __VA_ARGS__) + + +#define DO_270(MACRO, ...) \ +MACRO(270, __VA_ARGS__) \ +DO_269(MACRO, __VA_ARGS__) + + +#define DO_271(MACRO, ...) \ +MACRO(271, __VA_ARGS__) \ +DO_270(MACRO, __VA_ARGS__) + + +#define DO_272(MACRO, ...) \ +MACRO(272, __VA_ARGS__) \ +DO_271(MACRO, __VA_ARGS__) + + +#define DO_273(MACRO, ...) \ +MACRO(273, __VA_ARGS__) \ +DO_272(MACRO, __VA_ARGS__) + + +#define DO_274(MACRO, ...) \ +MACRO(274, __VA_ARGS__) \ +DO_273(MACRO, __VA_ARGS__) + + +#define DO_275(MACRO, ...) \ +MACRO(275, __VA_ARGS__) \ +DO_274(MACRO, __VA_ARGS__) + + +#define DO_276(MACRO, ...) \ +MACRO(276, __VA_ARGS__) \ +DO_275(MACRO, __VA_ARGS__) + + +#define DO_277(MACRO, ...) \ +MACRO(277, __VA_ARGS__) \ +DO_276(MACRO, __VA_ARGS__) + + +#define DO_278(MACRO, ...) \ +MACRO(278, __VA_ARGS__) \ +DO_277(MACRO, __VA_ARGS__) + + +#define DO_279(MACRO, ...) \ +MACRO(279, __VA_ARGS__) \ +DO_278(MACRO, __VA_ARGS__) + + +#define DO_280(MACRO, ...) \ +MACRO(280, __VA_ARGS__) \ +DO_279(MACRO, __VA_ARGS__) + + +#define DO_281(MACRO, ...) \ +MACRO(281, __VA_ARGS__) \ +DO_280(MACRO, __VA_ARGS__) + + +#define DO_282(MACRO, ...) \ +MACRO(282, __VA_ARGS__) \ +DO_281(MACRO, __VA_ARGS__) + + +#define DO_283(MACRO, ...) \ +MACRO(283, __VA_ARGS__) \ +DO_282(MACRO, __VA_ARGS__) + + +#define DO_284(MACRO, ...) \ +MACRO(284, __VA_ARGS__) \ +DO_283(MACRO, __VA_ARGS__) + + +#define DO_285(MACRO, ...) \ +MACRO(285, __VA_ARGS__) \ +DO_284(MACRO, __VA_ARGS__) + + +#define DO_286(MACRO, ...) \ +MACRO(286, __VA_ARGS__) \ +DO_285(MACRO, __VA_ARGS__) + + +#define DO_287(MACRO, ...) \ +MACRO(287, __VA_ARGS__) \ +DO_286(MACRO, __VA_ARGS__) + + +#define DO_288(MACRO, ...) \ +MACRO(288, __VA_ARGS__) \ +DO_287(MACRO, __VA_ARGS__) + + +#define DO_289(MACRO, ...) \ +MACRO(289, __VA_ARGS__) \ +DO_288(MACRO, __VA_ARGS__) + + +#define DO_290(MACRO, ...) \ +MACRO(290, __VA_ARGS__) \ +DO_289(MACRO, __VA_ARGS__) + + +#define DO_291(MACRO, ...) \ +MACRO(291, __VA_ARGS__) \ +DO_290(MACRO, __VA_ARGS__) + + +#define DO_292(MACRO, ...) \ +MACRO(292, __VA_ARGS__) \ +DO_291(MACRO, __VA_ARGS__) + + +#define DO_293(MACRO, ...) \ +MACRO(293, __VA_ARGS__) \ +DO_292(MACRO, __VA_ARGS__) + + +#define DO_294(MACRO, ...) \ +MACRO(294, __VA_ARGS__) \ +DO_293(MACRO, __VA_ARGS__) + + +#define DO_295(MACRO, ...) \ +MACRO(295, __VA_ARGS__) \ +DO_294(MACRO, __VA_ARGS__) + + +#define DO_296(MACRO, ...) \ +MACRO(296, __VA_ARGS__) \ +DO_295(MACRO, __VA_ARGS__) + + +#define DO_297(MACRO, ...) \ +MACRO(297, __VA_ARGS__) \ +DO_296(MACRO, __VA_ARGS__) + + +#define DO_298(MACRO, ...) \ +MACRO(298, __VA_ARGS__) \ +DO_297(MACRO, __VA_ARGS__) + + +#define DO_299(MACRO, ...) \ +MACRO(299, __VA_ARGS__) \ +DO_298(MACRO, __VA_ARGS__) + + +#define DO_300(MACRO, ...) \ +MACRO(300, __VA_ARGS__) \ +DO_299(MACRO, __VA_ARGS__) + + +#define DO_301(MACRO, ...) \ +MACRO(301, __VA_ARGS__) \ +DO_300(MACRO, __VA_ARGS__) + + +#define DO_302(MACRO, ...) \ +MACRO(302, __VA_ARGS__) \ +DO_301(MACRO, __VA_ARGS__) + + +#define DO_303(MACRO, ...) \ +MACRO(303, __VA_ARGS__) \ +DO_302(MACRO, __VA_ARGS__) + + +#define DO_304(MACRO, ...) \ +MACRO(304, __VA_ARGS__) \ +DO_303(MACRO, __VA_ARGS__) + + +#define DO_305(MACRO, ...) \ +MACRO(305, __VA_ARGS__) \ +DO_304(MACRO, __VA_ARGS__) + + +#define DO_306(MACRO, ...) \ +MACRO(306, __VA_ARGS__) \ +DO_305(MACRO, __VA_ARGS__) + + +#define DO_307(MACRO, ...) \ +MACRO(307, __VA_ARGS__) \ +DO_306(MACRO, __VA_ARGS__) + + +#define DO_308(MACRO, ...) \ +MACRO(308, __VA_ARGS__) \ +DO_307(MACRO, __VA_ARGS__) + + +#define DO_309(MACRO, ...) \ +MACRO(309, __VA_ARGS__) \ +DO_308(MACRO, __VA_ARGS__) + + +#define DO_310(MACRO, ...) \ +MACRO(310, __VA_ARGS__) \ +DO_309(MACRO, __VA_ARGS__) + + +#define DO_311(MACRO, ...) \ +MACRO(311, __VA_ARGS__) \ +DO_310(MACRO, __VA_ARGS__) + + +#define DO_312(MACRO, ...) \ +MACRO(312, __VA_ARGS__) \ +DO_311(MACRO, __VA_ARGS__) + + +#define DO_313(MACRO, ...) \ +MACRO(313, __VA_ARGS__) \ +DO_312(MACRO, __VA_ARGS__) + + +#define DO_314(MACRO, ...) \ +MACRO(314, __VA_ARGS__) \ +DO_313(MACRO, __VA_ARGS__) + + +#define DO_315(MACRO, ...) \ +MACRO(315, __VA_ARGS__) \ +DO_314(MACRO, __VA_ARGS__) + + +#define DO_316(MACRO, ...) \ +MACRO(316, __VA_ARGS__) \ +DO_315(MACRO, __VA_ARGS__) + + +#define DO_317(MACRO, ...) \ +MACRO(317, __VA_ARGS__) \ +DO_316(MACRO, __VA_ARGS__) + + +#define DO_318(MACRO, ...) \ +MACRO(318, __VA_ARGS__) \ +DO_317(MACRO, __VA_ARGS__) + + +#define DO_319(MACRO, ...) \ +MACRO(319, __VA_ARGS__) \ +DO_318(MACRO, __VA_ARGS__) + + +#define DO_320(MACRO, ...) \ +MACRO(320, __VA_ARGS__) \ +DO_319(MACRO, __VA_ARGS__) + + +#define DO_321(MACRO, ...) \ +MACRO(321, __VA_ARGS__) \ +DO_320(MACRO, __VA_ARGS__) + + +#define DO_322(MACRO, ...) \ +MACRO(322, __VA_ARGS__) \ +DO_321(MACRO, __VA_ARGS__) + + +#define DO_323(MACRO, ...) \ +MACRO(323, __VA_ARGS__) \ +DO_322(MACRO, __VA_ARGS__) + + +#define DO_324(MACRO, ...) \ +MACRO(324, __VA_ARGS__) \ +DO_323(MACRO, __VA_ARGS__) + + +#define DO_325(MACRO, ...) \ +MACRO(325, __VA_ARGS__) \ +DO_324(MACRO, __VA_ARGS__) + + +#define DO_326(MACRO, ...) \ +MACRO(326, __VA_ARGS__) \ +DO_325(MACRO, __VA_ARGS__) + + +#define DO_327(MACRO, ...) \ +MACRO(327, __VA_ARGS__) \ +DO_326(MACRO, __VA_ARGS__) + + +#define DO_328(MACRO, ...) \ +MACRO(328, __VA_ARGS__) \ +DO_327(MACRO, __VA_ARGS__) + + +#define DO_329(MACRO, ...) \ +MACRO(329, __VA_ARGS__) \ +DO_328(MACRO, __VA_ARGS__) + + +#define DO_330(MACRO, ...) \ +MACRO(330, __VA_ARGS__) \ +DO_329(MACRO, __VA_ARGS__) + + +#define DO_331(MACRO, ...) \ +MACRO(331, __VA_ARGS__) \ +DO_330(MACRO, __VA_ARGS__) + + +#define DO_332(MACRO, ...) \ +MACRO(332, __VA_ARGS__) \ +DO_331(MACRO, __VA_ARGS__) + + +#define DO_333(MACRO, ...) \ +MACRO(333, __VA_ARGS__) \ +DO_332(MACRO, __VA_ARGS__) + + +#define DO_334(MACRO, ...) \ +MACRO(334, __VA_ARGS__) \ +DO_333(MACRO, __VA_ARGS__) + + +#define DO_335(MACRO, ...) \ +MACRO(335, __VA_ARGS__) \ +DO_334(MACRO, __VA_ARGS__) + + +#define DO_336(MACRO, ...) \ +MACRO(336, __VA_ARGS__) \ +DO_335(MACRO, __VA_ARGS__) + + +#define DO_337(MACRO, ...) \ +MACRO(337, __VA_ARGS__) \ +DO_336(MACRO, __VA_ARGS__) + + +#define DO_338(MACRO, ...) \ +MACRO(338, __VA_ARGS__) \ +DO_337(MACRO, __VA_ARGS__) + + +#define DO_339(MACRO, ...) \ +MACRO(339, __VA_ARGS__) \ +DO_338(MACRO, __VA_ARGS__) + + +#define DO_340(MACRO, ...) \ +MACRO(340, __VA_ARGS__) \ +DO_339(MACRO, __VA_ARGS__) + + +#define DO_341(MACRO, ...) \ +MACRO(341, __VA_ARGS__) \ +DO_340(MACRO, __VA_ARGS__) + + +#define DO_342(MACRO, ...) \ +MACRO(342, __VA_ARGS__) \ +DO_341(MACRO, __VA_ARGS__) + + +#define DO_343(MACRO, ...) \ +MACRO(343, __VA_ARGS__) \ +DO_342(MACRO, __VA_ARGS__) + + +#define DO_344(MACRO, ...) \ +MACRO(344, __VA_ARGS__) \ +DO_343(MACRO, __VA_ARGS__) + + +#define DO_345(MACRO, ...) \ +MACRO(345, __VA_ARGS__) \ +DO_344(MACRO, __VA_ARGS__) + + +#define DO_346(MACRO, ...) \ +MACRO(346, __VA_ARGS__) \ +DO_345(MACRO, __VA_ARGS__) + + +#define DO_347(MACRO, ...) \ +MACRO(347, __VA_ARGS__) \ +DO_346(MACRO, __VA_ARGS__) + + +#define DO_348(MACRO, ...) \ +MACRO(348, __VA_ARGS__) \ +DO_347(MACRO, __VA_ARGS__) + + +#define DO_349(MACRO, ...) \ +MACRO(349, __VA_ARGS__) \ +DO_348(MACRO, __VA_ARGS__) + + +#define DO_350(MACRO, ...) \ +MACRO(350, __VA_ARGS__) \ +DO_349(MACRO, __VA_ARGS__) + + +#define DO_351(MACRO, ...) \ +MACRO(351, __VA_ARGS__) \ +DO_350(MACRO, __VA_ARGS__) + + +#define DO_352(MACRO, ...) \ +MACRO(352, __VA_ARGS__) \ +DO_351(MACRO, __VA_ARGS__) + + +#define DO_353(MACRO, ...) \ +MACRO(353, __VA_ARGS__) \ +DO_352(MACRO, __VA_ARGS__) + + +#define DO_354(MACRO, ...) \ +MACRO(354, __VA_ARGS__) \ +DO_353(MACRO, __VA_ARGS__) + + +#define DO_355(MACRO, ...) \ +MACRO(355, __VA_ARGS__) \ +DO_354(MACRO, __VA_ARGS__) + + +#define DO_356(MACRO, ...) \ +MACRO(356, __VA_ARGS__) \ +DO_355(MACRO, __VA_ARGS__) + + +#define DO_357(MACRO, ...) \ +MACRO(357, __VA_ARGS__) \ +DO_356(MACRO, __VA_ARGS__) + + +#define DO_358(MACRO, ...) \ +MACRO(358, __VA_ARGS__) \ +DO_357(MACRO, __VA_ARGS__) + + +#define DO_359(MACRO, ...) \ +MACRO(359, __VA_ARGS__) \ +DO_358(MACRO, __VA_ARGS__) + + +#define DO_360(MACRO, ...) \ +MACRO(360, __VA_ARGS__) \ +DO_359(MACRO, __VA_ARGS__) + + +#define DO_361(MACRO, ...) \ +MACRO(361, __VA_ARGS__) \ +DO_360(MACRO, __VA_ARGS__) + + +#define DO_362(MACRO, ...) \ +MACRO(362, __VA_ARGS__) \ +DO_361(MACRO, __VA_ARGS__) + + +#define DO_363(MACRO, ...) \ +MACRO(363, __VA_ARGS__) \ +DO_362(MACRO, __VA_ARGS__) + + +#define DO_364(MACRO, ...) \ +MACRO(364, __VA_ARGS__) \ +DO_363(MACRO, __VA_ARGS__) + + +#define DO_365(MACRO, ...) \ +MACRO(365, __VA_ARGS__) \ +DO_364(MACRO, __VA_ARGS__) + + +#define DO_366(MACRO, ...) \ +MACRO(366, __VA_ARGS__) \ +DO_365(MACRO, __VA_ARGS__) + + +#define DO_367(MACRO, ...) \ +MACRO(367, __VA_ARGS__) \ +DO_366(MACRO, __VA_ARGS__) + + +#define DO_368(MACRO, ...) \ +MACRO(368, __VA_ARGS__) \ +DO_367(MACRO, __VA_ARGS__) + + +#define DO_369(MACRO, ...) \ +MACRO(369, __VA_ARGS__) \ +DO_368(MACRO, __VA_ARGS__) + + +#define DO_370(MACRO, ...) \ +MACRO(370, __VA_ARGS__) \ +DO_369(MACRO, __VA_ARGS__) + + +#define DO_371(MACRO, ...) \ +MACRO(371, __VA_ARGS__) \ +DO_370(MACRO, __VA_ARGS__) + + +#define DO_372(MACRO, ...) \ +MACRO(372, __VA_ARGS__) \ +DO_371(MACRO, __VA_ARGS__) + + +#define DO_373(MACRO, ...) \ +MACRO(373, __VA_ARGS__) \ +DO_372(MACRO, __VA_ARGS__) + + +#define DO_374(MACRO, ...) \ +MACRO(374, __VA_ARGS__) \ +DO_373(MACRO, __VA_ARGS__) + + +#define DO_375(MACRO, ...) \ +MACRO(375, __VA_ARGS__) \ +DO_374(MACRO, __VA_ARGS__) + + +#define DO_376(MACRO, ...) \ +MACRO(376, __VA_ARGS__) \ +DO_375(MACRO, __VA_ARGS__) + + +#define DO_377(MACRO, ...) \ +MACRO(377, __VA_ARGS__) \ +DO_376(MACRO, __VA_ARGS__) + + +#define DO_378(MACRO, ...) \ +MACRO(378, __VA_ARGS__) \ +DO_377(MACRO, __VA_ARGS__) + + +#define DO_379(MACRO, ...) \ +MACRO(379, __VA_ARGS__) \ +DO_378(MACRO, __VA_ARGS__) + + +#define DO_380(MACRO, ...) \ +MACRO(380, __VA_ARGS__) \ +DO_379(MACRO, __VA_ARGS__) + + +#define DO_381(MACRO, ...) \ +MACRO(381, __VA_ARGS__) \ +DO_380(MACRO, __VA_ARGS__) + + +#define DO_382(MACRO, ...) \ +MACRO(382, __VA_ARGS__) \ +DO_381(MACRO, __VA_ARGS__) + + +#define DO_383(MACRO, ...) \ +MACRO(383, __VA_ARGS__) \ +DO_382(MACRO, __VA_ARGS__) + + +#define DO_384(MACRO, ...) \ +MACRO(384, __VA_ARGS__) \ +DO_383(MACRO, __VA_ARGS__) + + +#define DO_385(MACRO, ...) \ +MACRO(385, __VA_ARGS__) \ +DO_384(MACRO, __VA_ARGS__) + + +#define DO_386(MACRO, ...) \ +MACRO(386, __VA_ARGS__) \ +DO_385(MACRO, __VA_ARGS__) + + +#define DO_387(MACRO, ...) \ +MACRO(387, __VA_ARGS__) \ +DO_386(MACRO, __VA_ARGS__) + + +#define DO_388(MACRO, ...) \ +MACRO(388, __VA_ARGS__) \ +DO_387(MACRO, __VA_ARGS__) + + +#define DO_389(MACRO, ...) \ +MACRO(389, __VA_ARGS__) \ +DO_388(MACRO, __VA_ARGS__) + + +#define DO_390(MACRO, ...) \ +MACRO(390, __VA_ARGS__) \ +DO_389(MACRO, __VA_ARGS__) + + +#define DO_391(MACRO, ...) \ +MACRO(391, __VA_ARGS__) \ +DO_390(MACRO, __VA_ARGS__) + + +#define DO_392(MACRO, ...) \ +MACRO(392, __VA_ARGS__) \ +DO_391(MACRO, __VA_ARGS__) + + +#define DO_393(MACRO, ...) \ +MACRO(393, __VA_ARGS__) \ +DO_392(MACRO, __VA_ARGS__) + + +#define DO_394(MACRO, ...) \ +MACRO(394, __VA_ARGS__) \ +DO_393(MACRO, __VA_ARGS__) + + +#define DO_395(MACRO, ...) \ +MACRO(395, __VA_ARGS__) \ +DO_394(MACRO, __VA_ARGS__) + + +#define DO_396(MACRO, ...) \ +MACRO(396, __VA_ARGS__) \ +DO_395(MACRO, __VA_ARGS__) + + +#define DO_397(MACRO, ...) \ +MACRO(397, __VA_ARGS__) \ +DO_396(MACRO, __VA_ARGS__) + + +#define DO_398(MACRO, ...) \ +MACRO(398, __VA_ARGS__) \ +DO_397(MACRO, __VA_ARGS__) + + +#define DO_399(MACRO, ...) \ +MACRO(399, __VA_ARGS__) \ +DO_398(MACRO, __VA_ARGS__) + + +#define DO_400(MACRO, ...) \ +MACRO(400, __VA_ARGS__) \ +DO_399(MACRO, __VA_ARGS__) + + +#define DO_401(MACRO, ...) \ +MACRO(401, __VA_ARGS__) \ +DO_400(MACRO, __VA_ARGS__) + + +#define DO_402(MACRO, ...) \ +MACRO(402, __VA_ARGS__) \ +DO_401(MACRO, __VA_ARGS__) + + +#define DO_403(MACRO, ...) \ +MACRO(403, __VA_ARGS__) \ +DO_402(MACRO, __VA_ARGS__) + + +#define DO_404(MACRO, ...) \ +MACRO(404, __VA_ARGS__) \ +DO_403(MACRO, __VA_ARGS__) + + +#define DO_405(MACRO, ...) \ +MACRO(405, __VA_ARGS__) \ +DO_404(MACRO, __VA_ARGS__) + + +#define DO_406(MACRO, ...) \ +MACRO(406, __VA_ARGS__) \ +DO_405(MACRO, __VA_ARGS__) + + +#define DO_407(MACRO, ...) \ +MACRO(407, __VA_ARGS__) \ +DO_406(MACRO, __VA_ARGS__) + + +#define DO_408(MACRO, ...) \ +MACRO(408, __VA_ARGS__) \ +DO_407(MACRO, __VA_ARGS__) + + +#define DO_409(MACRO, ...) \ +MACRO(409, __VA_ARGS__) \ +DO_408(MACRO, __VA_ARGS__) + + +#define DO_410(MACRO, ...) \ +MACRO(410, __VA_ARGS__) \ +DO_409(MACRO, __VA_ARGS__) + + +#define DO_411(MACRO, ...) \ +MACRO(411, __VA_ARGS__) \ +DO_410(MACRO, __VA_ARGS__) + + +#define DO_412(MACRO, ...) \ +MACRO(412, __VA_ARGS__) \ +DO_411(MACRO, __VA_ARGS__) + + +#define DO_413(MACRO, ...) \ +MACRO(413, __VA_ARGS__) \ +DO_412(MACRO, __VA_ARGS__) + + +#define DO_414(MACRO, ...) \ +MACRO(414, __VA_ARGS__) \ +DO_413(MACRO, __VA_ARGS__) + + +#define DO_415(MACRO, ...) \ +MACRO(415, __VA_ARGS__) \ +DO_414(MACRO, __VA_ARGS__) + + +#define DO_416(MACRO, ...) \ +MACRO(416, __VA_ARGS__) \ +DO_415(MACRO, __VA_ARGS__) + + +#define DO_417(MACRO, ...) \ +MACRO(417, __VA_ARGS__) \ +DO_416(MACRO, __VA_ARGS__) + + +#define DO_418(MACRO, ...) \ +MACRO(418, __VA_ARGS__) \ +DO_417(MACRO, __VA_ARGS__) + + +#define DO_419(MACRO, ...) \ +MACRO(419, __VA_ARGS__) \ +DO_418(MACRO, __VA_ARGS__) + + +#define DO_420(MACRO, ...) \ +MACRO(420, __VA_ARGS__) \ +DO_419(MACRO, __VA_ARGS__) + + +#define DO_421(MACRO, ...) \ +MACRO(421, __VA_ARGS__) \ +DO_420(MACRO, __VA_ARGS__) + + +#define DO_422(MACRO, ...) \ +MACRO(422, __VA_ARGS__) \ +DO_421(MACRO, __VA_ARGS__) + + +#define DO_423(MACRO, ...) \ +MACRO(423, __VA_ARGS__) \ +DO_422(MACRO, __VA_ARGS__) + + +#define DO_424(MACRO, ...) \ +MACRO(424, __VA_ARGS__) \ +DO_423(MACRO, __VA_ARGS__) + + +#define DO_425(MACRO, ...) \ +MACRO(425, __VA_ARGS__) \ +DO_424(MACRO, __VA_ARGS__) + + +#define DO_426(MACRO, ...) \ +MACRO(426, __VA_ARGS__) \ +DO_425(MACRO, __VA_ARGS__) + + +#define DO_427(MACRO, ...) \ +MACRO(427, __VA_ARGS__) \ +DO_426(MACRO, __VA_ARGS__) + + +#define DO_428(MACRO, ...) \ +MACRO(428, __VA_ARGS__) \ +DO_427(MACRO, __VA_ARGS__) + + +#define DO_429(MACRO, ...) \ +MACRO(429, __VA_ARGS__) \ +DO_428(MACRO, __VA_ARGS__) + + +#define DO_430(MACRO, ...) \ +MACRO(430, __VA_ARGS__) \ +DO_429(MACRO, __VA_ARGS__) + + +#define DO_431(MACRO, ...) \ +MACRO(431, __VA_ARGS__) \ +DO_430(MACRO, __VA_ARGS__) + + +#define DO_432(MACRO, ...) \ +MACRO(432, __VA_ARGS__) \ +DO_431(MACRO, __VA_ARGS__) + + +#define DO_433(MACRO, ...) \ +MACRO(433, __VA_ARGS__) \ +DO_432(MACRO, __VA_ARGS__) + + +#define DO_434(MACRO, ...) \ +MACRO(434, __VA_ARGS__) \ +DO_433(MACRO, __VA_ARGS__) + + +#define DO_435(MACRO, ...) \ +MACRO(435, __VA_ARGS__) \ +DO_434(MACRO, __VA_ARGS__) + + +#define DO_436(MACRO, ...) \ +MACRO(436, __VA_ARGS__) \ +DO_435(MACRO, __VA_ARGS__) + + +#define DO_437(MACRO, ...) \ +MACRO(437, __VA_ARGS__) \ +DO_436(MACRO, __VA_ARGS__) + + +#define DO_438(MACRO, ...) \ +MACRO(438, __VA_ARGS__) \ +DO_437(MACRO, __VA_ARGS__) + + +#define DO_439(MACRO, ...) \ +MACRO(439, __VA_ARGS__) \ +DO_438(MACRO, __VA_ARGS__) + + +#define DO_440(MACRO, ...) \ +MACRO(440, __VA_ARGS__) \ +DO_439(MACRO, __VA_ARGS__) + + +#define DO_441(MACRO, ...) \ +MACRO(441, __VA_ARGS__) \ +DO_440(MACRO, __VA_ARGS__) + + +#define DO_442(MACRO, ...) \ +MACRO(442, __VA_ARGS__) \ +DO_441(MACRO, __VA_ARGS__) + + +#define DO_443(MACRO, ...) \ +MACRO(443, __VA_ARGS__) \ +DO_442(MACRO, __VA_ARGS__) + + +#define DO_444(MACRO, ...) \ +MACRO(444, __VA_ARGS__) \ +DO_443(MACRO, __VA_ARGS__) + + +#define DO_445(MACRO, ...) \ +MACRO(445, __VA_ARGS__) \ +DO_444(MACRO, __VA_ARGS__) + + +#define DO_446(MACRO, ...) \ +MACRO(446, __VA_ARGS__) \ +DO_445(MACRO, __VA_ARGS__) + + +#define DO_447(MACRO, ...) \ +MACRO(447, __VA_ARGS__) \ +DO_446(MACRO, __VA_ARGS__) + + +#define DO_448(MACRO, ...) \ +MACRO(448, __VA_ARGS__) \ +DO_447(MACRO, __VA_ARGS__) + + +#define DO_449(MACRO, ...) \ +MACRO(449, __VA_ARGS__) \ +DO_448(MACRO, __VA_ARGS__) + + +#define DO_450(MACRO, ...) \ +MACRO(450, __VA_ARGS__) \ +DO_449(MACRO, __VA_ARGS__) + + +#define DO_451(MACRO, ...) \ +MACRO(451, __VA_ARGS__) \ +DO_450(MACRO, __VA_ARGS__) + + +#define DO_452(MACRO, ...) \ +MACRO(452, __VA_ARGS__) \ +DO_451(MACRO, __VA_ARGS__) + + +#define DO_453(MACRO, ...) \ +MACRO(453, __VA_ARGS__) \ +DO_452(MACRO, __VA_ARGS__) + + +#define DO_454(MACRO, ...) \ +MACRO(454, __VA_ARGS__) \ +DO_453(MACRO, __VA_ARGS__) + + +#define DO_455(MACRO, ...) \ +MACRO(455, __VA_ARGS__) \ +DO_454(MACRO, __VA_ARGS__) + + +#define DO_456(MACRO, ...) \ +MACRO(456, __VA_ARGS__) \ +DO_455(MACRO, __VA_ARGS__) + + +#define DO_457(MACRO, ...) \ +MACRO(457, __VA_ARGS__) \ +DO_456(MACRO, __VA_ARGS__) + + +#define DO_458(MACRO, ...) \ +MACRO(458, __VA_ARGS__) \ +DO_457(MACRO, __VA_ARGS__) + + +#define DO_459(MACRO, ...) \ +MACRO(459, __VA_ARGS__) \ +DO_458(MACRO, __VA_ARGS__) + + +#define DO_460(MACRO, ...) \ +MACRO(460, __VA_ARGS__) \ +DO_459(MACRO, __VA_ARGS__) + + +#define DO_461(MACRO, ...) \ +MACRO(461, __VA_ARGS__) \ +DO_460(MACRO, __VA_ARGS__) + + +#define DO_462(MACRO, ...) \ +MACRO(462, __VA_ARGS__) \ +DO_461(MACRO, __VA_ARGS__) + + +#define DO_463(MACRO, ...) \ +MACRO(463, __VA_ARGS__) \ +DO_462(MACRO, __VA_ARGS__) + + +#define DO_464(MACRO, ...) \ +MACRO(464, __VA_ARGS__) \ +DO_463(MACRO, __VA_ARGS__) + + +#define DO_465(MACRO, ...) \ +MACRO(465, __VA_ARGS__) \ +DO_464(MACRO, __VA_ARGS__) + + +#define DO_466(MACRO, ...) \ +MACRO(466, __VA_ARGS__) \ +DO_465(MACRO, __VA_ARGS__) + + +#define DO_467(MACRO, ...) \ +MACRO(467, __VA_ARGS__) \ +DO_466(MACRO, __VA_ARGS__) + + +#define DO_468(MACRO, ...) \ +MACRO(468, __VA_ARGS__) \ +DO_467(MACRO, __VA_ARGS__) + + +#define DO_469(MACRO, ...) \ +MACRO(469, __VA_ARGS__) \ +DO_468(MACRO, __VA_ARGS__) + + +#define DO_470(MACRO, ...) \ +MACRO(470, __VA_ARGS__) \ +DO_469(MACRO, __VA_ARGS__) + + +#define DO_471(MACRO, ...) \ +MACRO(471, __VA_ARGS__) \ +DO_470(MACRO, __VA_ARGS__) + + +#define DO_472(MACRO, ...) \ +MACRO(472, __VA_ARGS__) \ +DO_471(MACRO, __VA_ARGS__) + + +#define DO_473(MACRO, ...) \ +MACRO(473, __VA_ARGS__) \ +DO_472(MACRO, __VA_ARGS__) + + +#define DO_474(MACRO, ...) \ +MACRO(474, __VA_ARGS__) \ +DO_473(MACRO, __VA_ARGS__) + + +#define DO_475(MACRO, ...) \ +MACRO(475, __VA_ARGS__) \ +DO_474(MACRO, __VA_ARGS__) + + +#define DO_476(MACRO, ...) \ +MACRO(476, __VA_ARGS__) \ +DO_475(MACRO, __VA_ARGS__) + + +#define DO_477(MACRO, ...) \ +MACRO(477, __VA_ARGS__) \ +DO_476(MACRO, __VA_ARGS__) + + +#define DO_478(MACRO, ...) \ +MACRO(478, __VA_ARGS__) \ +DO_477(MACRO, __VA_ARGS__) + + +#define DO_479(MACRO, ...) \ +MACRO(479, __VA_ARGS__) \ +DO_478(MACRO, __VA_ARGS__) + + +#define DO_480(MACRO, ...) \ +MACRO(480, __VA_ARGS__) \ +DO_479(MACRO, __VA_ARGS__) + + +#define DO_481(MACRO, ...) \ +MACRO(481, __VA_ARGS__) \ +DO_480(MACRO, __VA_ARGS__) + + +#define DO_482(MACRO, ...) \ +MACRO(482, __VA_ARGS__) \ +DO_481(MACRO, __VA_ARGS__) + + +#define DO_483(MACRO, ...) \ +MACRO(483, __VA_ARGS__) \ +DO_482(MACRO, __VA_ARGS__) + + +#define DO_484(MACRO, ...) \ +MACRO(484, __VA_ARGS__) \ +DO_483(MACRO, __VA_ARGS__) + + +#define DO_485(MACRO, ...) \ +MACRO(485, __VA_ARGS__) \ +DO_484(MACRO, __VA_ARGS__) + + +#define DO_486(MACRO, ...) \ +MACRO(486, __VA_ARGS__) \ +DO_485(MACRO, __VA_ARGS__) + + +#define DO_487(MACRO, ...) \ +MACRO(487, __VA_ARGS__) \ +DO_486(MACRO, __VA_ARGS__) + + +#define DO_488(MACRO, ...) \ +MACRO(488, __VA_ARGS__) \ +DO_487(MACRO, __VA_ARGS__) + + +#define DO_489(MACRO, ...) \ +MACRO(489, __VA_ARGS__) \ +DO_488(MACRO, __VA_ARGS__) + + +#define DO_490(MACRO, ...) \ +MACRO(490, __VA_ARGS__) \ +DO_489(MACRO, __VA_ARGS__) + + +#define DO_491(MACRO, ...) \ +MACRO(491, __VA_ARGS__) \ +DO_490(MACRO, __VA_ARGS__) + + +#define DO_492(MACRO, ...) \ +MACRO(492, __VA_ARGS__) \ +DO_491(MACRO, __VA_ARGS__) + + +#define DO_493(MACRO, ...) \ +MACRO(493, __VA_ARGS__) \ +DO_492(MACRO, __VA_ARGS__) + + +#define DO_494(MACRO, ...) \ +MACRO(494, __VA_ARGS__) \ +DO_493(MACRO, __VA_ARGS__) + + +#define DO_495(MACRO, ...) \ +MACRO(495, __VA_ARGS__) \ +DO_494(MACRO, __VA_ARGS__) + + +#define DO_496(MACRO, ...) \ +MACRO(496, __VA_ARGS__) \ +DO_495(MACRO, __VA_ARGS__) + + +#define DO_497(MACRO, ...) \ +MACRO(497, __VA_ARGS__) \ +DO_496(MACRO, __VA_ARGS__) + + +#define DO_498(MACRO, ...) \ +MACRO(498, __VA_ARGS__) \ +DO_497(MACRO, __VA_ARGS__) + + +#define DO_499(MACRO, ...) \ +MACRO(499, __VA_ARGS__) \ +DO_498(MACRO, __VA_ARGS__) + + +#define DO_500(MACRO, ...) \ +MACRO(500, __VA_ARGS__) \ +DO_499(MACRO, __VA_ARGS__) + + +#define DO_501(MACRO, ...) \ +MACRO(501, __VA_ARGS__) \ +DO_500(MACRO, __VA_ARGS__) + + +#define DO_502(MACRO, ...) \ +MACRO(502, __VA_ARGS__) \ +DO_501(MACRO, __VA_ARGS__) + + +#define DO_503(MACRO, ...) \ +MACRO(503, __VA_ARGS__) \ +DO_502(MACRO, __VA_ARGS__) + + +#define DO_504(MACRO, ...) \ +MACRO(504, __VA_ARGS__) \ +DO_503(MACRO, __VA_ARGS__) + + +#define DO_505(MACRO, ...) \ +MACRO(505, __VA_ARGS__) \ +DO_504(MACRO, __VA_ARGS__) + + +#define DO_506(MACRO, ...) \ +MACRO(506, __VA_ARGS__) \ +DO_505(MACRO, __VA_ARGS__) + + +#define DO_507(MACRO, ...) \ +MACRO(507, __VA_ARGS__) \ +DO_506(MACRO, __VA_ARGS__) + + +#define DO_508(MACRO, ...) \ +MACRO(508, __VA_ARGS__) \ +DO_507(MACRO, __VA_ARGS__) + + +#define DO_509(MACRO, ...) \ +MACRO(509, __VA_ARGS__) \ +DO_508(MACRO, __VA_ARGS__) + + +#define DO_510(MACRO, ...) \ +MACRO(510, __VA_ARGS__) \ +DO_509(MACRO, __VA_ARGS__) + + +#define DO_511(MACRO, ...) \ +MACRO(511, __VA_ARGS__) \ +DO_510(MACRO, __VA_ARGS__) + + +#define DO_512(MACRO, ...) \ +MACRO(512, __VA_ARGS__) \ +DO_511(MACRO, __VA_ARGS__) + + +#define DO_513(MACRO, ...) \ +MACRO(513, __VA_ARGS__) \ +DO_512(MACRO, __VA_ARGS__) + + +#define DO_514(MACRO, ...) \ +MACRO(514, __VA_ARGS__) \ +DO_513(MACRO, __VA_ARGS__) + + +#define DO_515(MACRO, ...) \ +MACRO(515, __VA_ARGS__) \ +DO_514(MACRO, __VA_ARGS__) + + +#define DO_516(MACRO, ...) \ +MACRO(516, __VA_ARGS__) \ +DO_515(MACRO, __VA_ARGS__) + + +#define DO_517(MACRO, ...) \ +MACRO(517, __VA_ARGS__) \ +DO_516(MACRO, __VA_ARGS__) + + +#define DO_518(MACRO, ...) \ +MACRO(518, __VA_ARGS__) \ +DO_517(MACRO, __VA_ARGS__) + + +#define DO_519(MACRO, ...) \ +MACRO(519, __VA_ARGS__) \ +DO_518(MACRO, __VA_ARGS__) + + +#define DO_520(MACRO, ...) \ +MACRO(520, __VA_ARGS__) \ +DO_519(MACRO, __VA_ARGS__) + + +#define DO_521(MACRO, ...) \ +MACRO(521, __VA_ARGS__) \ +DO_520(MACRO, __VA_ARGS__) + + +#define DO_522(MACRO, ...) \ +MACRO(522, __VA_ARGS__) \ +DO_521(MACRO, __VA_ARGS__) + + +#define DO_523(MACRO, ...) \ +MACRO(523, __VA_ARGS__) \ +DO_522(MACRO, __VA_ARGS__) + + +#define DO_524(MACRO, ...) \ +MACRO(524, __VA_ARGS__) \ +DO_523(MACRO, __VA_ARGS__) + + +#define DO_525(MACRO, ...) \ +MACRO(525, __VA_ARGS__) \ +DO_524(MACRO, __VA_ARGS__) + + +#define DO_526(MACRO, ...) \ +MACRO(526, __VA_ARGS__) \ +DO_525(MACRO, __VA_ARGS__) + + +#define DO_527(MACRO, ...) \ +MACRO(527, __VA_ARGS__) \ +DO_526(MACRO, __VA_ARGS__) + + +#define DO_528(MACRO, ...) \ +MACRO(528, __VA_ARGS__) \ +DO_527(MACRO, __VA_ARGS__) + + +#define DO_529(MACRO, ...) \ +MACRO(529, __VA_ARGS__) \ +DO_528(MACRO, __VA_ARGS__) + + +#define DO_530(MACRO, ...) \ +MACRO(530, __VA_ARGS__) \ +DO_529(MACRO, __VA_ARGS__) + + +#define DO_531(MACRO, ...) \ +MACRO(531, __VA_ARGS__) \ +DO_530(MACRO, __VA_ARGS__) + + +#define DO_532(MACRO, ...) \ +MACRO(532, __VA_ARGS__) \ +DO_531(MACRO, __VA_ARGS__) + + +#define DO_533(MACRO, ...) \ +MACRO(533, __VA_ARGS__) \ +DO_532(MACRO, __VA_ARGS__) + + +#define DO_534(MACRO, ...) \ +MACRO(534, __VA_ARGS__) \ +DO_533(MACRO, __VA_ARGS__) + + +#define DO_535(MACRO, ...) \ +MACRO(535, __VA_ARGS__) \ +DO_534(MACRO, __VA_ARGS__) + + +#define DO_536(MACRO, ...) \ +MACRO(536, __VA_ARGS__) \ +DO_535(MACRO, __VA_ARGS__) + + +#define DO_537(MACRO, ...) \ +MACRO(537, __VA_ARGS__) \ +DO_536(MACRO, __VA_ARGS__) + + +#define DO_538(MACRO, ...) \ +MACRO(538, __VA_ARGS__) \ +DO_537(MACRO, __VA_ARGS__) + + +#define DO_539(MACRO, ...) \ +MACRO(539, __VA_ARGS__) \ +DO_538(MACRO, __VA_ARGS__) + + +#define DO_540(MACRO, ...) \ +MACRO(540, __VA_ARGS__) \ +DO_539(MACRO, __VA_ARGS__) + + +#define DO_541(MACRO, ...) \ +MACRO(541, __VA_ARGS__) \ +DO_540(MACRO, __VA_ARGS__) + + +#define DO_542(MACRO, ...) \ +MACRO(542, __VA_ARGS__) \ +DO_541(MACRO, __VA_ARGS__) + + +#define DO_543(MACRO, ...) \ +MACRO(543, __VA_ARGS__) \ +DO_542(MACRO, __VA_ARGS__) + + +#define DO_544(MACRO, ...) \ +MACRO(544, __VA_ARGS__) \ +DO_543(MACRO, __VA_ARGS__) + + +#define DO_545(MACRO, ...) \ +MACRO(545, __VA_ARGS__) \ +DO_544(MACRO, __VA_ARGS__) + + +#define DO_546(MACRO, ...) \ +MACRO(546, __VA_ARGS__) \ +DO_545(MACRO, __VA_ARGS__) + + +#define DO_547(MACRO, ...) \ +MACRO(547, __VA_ARGS__) \ +DO_546(MACRO, __VA_ARGS__) + + +#define DO_548(MACRO, ...) \ +MACRO(548, __VA_ARGS__) \ +DO_547(MACRO, __VA_ARGS__) + + +#define DO_549(MACRO, ...) \ +MACRO(549, __VA_ARGS__) \ +DO_548(MACRO, __VA_ARGS__) + + +#define DO_550(MACRO, ...) \ +MACRO(550, __VA_ARGS__) \ +DO_549(MACRO, __VA_ARGS__) + + +#define DO_551(MACRO, ...) \ +MACRO(551, __VA_ARGS__) \ +DO_550(MACRO, __VA_ARGS__) + + +#define DO_552(MACRO, ...) \ +MACRO(552, __VA_ARGS__) \ +DO_551(MACRO, __VA_ARGS__) + + +#define DO_553(MACRO, ...) \ +MACRO(553, __VA_ARGS__) \ +DO_552(MACRO, __VA_ARGS__) + + +#define DO_554(MACRO, ...) \ +MACRO(554, __VA_ARGS__) \ +DO_553(MACRO, __VA_ARGS__) + + +#define DO_555(MACRO, ...) \ +MACRO(555, __VA_ARGS__) \ +DO_554(MACRO, __VA_ARGS__) + + +#define DO_556(MACRO, ...) \ +MACRO(556, __VA_ARGS__) \ +DO_555(MACRO, __VA_ARGS__) + + +#define DO_557(MACRO, ...) \ +MACRO(557, __VA_ARGS__) \ +DO_556(MACRO, __VA_ARGS__) + + +#define DO_558(MACRO, ...) \ +MACRO(558, __VA_ARGS__) \ +DO_557(MACRO, __VA_ARGS__) + + +#define DO_559(MACRO, ...) \ +MACRO(559, __VA_ARGS__) \ +DO_558(MACRO, __VA_ARGS__) + + +#define DO_560(MACRO, ...) \ +MACRO(560, __VA_ARGS__) \ +DO_559(MACRO, __VA_ARGS__) + + +#define DO_561(MACRO, ...) \ +MACRO(561, __VA_ARGS__) \ +DO_560(MACRO, __VA_ARGS__) + + +#define DO_562(MACRO, ...) \ +MACRO(562, __VA_ARGS__) \ +DO_561(MACRO, __VA_ARGS__) + + +#define DO_563(MACRO, ...) \ +MACRO(563, __VA_ARGS__) \ +DO_562(MACRO, __VA_ARGS__) + + +#define DO_564(MACRO, ...) \ +MACRO(564, __VA_ARGS__) \ +DO_563(MACRO, __VA_ARGS__) + + +#define DO_565(MACRO, ...) \ +MACRO(565, __VA_ARGS__) \ +DO_564(MACRO, __VA_ARGS__) + + +#define DO_566(MACRO, ...) \ +MACRO(566, __VA_ARGS__) \ +DO_565(MACRO, __VA_ARGS__) + + +#define DO_567(MACRO, ...) \ +MACRO(567, __VA_ARGS__) \ +DO_566(MACRO, __VA_ARGS__) + + +#define DO_568(MACRO, ...) \ +MACRO(568, __VA_ARGS__) \ +DO_567(MACRO, __VA_ARGS__) + + +#define DO_569(MACRO, ...) \ +MACRO(569, __VA_ARGS__) \ +DO_568(MACRO, __VA_ARGS__) + + +#define DO_570(MACRO, ...) \ +MACRO(570, __VA_ARGS__) \ +DO_569(MACRO, __VA_ARGS__) + + +#define DO_571(MACRO, ...) \ +MACRO(571, __VA_ARGS__) \ +DO_570(MACRO, __VA_ARGS__) + + +#define DO_572(MACRO, ...) \ +MACRO(572, __VA_ARGS__) \ +DO_571(MACRO, __VA_ARGS__) + + +#define DO_573(MACRO, ...) \ +MACRO(573, __VA_ARGS__) \ +DO_572(MACRO, __VA_ARGS__) + + +#define DO_574(MACRO, ...) \ +MACRO(574, __VA_ARGS__) \ +DO_573(MACRO, __VA_ARGS__) + + +#define DO_575(MACRO, ...) \ +MACRO(575, __VA_ARGS__) \ +DO_574(MACRO, __VA_ARGS__) + + +#define DO_576(MACRO, ...) \ +MACRO(576, __VA_ARGS__) \ +DO_575(MACRO, __VA_ARGS__) + + +#define DO_577(MACRO, ...) \ +MACRO(577, __VA_ARGS__) \ +DO_576(MACRO, __VA_ARGS__) + + +#define DO_578(MACRO, ...) \ +MACRO(578, __VA_ARGS__) \ +DO_577(MACRO, __VA_ARGS__) + + +#define DO_579(MACRO, ...) \ +MACRO(579, __VA_ARGS__) \ +DO_578(MACRO, __VA_ARGS__) + + +#define DO_580(MACRO, ...) \ +MACRO(580, __VA_ARGS__) \ +DO_579(MACRO, __VA_ARGS__) + + +#define DO_581(MACRO, ...) \ +MACRO(581, __VA_ARGS__) \ +DO_580(MACRO, __VA_ARGS__) + + +#define DO_582(MACRO, ...) \ +MACRO(582, __VA_ARGS__) \ +DO_581(MACRO, __VA_ARGS__) + + +#define DO_583(MACRO, ...) \ +MACRO(583, __VA_ARGS__) \ +DO_582(MACRO, __VA_ARGS__) + + +#define DO_584(MACRO, ...) \ +MACRO(584, __VA_ARGS__) \ +DO_583(MACRO, __VA_ARGS__) + + +#define DO_585(MACRO, ...) \ +MACRO(585, __VA_ARGS__) \ +DO_584(MACRO, __VA_ARGS__) + + +#define DO_586(MACRO, ...) \ +MACRO(586, __VA_ARGS__) \ +DO_585(MACRO, __VA_ARGS__) + + +#define DO_587(MACRO, ...) \ +MACRO(587, __VA_ARGS__) \ +DO_586(MACRO, __VA_ARGS__) + + +#define DO_588(MACRO, ...) \ +MACRO(588, __VA_ARGS__) \ +DO_587(MACRO, __VA_ARGS__) + + +#define DO_589(MACRO, ...) \ +MACRO(589, __VA_ARGS__) \ +DO_588(MACRO, __VA_ARGS__) + + +#define DO_590(MACRO, ...) \ +MACRO(590, __VA_ARGS__) \ +DO_589(MACRO, __VA_ARGS__) + + +#define DO_591(MACRO, ...) \ +MACRO(591, __VA_ARGS__) \ +DO_590(MACRO, __VA_ARGS__) + + +#define DO_592(MACRO, ...) \ +MACRO(592, __VA_ARGS__) \ +DO_591(MACRO, __VA_ARGS__) + + +#define DO_593(MACRO, ...) \ +MACRO(593, __VA_ARGS__) \ +DO_592(MACRO, __VA_ARGS__) + + +#define DO_594(MACRO, ...) \ +MACRO(594, __VA_ARGS__) \ +DO_593(MACRO, __VA_ARGS__) + + +#define DO_595(MACRO, ...) \ +MACRO(595, __VA_ARGS__) \ +DO_594(MACRO, __VA_ARGS__) + + +#define DO_596(MACRO, ...) \ +MACRO(596, __VA_ARGS__) \ +DO_595(MACRO, __VA_ARGS__) + + +#define DO_597(MACRO, ...) \ +MACRO(597, __VA_ARGS__) \ +DO_596(MACRO, __VA_ARGS__) + + +#define DO_598(MACRO, ...) \ +MACRO(598, __VA_ARGS__) \ +DO_597(MACRO, __VA_ARGS__) + + +#define DO_599(MACRO, ...) \ +MACRO(599, __VA_ARGS__) \ +DO_598(MACRO, __VA_ARGS__) + + +#define DO_600(MACRO, ...) \ +MACRO(600, __VA_ARGS__) \ +DO_599(MACRO, __VA_ARGS__) + + +#define DO_601(MACRO, ...) \ +MACRO(601, __VA_ARGS__) \ +DO_600(MACRO, __VA_ARGS__) + + +#define DO_602(MACRO, ...) \ +MACRO(602, __VA_ARGS__) \ +DO_601(MACRO, __VA_ARGS__) + + +#define DO_603(MACRO, ...) \ +MACRO(603, __VA_ARGS__) \ +DO_602(MACRO, __VA_ARGS__) + + +#define DO_604(MACRO, ...) \ +MACRO(604, __VA_ARGS__) \ +DO_603(MACRO, __VA_ARGS__) + + +#define DO_605(MACRO, ...) \ +MACRO(605, __VA_ARGS__) \ +DO_604(MACRO, __VA_ARGS__) + + +#define DO_606(MACRO, ...) \ +MACRO(606, __VA_ARGS__) \ +DO_605(MACRO, __VA_ARGS__) + + +#define DO_607(MACRO, ...) \ +MACRO(607, __VA_ARGS__) \ +DO_606(MACRO, __VA_ARGS__) + + +#define DO_608(MACRO, ...) \ +MACRO(608, __VA_ARGS__) \ +DO_607(MACRO, __VA_ARGS__) + + +#define DO_609(MACRO, ...) \ +MACRO(609, __VA_ARGS__) \ +DO_608(MACRO, __VA_ARGS__) + + +#define DO_610(MACRO, ...) \ +MACRO(610, __VA_ARGS__) \ +DO_609(MACRO, __VA_ARGS__) + + +#define DO_611(MACRO, ...) \ +MACRO(611, __VA_ARGS__) \ +DO_610(MACRO, __VA_ARGS__) + + +#define DO_612(MACRO, ...) \ +MACRO(612, __VA_ARGS__) \ +DO_611(MACRO, __VA_ARGS__) + + +#define DO_613(MACRO, ...) \ +MACRO(613, __VA_ARGS__) \ +DO_612(MACRO, __VA_ARGS__) + + +#define DO_614(MACRO, ...) \ +MACRO(614, __VA_ARGS__) \ +DO_613(MACRO, __VA_ARGS__) + + +#define DO_615(MACRO, ...) \ +MACRO(615, __VA_ARGS__) \ +DO_614(MACRO, __VA_ARGS__) + + +#define DO_616(MACRO, ...) \ +MACRO(616, __VA_ARGS__) \ +DO_615(MACRO, __VA_ARGS__) + + +#define DO_617(MACRO, ...) \ +MACRO(617, __VA_ARGS__) \ +DO_616(MACRO, __VA_ARGS__) + + +#define DO_618(MACRO, ...) \ +MACRO(618, __VA_ARGS__) \ +DO_617(MACRO, __VA_ARGS__) + + +#define DO_619(MACRO, ...) \ +MACRO(619, __VA_ARGS__) \ +DO_618(MACRO, __VA_ARGS__) + + +#define DO_620(MACRO, ...) \ +MACRO(620, __VA_ARGS__) \ +DO_619(MACRO, __VA_ARGS__) + + +#define DO_621(MACRO, ...) \ +MACRO(621, __VA_ARGS__) \ +DO_620(MACRO, __VA_ARGS__) + + +#define DO_622(MACRO, ...) \ +MACRO(622, __VA_ARGS__) \ +DO_621(MACRO, __VA_ARGS__) + + +#define DO_623(MACRO, ...) \ +MACRO(623, __VA_ARGS__) \ +DO_622(MACRO, __VA_ARGS__) + + +#define DO_624(MACRO, ...) \ +MACRO(624, __VA_ARGS__) \ +DO_623(MACRO, __VA_ARGS__) + + +#define DO_625(MACRO, ...) \ +MACRO(625, __VA_ARGS__) \ +DO_624(MACRO, __VA_ARGS__) + + +#define DO_626(MACRO, ...) \ +MACRO(626, __VA_ARGS__) \ +DO_625(MACRO, __VA_ARGS__) + + +#define DO_627(MACRO, ...) \ +MACRO(627, __VA_ARGS__) \ +DO_626(MACRO, __VA_ARGS__) + + +#define DO_628(MACRO, ...) \ +MACRO(628, __VA_ARGS__) \ +DO_627(MACRO, __VA_ARGS__) + + +#define DO_629(MACRO, ...) \ +MACRO(629, __VA_ARGS__) \ +DO_628(MACRO, __VA_ARGS__) + + +#define DO_630(MACRO, ...) \ +MACRO(630, __VA_ARGS__) \ +DO_629(MACRO, __VA_ARGS__) + + +#define DO_631(MACRO, ...) \ +MACRO(631, __VA_ARGS__) \ +DO_630(MACRO, __VA_ARGS__) + + +#define DO_632(MACRO, ...) \ +MACRO(632, __VA_ARGS__) \ +DO_631(MACRO, __VA_ARGS__) + + +#define DO_633(MACRO, ...) \ +MACRO(633, __VA_ARGS__) \ +DO_632(MACRO, __VA_ARGS__) + + +#define DO_634(MACRO, ...) \ +MACRO(634, __VA_ARGS__) \ +DO_633(MACRO, __VA_ARGS__) + + +#define DO_635(MACRO, ...) \ +MACRO(635, __VA_ARGS__) \ +DO_634(MACRO, __VA_ARGS__) + + +#define DO_636(MACRO, ...) \ +MACRO(636, __VA_ARGS__) \ +DO_635(MACRO, __VA_ARGS__) + + +#define DO_637(MACRO, ...) \ +MACRO(637, __VA_ARGS__) \ +DO_636(MACRO, __VA_ARGS__) + + +#define DO_638(MACRO, ...) \ +MACRO(638, __VA_ARGS__) \ +DO_637(MACRO, __VA_ARGS__) + + +#define DO_639(MACRO, ...) \ +MACRO(639, __VA_ARGS__) \ +DO_638(MACRO, __VA_ARGS__) + + +#define DO_640(MACRO, ...) \ +MACRO(640, __VA_ARGS__) \ +DO_639(MACRO, __VA_ARGS__) + + +#define DO_641(MACRO, ...) \ +MACRO(641, __VA_ARGS__) \ +DO_640(MACRO, __VA_ARGS__) + + +#define DO_642(MACRO, ...) \ +MACRO(642, __VA_ARGS__) \ +DO_641(MACRO, __VA_ARGS__) + + +#define DO_643(MACRO, ...) \ +MACRO(643, __VA_ARGS__) \ +DO_642(MACRO, __VA_ARGS__) + + +#define DO_644(MACRO, ...) \ +MACRO(644, __VA_ARGS__) \ +DO_643(MACRO, __VA_ARGS__) + + +#define DO_645(MACRO, ...) \ +MACRO(645, __VA_ARGS__) \ +DO_644(MACRO, __VA_ARGS__) + + +#define DO_646(MACRO, ...) \ +MACRO(646, __VA_ARGS__) \ +DO_645(MACRO, __VA_ARGS__) + + +#define DO_647(MACRO, ...) \ +MACRO(647, __VA_ARGS__) \ +DO_646(MACRO, __VA_ARGS__) + + +#define DO_648(MACRO, ...) \ +MACRO(648, __VA_ARGS__) \ +DO_647(MACRO, __VA_ARGS__) + + +#define DO_649(MACRO, ...) \ +MACRO(649, __VA_ARGS__) \ +DO_648(MACRO, __VA_ARGS__) + + +#define DO_650(MACRO, ...) \ +MACRO(650, __VA_ARGS__) \ +DO_649(MACRO, __VA_ARGS__) + + +#define DO_651(MACRO, ...) \ +MACRO(651, __VA_ARGS__) \ +DO_650(MACRO, __VA_ARGS__) + + +#define DO_652(MACRO, ...) \ +MACRO(652, __VA_ARGS__) \ +DO_651(MACRO, __VA_ARGS__) + + +#define DO_653(MACRO, ...) \ +MACRO(653, __VA_ARGS__) \ +DO_652(MACRO, __VA_ARGS__) + + +#define DO_654(MACRO, ...) \ +MACRO(654, __VA_ARGS__) \ +DO_653(MACRO, __VA_ARGS__) + + +#define DO_655(MACRO, ...) \ +MACRO(655, __VA_ARGS__) \ +DO_654(MACRO, __VA_ARGS__) + + +#define DO_656(MACRO, ...) \ +MACRO(656, __VA_ARGS__) \ +DO_655(MACRO, __VA_ARGS__) + + +#define DO_657(MACRO, ...) \ +MACRO(657, __VA_ARGS__) \ +DO_656(MACRO, __VA_ARGS__) + + +#define DO_658(MACRO, ...) \ +MACRO(658, __VA_ARGS__) \ +DO_657(MACRO, __VA_ARGS__) + + +#define DO_659(MACRO, ...) \ +MACRO(659, __VA_ARGS__) \ +DO_658(MACRO, __VA_ARGS__) + + +#define DO_660(MACRO, ...) \ +MACRO(660, __VA_ARGS__) \ +DO_659(MACRO, __VA_ARGS__) + + +#define DO_661(MACRO, ...) \ +MACRO(661, __VA_ARGS__) \ +DO_660(MACRO, __VA_ARGS__) + + +#define DO_662(MACRO, ...) \ +MACRO(662, __VA_ARGS__) \ +DO_661(MACRO, __VA_ARGS__) + + +#define DO_663(MACRO, ...) \ +MACRO(663, __VA_ARGS__) \ +DO_662(MACRO, __VA_ARGS__) + + +#define DO_664(MACRO, ...) \ +MACRO(664, __VA_ARGS__) \ +DO_663(MACRO, __VA_ARGS__) + + +#define DO_665(MACRO, ...) \ +MACRO(665, __VA_ARGS__) \ +DO_664(MACRO, __VA_ARGS__) + + +#define DO_666(MACRO, ...) \ +MACRO(666, __VA_ARGS__) \ +DO_665(MACRO, __VA_ARGS__) + + +#define DO_667(MACRO, ...) \ +MACRO(667, __VA_ARGS__) \ +DO_666(MACRO, __VA_ARGS__) + + +#define DO_668(MACRO, ...) \ +MACRO(668, __VA_ARGS__) \ +DO_667(MACRO, __VA_ARGS__) + + +#define DO_669(MACRO, ...) \ +MACRO(669, __VA_ARGS__) \ +DO_668(MACRO, __VA_ARGS__) + + +#define DO_670(MACRO, ...) \ +MACRO(670, __VA_ARGS__) \ +DO_669(MACRO, __VA_ARGS__) + + +#define DO_671(MACRO, ...) \ +MACRO(671, __VA_ARGS__) \ +DO_670(MACRO, __VA_ARGS__) + + +#define DO_672(MACRO, ...) \ +MACRO(672, __VA_ARGS__) \ +DO_671(MACRO, __VA_ARGS__) + + +#define DO_673(MACRO, ...) \ +MACRO(673, __VA_ARGS__) \ +DO_672(MACRO, __VA_ARGS__) + + +#define DO_674(MACRO, ...) \ +MACRO(674, __VA_ARGS__) \ +DO_673(MACRO, __VA_ARGS__) + + +#define DO_675(MACRO, ...) \ +MACRO(675, __VA_ARGS__) \ +DO_674(MACRO, __VA_ARGS__) + + +#define DO_676(MACRO, ...) \ +MACRO(676, __VA_ARGS__) \ +DO_675(MACRO, __VA_ARGS__) + + +#define DO_677(MACRO, ...) \ +MACRO(677, __VA_ARGS__) \ +DO_676(MACRO, __VA_ARGS__) + + +#define DO_678(MACRO, ...) \ +MACRO(678, __VA_ARGS__) \ +DO_677(MACRO, __VA_ARGS__) + + +#define DO_679(MACRO, ...) \ +MACRO(679, __VA_ARGS__) \ +DO_678(MACRO, __VA_ARGS__) + + +#define DO_680(MACRO, ...) \ +MACRO(680, __VA_ARGS__) \ +DO_679(MACRO, __VA_ARGS__) + + +#define DO_681(MACRO, ...) \ +MACRO(681, __VA_ARGS__) \ +DO_680(MACRO, __VA_ARGS__) + + +#define DO_682(MACRO, ...) \ +MACRO(682, __VA_ARGS__) \ +DO_681(MACRO, __VA_ARGS__) + + +#define DO_683(MACRO, ...) \ +MACRO(683, __VA_ARGS__) \ +DO_682(MACRO, __VA_ARGS__) + + +#define DO_684(MACRO, ...) \ +MACRO(684, __VA_ARGS__) \ +DO_683(MACRO, __VA_ARGS__) + + +#define DO_685(MACRO, ...) \ +MACRO(685, __VA_ARGS__) \ +DO_684(MACRO, __VA_ARGS__) + + +#define DO_686(MACRO, ...) \ +MACRO(686, __VA_ARGS__) \ +DO_685(MACRO, __VA_ARGS__) + + +#define DO_687(MACRO, ...) \ +MACRO(687, __VA_ARGS__) \ +DO_686(MACRO, __VA_ARGS__) + + +#define DO_688(MACRO, ...) \ +MACRO(688, __VA_ARGS__) \ +DO_687(MACRO, __VA_ARGS__) + + +#define DO_689(MACRO, ...) \ +MACRO(689, __VA_ARGS__) \ +DO_688(MACRO, __VA_ARGS__) + + +#define DO_690(MACRO, ...) \ +MACRO(690, __VA_ARGS__) \ +DO_689(MACRO, __VA_ARGS__) + + +#define DO_691(MACRO, ...) \ +MACRO(691, __VA_ARGS__) \ +DO_690(MACRO, __VA_ARGS__) + + +#define DO_692(MACRO, ...) \ +MACRO(692, __VA_ARGS__) \ +DO_691(MACRO, __VA_ARGS__) + + +#define DO_693(MACRO, ...) \ +MACRO(693, __VA_ARGS__) \ +DO_692(MACRO, __VA_ARGS__) + + +#define DO_694(MACRO, ...) \ +MACRO(694, __VA_ARGS__) \ +DO_693(MACRO, __VA_ARGS__) + + +#define DO_695(MACRO, ...) \ +MACRO(695, __VA_ARGS__) \ +DO_694(MACRO, __VA_ARGS__) + + +#define DO_696(MACRO, ...) \ +MACRO(696, __VA_ARGS__) \ +DO_695(MACRO, __VA_ARGS__) + + +#define DO_697(MACRO, ...) \ +MACRO(697, __VA_ARGS__) \ +DO_696(MACRO, __VA_ARGS__) + + +#define DO_698(MACRO, ...) \ +MACRO(698, __VA_ARGS__) \ +DO_697(MACRO, __VA_ARGS__) + + +#define DO_699(MACRO, ...) \ +MACRO(699, __VA_ARGS__) \ +DO_698(MACRO, __VA_ARGS__) + + +#define DO_700(MACRO, ...) \ +MACRO(700, __VA_ARGS__) \ +DO_699(MACRO, __VA_ARGS__) + + +#define DO_701(MACRO, ...) \ +MACRO(701, __VA_ARGS__) \ +DO_700(MACRO, __VA_ARGS__) + + +#define DO_702(MACRO, ...) \ +MACRO(702, __VA_ARGS__) \ +DO_701(MACRO, __VA_ARGS__) + + +#define DO_703(MACRO, ...) \ +MACRO(703, __VA_ARGS__) \ +DO_702(MACRO, __VA_ARGS__) + + +#define DO_704(MACRO, ...) \ +MACRO(704, __VA_ARGS__) \ +DO_703(MACRO, __VA_ARGS__) + + +#define DO_705(MACRO, ...) \ +MACRO(705, __VA_ARGS__) \ +DO_704(MACRO, __VA_ARGS__) + + +#define DO_706(MACRO, ...) \ +MACRO(706, __VA_ARGS__) \ +DO_705(MACRO, __VA_ARGS__) + + +#define DO_707(MACRO, ...) \ +MACRO(707, __VA_ARGS__) \ +DO_706(MACRO, __VA_ARGS__) + + +#define DO_708(MACRO, ...) \ +MACRO(708, __VA_ARGS__) \ +DO_707(MACRO, __VA_ARGS__) + + +#define DO_709(MACRO, ...) \ +MACRO(709, __VA_ARGS__) \ +DO_708(MACRO, __VA_ARGS__) + + +#define DO_710(MACRO, ...) \ +MACRO(710, __VA_ARGS__) \ +DO_709(MACRO, __VA_ARGS__) + + +#define DO_711(MACRO, ...) \ +MACRO(711, __VA_ARGS__) \ +DO_710(MACRO, __VA_ARGS__) + + +#define DO_712(MACRO, ...) \ +MACRO(712, __VA_ARGS__) \ +DO_711(MACRO, __VA_ARGS__) + + +#define DO_713(MACRO, ...) \ +MACRO(713, __VA_ARGS__) \ +DO_712(MACRO, __VA_ARGS__) + + +#define DO_714(MACRO, ...) \ +MACRO(714, __VA_ARGS__) \ +DO_713(MACRO, __VA_ARGS__) + + +#define DO_715(MACRO, ...) \ +MACRO(715, __VA_ARGS__) \ +DO_714(MACRO, __VA_ARGS__) + + +#define DO_716(MACRO, ...) \ +MACRO(716, __VA_ARGS__) \ +DO_715(MACRO, __VA_ARGS__) + + +#define DO_717(MACRO, ...) \ +MACRO(717, __VA_ARGS__) \ +DO_716(MACRO, __VA_ARGS__) + + +#define DO_718(MACRO, ...) \ +MACRO(718, __VA_ARGS__) \ +DO_717(MACRO, __VA_ARGS__) + + +#define DO_719(MACRO, ...) \ +MACRO(719, __VA_ARGS__) \ +DO_718(MACRO, __VA_ARGS__) + + +#define DO_720(MACRO, ...) \ +MACRO(720, __VA_ARGS__) \ +DO_719(MACRO, __VA_ARGS__) + + +#define DO_721(MACRO, ...) \ +MACRO(721, __VA_ARGS__) \ +DO_720(MACRO, __VA_ARGS__) + + +#define DO_722(MACRO, ...) \ +MACRO(722, __VA_ARGS__) \ +DO_721(MACRO, __VA_ARGS__) + + +#define DO_723(MACRO, ...) \ +MACRO(723, __VA_ARGS__) \ +DO_722(MACRO, __VA_ARGS__) + + +#define DO_724(MACRO, ...) \ +MACRO(724, __VA_ARGS__) \ +DO_723(MACRO, __VA_ARGS__) + + +#define DO_725(MACRO, ...) \ +MACRO(725, __VA_ARGS__) \ +DO_724(MACRO, __VA_ARGS__) + + +#define DO_726(MACRO, ...) \ +MACRO(726, __VA_ARGS__) \ +DO_725(MACRO, __VA_ARGS__) + + +#define DO_727(MACRO, ...) \ +MACRO(727, __VA_ARGS__) \ +DO_726(MACRO, __VA_ARGS__) + + +#define DO_728(MACRO, ...) \ +MACRO(728, __VA_ARGS__) \ +DO_727(MACRO, __VA_ARGS__) + + +#define DO_729(MACRO, ...) \ +MACRO(729, __VA_ARGS__) \ +DO_728(MACRO, __VA_ARGS__) + + +#define DO_730(MACRO, ...) \ +MACRO(730, __VA_ARGS__) \ +DO_729(MACRO, __VA_ARGS__) + + +#define DO_731(MACRO, ...) \ +MACRO(731, __VA_ARGS__) \ +DO_730(MACRO, __VA_ARGS__) + + +#define DO_732(MACRO, ...) \ +MACRO(732, __VA_ARGS__) \ +DO_731(MACRO, __VA_ARGS__) + + +#define DO_733(MACRO, ...) \ +MACRO(733, __VA_ARGS__) \ +DO_732(MACRO, __VA_ARGS__) + + +#define DO_734(MACRO, ...) \ +MACRO(734, __VA_ARGS__) \ +DO_733(MACRO, __VA_ARGS__) + + +#define DO_735(MACRO, ...) \ +MACRO(735, __VA_ARGS__) \ +DO_734(MACRO, __VA_ARGS__) + + +#define DO_736(MACRO, ...) \ +MACRO(736, __VA_ARGS__) \ +DO_735(MACRO, __VA_ARGS__) + + +#define DO_737(MACRO, ...) \ +MACRO(737, __VA_ARGS__) \ +DO_736(MACRO, __VA_ARGS__) + + +#define DO_738(MACRO, ...) \ +MACRO(738, __VA_ARGS__) \ +DO_737(MACRO, __VA_ARGS__) + + +#define DO_739(MACRO, ...) \ +MACRO(739, __VA_ARGS__) \ +DO_738(MACRO, __VA_ARGS__) + + +#define DO_740(MACRO, ...) \ +MACRO(740, __VA_ARGS__) \ +DO_739(MACRO, __VA_ARGS__) + + +#define DO_741(MACRO, ...) \ +MACRO(741, __VA_ARGS__) \ +DO_740(MACRO, __VA_ARGS__) + + +#define DO_742(MACRO, ...) \ +MACRO(742, __VA_ARGS__) \ +DO_741(MACRO, __VA_ARGS__) + + +#define DO_743(MACRO, ...) \ +MACRO(743, __VA_ARGS__) \ +DO_742(MACRO, __VA_ARGS__) + + +#define DO_744(MACRO, ...) \ +MACRO(744, __VA_ARGS__) \ +DO_743(MACRO, __VA_ARGS__) + + +#define DO_745(MACRO, ...) \ +MACRO(745, __VA_ARGS__) \ +DO_744(MACRO, __VA_ARGS__) + + +#define DO_746(MACRO, ...) \ +MACRO(746, __VA_ARGS__) \ +DO_745(MACRO, __VA_ARGS__) + + +#define DO_747(MACRO, ...) \ +MACRO(747, __VA_ARGS__) \ +DO_746(MACRO, __VA_ARGS__) + + +#define DO_748(MACRO, ...) \ +MACRO(748, __VA_ARGS__) \ +DO_747(MACRO, __VA_ARGS__) + + +#define DO_749(MACRO, ...) \ +MACRO(749, __VA_ARGS__) \ +DO_748(MACRO, __VA_ARGS__) + + +#define DO_750(MACRO, ...) \ +MACRO(750, __VA_ARGS__) \ +DO_749(MACRO, __VA_ARGS__) + + +#define DO_751(MACRO, ...) \ +MACRO(751, __VA_ARGS__) \ +DO_750(MACRO, __VA_ARGS__) + + +#define DO_752(MACRO, ...) \ +MACRO(752, __VA_ARGS__) \ +DO_751(MACRO, __VA_ARGS__) + + +#define DO_753(MACRO, ...) \ +MACRO(753, __VA_ARGS__) \ +DO_752(MACRO, __VA_ARGS__) + + +#define DO_754(MACRO, ...) \ +MACRO(754, __VA_ARGS__) \ +DO_753(MACRO, __VA_ARGS__) + + +#define DO_755(MACRO, ...) \ +MACRO(755, __VA_ARGS__) \ +DO_754(MACRO, __VA_ARGS__) + + +#define DO_756(MACRO, ...) \ +MACRO(756, __VA_ARGS__) \ +DO_755(MACRO, __VA_ARGS__) + + +#define DO_757(MACRO, ...) \ +MACRO(757, __VA_ARGS__) \ +DO_756(MACRO, __VA_ARGS__) + + +#define DO_758(MACRO, ...) \ +MACRO(758, __VA_ARGS__) \ +DO_757(MACRO, __VA_ARGS__) + + +#define DO_759(MACRO, ...) \ +MACRO(759, __VA_ARGS__) \ +DO_758(MACRO, __VA_ARGS__) + + +#define DO_760(MACRO, ...) \ +MACRO(760, __VA_ARGS__) \ +DO_759(MACRO, __VA_ARGS__) + + +#define DO_761(MACRO, ...) \ +MACRO(761, __VA_ARGS__) \ +DO_760(MACRO, __VA_ARGS__) + + +#define DO_762(MACRO, ...) \ +MACRO(762, __VA_ARGS__) \ +DO_761(MACRO, __VA_ARGS__) + + +#define DO_763(MACRO, ...) \ +MACRO(763, __VA_ARGS__) \ +DO_762(MACRO, __VA_ARGS__) + + +#define DO_764(MACRO, ...) \ +MACRO(764, __VA_ARGS__) \ +DO_763(MACRO, __VA_ARGS__) + + +#define DO_765(MACRO, ...) \ +MACRO(765, __VA_ARGS__) \ +DO_764(MACRO, __VA_ARGS__) + + +#define DO_766(MACRO, ...) \ +MACRO(766, __VA_ARGS__) \ +DO_765(MACRO, __VA_ARGS__) + + +#define DO_767(MACRO, ...) \ +MACRO(767, __VA_ARGS__) \ +DO_766(MACRO, __VA_ARGS__) + + +#define DO_768(MACRO, ...) \ +MACRO(768, __VA_ARGS__) \ +DO_767(MACRO, __VA_ARGS__) + + +#define DO_769(MACRO, ...) \ +MACRO(769, __VA_ARGS__) \ +DO_768(MACRO, __VA_ARGS__) + + +#define DO_770(MACRO, ...) \ +MACRO(770, __VA_ARGS__) \ +DO_769(MACRO, __VA_ARGS__) + + +#define DO_771(MACRO, ...) \ +MACRO(771, __VA_ARGS__) \ +DO_770(MACRO, __VA_ARGS__) + + +#define DO_772(MACRO, ...) \ +MACRO(772, __VA_ARGS__) \ +DO_771(MACRO, __VA_ARGS__) + + +#define DO_773(MACRO, ...) \ +MACRO(773, __VA_ARGS__) \ +DO_772(MACRO, __VA_ARGS__) + + +#define DO_774(MACRO, ...) \ +MACRO(774, __VA_ARGS__) \ +DO_773(MACRO, __VA_ARGS__) + + +#define DO_775(MACRO, ...) \ +MACRO(775, __VA_ARGS__) \ +DO_774(MACRO, __VA_ARGS__) + + +#define DO_776(MACRO, ...) \ +MACRO(776, __VA_ARGS__) \ +DO_775(MACRO, __VA_ARGS__) + + +#define DO_777(MACRO, ...) \ +MACRO(777, __VA_ARGS__) \ +DO_776(MACRO, __VA_ARGS__) + + +#define DO_778(MACRO, ...) \ +MACRO(778, __VA_ARGS__) \ +DO_777(MACRO, __VA_ARGS__) + + +#define DO_779(MACRO, ...) \ +MACRO(779, __VA_ARGS__) \ +DO_778(MACRO, __VA_ARGS__) + + +#define DO_780(MACRO, ...) \ +MACRO(780, __VA_ARGS__) \ +DO_779(MACRO, __VA_ARGS__) + + +#define DO_781(MACRO, ...) \ +MACRO(781, __VA_ARGS__) \ +DO_780(MACRO, __VA_ARGS__) + + +#define DO_782(MACRO, ...) \ +MACRO(782, __VA_ARGS__) \ +DO_781(MACRO, __VA_ARGS__) + + +#define DO_783(MACRO, ...) \ +MACRO(783, __VA_ARGS__) \ +DO_782(MACRO, __VA_ARGS__) + + +#define DO_784(MACRO, ...) \ +MACRO(784, __VA_ARGS__) \ +DO_783(MACRO, __VA_ARGS__) + + +#define DO_785(MACRO, ...) \ +MACRO(785, __VA_ARGS__) \ +DO_784(MACRO, __VA_ARGS__) + + +#define DO_786(MACRO, ...) \ +MACRO(786, __VA_ARGS__) \ +DO_785(MACRO, __VA_ARGS__) + + +#define DO_787(MACRO, ...) \ +MACRO(787, __VA_ARGS__) \ +DO_786(MACRO, __VA_ARGS__) + + +#define DO_788(MACRO, ...) \ +MACRO(788, __VA_ARGS__) \ +DO_787(MACRO, __VA_ARGS__) + + +#define DO_789(MACRO, ...) \ +MACRO(789, __VA_ARGS__) \ +DO_788(MACRO, __VA_ARGS__) + + +#define DO_790(MACRO, ...) \ +MACRO(790, __VA_ARGS__) \ +DO_789(MACRO, __VA_ARGS__) + + +#define DO_791(MACRO, ...) \ +MACRO(791, __VA_ARGS__) \ +DO_790(MACRO, __VA_ARGS__) + + +#define DO_792(MACRO, ...) \ +MACRO(792, __VA_ARGS__) \ +DO_791(MACRO, __VA_ARGS__) + + +#define DO_793(MACRO, ...) \ +MACRO(793, __VA_ARGS__) \ +DO_792(MACRO, __VA_ARGS__) + + +#define DO_794(MACRO, ...) \ +MACRO(794, __VA_ARGS__) \ +DO_793(MACRO, __VA_ARGS__) + + +#define DO_795(MACRO, ...) \ +MACRO(795, __VA_ARGS__) \ +DO_794(MACRO, __VA_ARGS__) + + +#define DO_796(MACRO, ...) \ +MACRO(796, __VA_ARGS__) \ +DO_795(MACRO, __VA_ARGS__) + + +#define DO_797(MACRO, ...) \ +MACRO(797, __VA_ARGS__) \ +DO_796(MACRO, __VA_ARGS__) + + +#define DO_798(MACRO, ...) \ +MACRO(798, __VA_ARGS__) \ +DO_797(MACRO, __VA_ARGS__) + + +#define DO_799(MACRO, ...) \ +MACRO(799, __VA_ARGS__) \ +DO_798(MACRO, __VA_ARGS__) + + +#define DO_800(MACRO, ...) \ +MACRO(800, __VA_ARGS__) \ +DO_799(MACRO, __VA_ARGS__) + + +#define DO_801(MACRO, ...) \ +MACRO(801, __VA_ARGS__) \ +DO_800(MACRO, __VA_ARGS__) + + +#define DO_802(MACRO, ...) \ +MACRO(802, __VA_ARGS__) \ +DO_801(MACRO, __VA_ARGS__) + + +#define DO_803(MACRO, ...) \ +MACRO(803, __VA_ARGS__) \ +DO_802(MACRO, __VA_ARGS__) + + +#define DO_804(MACRO, ...) \ +MACRO(804, __VA_ARGS__) \ +DO_803(MACRO, __VA_ARGS__) + + +#define DO_805(MACRO, ...) \ +MACRO(805, __VA_ARGS__) \ +DO_804(MACRO, __VA_ARGS__) + + +#define DO_806(MACRO, ...) \ +MACRO(806, __VA_ARGS__) \ +DO_805(MACRO, __VA_ARGS__) + + +#define DO_807(MACRO, ...) \ +MACRO(807, __VA_ARGS__) \ +DO_806(MACRO, __VA_ARGS__) + + +#define DO_808(MACRO, ...) \ +MACRO(808, __VA_ARGS__) \ +DO_807(MACRO, __VA_ARGS__) + + +#define DO_809(MACRO, ...) \ +MACRO(809, __VA_ARGS__) \ +DO_808(MACRO, __VA_ARGS__) + + +#define DO_810(MACRO, ...) \ +MACRO(810, __VA_ARGS__) \ +DO_809(MACRO, __VA_ARGS__) + + +#define DO_811(MACRO, ...) \ +MACRO(811, __VA_ARGS__) \ +DO_810(MACRO, __VA_ARGS__) + + +#define DO_812(MACRO, ...) \ +MACRO(812, __VA_ARGS__) \ +DO_811(MACRO, __VA_ARGS__) + + +#define DO_813(MACRO, ...) \ +MACRO(813, __VA_ARGS__) \ +DO_812(MACRO, __VA_ARGS__) + + +#define DO_814(MACRO, ...) \ +MACRO(814, __VA_ARGS__) \ +DO_813(MACRO, __VA_ARGS__) + + +#define DO_815(MACRO, ...) \ +MACRO(815, __VA_ARGS__) \ +DO_814(MACRO, __VA_ARGS__) + + +#define DO_816(MACRO, ...) \ +MACRO(816, __VA_ARGS__) \ +DO_815(MACRO, __VA_ARGS__) + + +#define DO_817(MACRO, ...) \ +MACRO(817, __VA_ARGS__) \ +DO_816(MACRO, __VA_ARGS__) + + +#define DO_818(MACRO, ...) \ +MACRO(818, __VA_ARGS__) \ +DO_817(MACRO, __VA_ARGS__) + + +#define DO_819(MACRO, ...) \ +MACRO(819, __VA_ARGS__) \ +DO_818(MACRO, __VA_ARGS__) + + +#define DO_820(MACRO, ...) \ +MACRO(820, __VA_ARGS__) \ +DO_819(MACRO, __VA_ARGS__) + + +#define DO_821(MACRO, ...) \ +MACRO(821, __VA_ARGS__) \ +DO_820(MACRO, __VA_ARGS__) + + +#define DO_822(MACRO, ...) \ +MACRO(822, __VA_ARGS__) \ +DO_821(MACRO, __VA_ARGS__) + + +#define DO_823(MACRO, ...) \ +MACRO(823, __VA_ARGS__) \ +DO_822(MACRO, __VA_ARGS__) + + +#define DO_824(MACRO, ...) \ +MACRO(824, __VA_ARGS__) \ +DO_823(MACRO, __VA_ARGS__) + + +#define DO_825(MACRO, ...) \ +MACRO(825, __VA_ARGS__) \ +DO_824(MACRO, __VA_ARGS__) + + +#define DO_826(MACRO, ...) \ +MACRO(826, __VA_ARGS__) \ +DO_825(MACRO, __VA_ARGS__) + + +#define DO_827(MACRO, ...) \ +MACRO(827, __VA_ARGS__) \ +DO_826(MACRO, __VA_ARGS__) + + +#define DO_828(MACRO, ...) \ +MACRO(828, __VA_ARGS__) \ +DO_827(MACRO, __VA_ARGS__) + + +#define DO_829(MACRO, ...) \ +MACRO(829, __VA_ARGS__) \ +DO_828(MACRO, __VA_ARGS__) + + +#define DO_830(MACRO, ...) \ +MACRO(830, __VA_ARGS__) \ +DO_829(MACRO, __VA_ARGS__) + + +#define DO_831(MACRO, ...) \ +MACRO(831, __VA_ARGS__) \ +DO_830(MACRO, __VA_ARGS__) + + +#define DO_832(MACRO, ...) \ +MACRO(832, __VA_ARGS__) \ +DO_831(MACRO, __VA_ARGS__) + + +#define DO_833(MACRO, ...) \ +MACRO(833, __VA_ARGS__) \ +DO_832(MACRO, __VA_ARGS__) + + +#define DO_834(MACRO, ...) \ +MACRO(834, __VA_ARGS__) \ +DO_833(MACRO, __VA_ARGS__) + + +#define DO_835(MACRO, ...) \ +MACRO(835, __VA_ARGS__) \ +DO_834(MACRO, __VA_ARGS__) + + +#define DO_836(MACRO, ...) \ +MACRO(836, __VA_ARGS__) \ +DO_835(MACRO, __VA_ARGS__) + + +#define DO_837(MACRO, ...) \ +MACRO(837, __VA_ARGS__) \ +DO_836(MACRO, __VA_ARGS__) + + +#define DO_838(MACRO, ...) \ +MACRO(838, __VA_ARGS__) \ +DO_837(MACRO, __VA_ARGS__) + + +#define DO_839(MACRO, ...) \ +MACRO(839, __VA_ARGS__) \ +DO_838(MACRO, __VA_ARGS__) + + +#define DO_840(MACRO, ...) \ +MACRO(840, __VA_ARGS__) \ +DO_839(MACRO, __VA_ARGS__) + + +#define DO_841(MACRO, ...) \ +MACRO(841, __VA_ARGS__) \ +DO_840(MACRO, __VA_ARGS__) + + +#define DO_842(MACRO, ...) \ +MACRO(842, __VA_ARGS__) \ +DO_841(MACRO, __VA_ARGS__) + + +#define DO_843(MACRO, ...) \ +MACRO(843, __VA_ARGS__) \ +DO_842(MACRO, __VA_ARGS__) + + +#define DO_844(MACRO, ...) \ +MACRO(844, __VA_ARGS__) \ +DO_843(MACRO, __VA_ARGS__) + + +#define DO_845(MACRO, ...) \ +MACRO(845, __VA_ARGS__) \ +DO_844(MACRO, __VA_ARGS__) + + +#define DO_846(MACRO, ...) \ +MACRO(846, __VA_ARGS__) \ +DO_845(MACRO, __VA_ARGS__) + + +#define DO_847(MACRO, ...) \ +MACRO(847, __VA_ARGS__) \ +DO_846(MACRO, __VA_ARGS__) + + +#define DO_848(MACRO, ...) \ +MACRO(848, __VA_ARGS__) \ +DO_847(MACRO, __VA_ARGS__) + + +#define DO_849(MACRO, ...) \ +MACRO(849, __VA_ARGS__) \ +DO_848(MACRO, __VA_ARGS__) + + +#define DO_850(MACRO, ...) \ +MACRO(850, __VA_ARGS__) \ +DO_849(MACRO, __VA_ARGS__) + + +#define DO_851(MACRO, ...) \ +MACRO(851, __VA_ARGS__) \ +DO_850(MACRO, __VA_ARGS__) + + +#define DO_852(MACRO, ...) \ +MACRO(852, __VA_ARGS__) \ +DO_851(MACRO, __VA_ARGS__) + + +#define DO_853(MACRO, ...) \ +MACRO(853, __VA_ARGS__) \ +DO_852(MACRO, __VA_ARGS__) + + +#define DO_854(MACRO, ...) \ +MACRO(854, __VA_ARGS__) \ +DO_853(MACRO, __VA_ARGS__) + + +#define DO_855(MACRO, ...) \ +MACRO(855, __VA_ARGS__) \ +DO_854(MACRO, __VA_ARGS__) + + +#define DO_856(MACRO, ...) \ +MACRO(856, __VA_ARGS__) \ +DO_855(MACRO, __VA_ARGS__) + + +#define DO_857(MACRO, ...) \ +MACRO(857, __VA_ARGS__) \ +DO_856(MACRO, __VA_ARGS__) + + +#define DO_858(MACRO, ...) \ +MACRO(858, __VA_ARGS__) \ +DO_857(MACRO, __VA_ARGS__) + + +#define DO_859(MACRO, ...) \ +MACRO(859, __VA_ARGS__) \ +DO_858(MACRO, __VA_ARGS__) + + +#define DO_860(MACRO, ...) \ +MACRO(860, __VA_ARGS__) \ +DO_859(MACRO, __VA_ARGS__) + + +#define DO_861(MACRO, ...) \ +MACRO(861, __VA_ARGS__) \ +DO_860(MACRO, __VA_ARGS__) + + +#define DO_862(MACRO, ...) \ +MACRO(862, __VA_ARGS__) \ +DO_861(MACRO, __VA_ARGS__) + + +#define DO_863(MACRO, ...) \ +MACRO(863, __VA_ARGS__) \ +DO_862(MACRO, __VA_ARGS__) + + +#define DO_864(MACRO, ...) \ +MACRO(864, __VA_ARGS__) \ +DO_863(MACRO, __VA_ARGS__) + + +#define DO_865(MACRO, ...) \ +MACRO(865, __VA_ARGS__) \ +DO_864(MACRO, __VA_ARGS__) + + +#define DO_866(MACRO, ...) \ +MACRO(866, __VA_ARGS__) \ +DO_865(MACRO, __VA_ARGS__) + + +#define DO_867(MACRO, ...) \ +MACRO(867, __VA_ARGS__) \ +DO_866(MACRO, __VA_ARGS__) + + +#define DO_868(MACRO, ...) \ +MACRO(868, __VA_ARGS__) \ +DO_867(MACRO, __VA_ARGS__) + + +#define DO_869(MACRO, ...) \ +MACRO(869, __VA_ARGS__) \ +DO_868(MACRO, __VA_ARGS__) + + +#define DO_870(MACRO, ...) \ +MACRO(870, __VA_ARGS__) \ +DO_869(MACRO, __VA_ARGS__) + + +#define DO_871(MACRO, ...) \ +MACRO(871, __VA_ARGS__) \ +DO_870(MACRO, __VA_ARGS__) + + +#define DO_872(MACRO, ...) \ +MACRO(872, __VA_ARGS__) \ +DO_871(MACRO, __VA_ARGS__) + + +#define DO_873(MACRO, ...) \ +MACRO(873, __VA_ARGS__) \ +DO_872(MACRO, __VA_ARGS__) + + +#define DO_874(MACRO, ...) \ +MACRO(874, __VA_ARGS__) \ +DO_873(MACRO, __VA_ARGS__) + + +#define DO_875(MACRO, ...) \ +MACRO(875, __VA_ARGS__) \ +DO_874(MACRO, __VA_ARGS__) + + +#define DO_876(MACRO, ...) \ +MACRO(876, __VA_ARGS__) \ +DO_875(MACRO, __VA_ARGS__) + + +#define DO_877(MACRO, ...) \ +MACRO(877, __VA_ARGS__) \ +DO_876(MACRO, __VA_ARGS__) + + +#define DO_878(MACRO, ...) \ +MACRO(878, __VA_ARGS__) \ +DO_877(MACRO, __VA_ARGS__) + + +#define DO_879(MACRO, ...) \ +MACRO(879, __VA_ARGS__) \ +DO_878(MACRO, __VA_ARGS__) + + +#define DO_880(MACRO, ...) \ +MACRO(880, __VA_ARGS__) \ +DO_879(MACRO, __VA_ARGS__) + + +#define DO_881(MACRO, ...) \ +MACRO(881, __VA_ARGS__) \ +DO_880(MACRO, __VA_ARGS__) + + +#define DO_882(MACRO, ...) \ +MACRO(882, __VA_ARGS__) \ +DO_881(MACRO, __VA_ARGS__) + + +#define DO_883(MACRO, ...) \ +MACRO(883, __VA_ARGS__) \ +DO_882(MACRO, __VA_ARGS__) + + +#define DO_884(MACRO, ...) \ +MACRO(884, __VA_ARGS__) \ +DO_883(MACRO, __VA_ARGS__) + + +#define DO_885(MACRO, ...) \ +MACRO(885, __VA_ARGS__) \ +DO_884(MACRO, __VA_ARGS__) + + +#define DO_886(MACRO, ...) \ +MACRO(886, __VA_ARGS__) \ +DO_885(MACRO, __VA_ARGS__) + + +#define DO_887(MACRO, ...) \ +MACRO(887, __VA_ARGS__) \ +DO_886(MACRO, __VA_ARGS__) + + +#define DO_888(MACRO, ...) \ +MACRO(888, __VA_ARGS__) \ +DO_887(MACRO, __VA_ARGS__) + + +#define DO_889(MACRO, ...) \ +MACRO(889, __VA_ARGS__) \ +DO_888(MACRO, __VA_ARGS__) + + +#define DO_890(MACRO, ...) \ +MACRO(890, __VA_ARGS__) \ +DO_889(MACRO, __VA_ARGS__) + + +#define DO_891(MACRO, ...) \ +MACRO(891, __VA_ARGS__) \ +DO_890(MACRO, __VA_ARGS__) + + +#define DO_892(MACRO, ...) \ +MACRO(892, __VA_ARGS__) \ +DO_891(MACRO, __VA_ARGS__) + + +#define DO_893(MACRO, ...) \ +MACRO(893, __VA_ARGS__) \ +DO_892(MACRO, __VA_ARGS__) + + +#define DO_894(MACRO, ...) \ +MACRO(894, __VA_ARGS__) \ +DO_893(MACRO, __VA_ARGS__) + + +#define DO_895(MACRO, ...) \ +MACRO(895, __VA_ARGS__) \ +DO_894(MACRO, __VA_ARGS__) + + +#define DO_896(MACRO, ...) \ +MACRO(896, __VA_ARGS__) \ +DO_895(MACRO, __VA_ARGS__) + + +#define DO_897(MACRO, ...) \ +MACRO(897, __VA_ARGS__) \ +DO_896(MACRO, __VA_ARGS__) + + +#define DO_898(MACRO, ...) \ +MACRO(898, __VA_ARGS__) \ +DO_897(MACRO, __VA_ARGS__) + + +#define DO_899(MACRO, ...) \ +MACRO(899, __VA_ARGS__) \ +DO_898(MACRO, __VA_ARGS__) + + +#define DO_900(MACRO, ...) \ +MACRO(900, __VA_ARGS__) \ +DO_899(MACRO, __VA_ARGS__) + + +#define DO_901(MACRO, ...) \ +MACRO(901, __VA_ARGS__) \ +DO_900(MACRO, __VA_ARGS__) + + +#define DO_902(MACRO, ...) \ +MACRO(902, __VA_ARGS__) \ +DO_901(MACRO, __VA_ARGS__) + + +#define DO_903(MACRO, ...) \ +MACRO(903, __VA_ARGS__) \ +DO_902(MACRO, __VA_ARGS__) + + +#define DO_904(MACRO, ...) \ +MACRO(904, __VA_ARGS__) \ +DO_903(MACRO, __VA_ARGS__) + + +#define DO_905(MACRO, ...) \ +MACRO(905, __VA_ARGS__) \ +DO_904(MACRO, __VA_ARGS__) + + +#define DO_906(MACRO, ...) \ +MACRO(906, __VA_ARGS__) \ +DO_905(MACRO, __VA_ARGS__) + + +#define DO_907(MACRO, ...) \ +MACRO(907, __VA_ARGS__) \ +DO_906(MACRO, __VA_ARGS__) + + +#define DO_908(MACRO, ...) \ +MACRO(908, __VA_ARGS__) \ +DO_907(MACRO, __VA_ARGS__) + + +#define DO_909(MACRO, ...) \ +MACRO(909, __VA_ARGS__) \ +DO_908(MACRO, __VA_ARGS__) + + +#define DO_910(MACRO, ...) \ +MACRO(910, __VA_ARGS__) \ +DO_909(MACRO, __VA_ARGS__) + + +#define DO_911(MACRO, ...) \ +MACRO(911, __VA_ARGS__) \ +DO_910(MACRO, __VA_ARGS__) + + +#define DO_912(MACRO, ...) \ +MACRO(912, __VA_ARGS__) \ +DO_911(MACRO, __VA_ARGS__) + + +#define DO_913(MACRO, ...) \ +MACRO(913, __VA_ARGS__) \ +DO_912(MACRO, __VA_ARGS__) + + +#define DO_914(MACRO, ...) \ +MACRO(914, __VA_ARGS__) \ +DO_913(MACRO, __VA_ARGS__) + + +#define DO_915(MACRO, ...) \ +MACRO(915, __VA_ARGS__) \ +DO_914(MACRO, __VA_ARGS__) + + +#define DO_916(MACRO, ...) \ +MACRO(916, __VA_ARGS__) \ +DO_915(MACRO, __VA_ARGS__) + + +#define DO_917(MACRO, ...) \ +MACRO(917, __VA_ARGS__) \ +DO_916(MACRO, __VA_ARGS__) + + +#define DO_918(MACRO, ...) \ +MACRO(918, __VA_ARGS__) \ +DO_917(MACRO, __VA_ARGS__) + + +#define DO_919(MACRO, ...) \ +MACRO(919, __VA_ARGS__) \ +DO_918(MACRO, __VA_ARGS__) + + +#define DO_920(MACRO, ...) \ +MACRO(920, __VA_ARGS__) \ +DO_919(MACRO, __VA_ARGS__) + + +#define DO_921(MACRO, ...) \ +MACRO(921, __VA_ARGS__) \ +DO_920(MACRO, __VA_ARGS__) + + +#define DO_922(MACRO, ...) \ +MACRO(922, __VA_ARGS__) \ +DO_921(MACRO, __VA_ARGS__) + + +#define DO_923(MACRO, ...) \ +MACRO(923, __VA_ARGS__) \ +DO_922(MACRO, __VA_ARGS__) + + +#define DO_924(MACRO, ...) \ +MACRO(924, __VA_ARGS__) \ +DO_923(MACRO, __VA_ARGS__) + + +#define DO_925(MACRO, ...) \ +MACRO(925, __VA_ARGS__) \ +DO_924(MACRO, __VA_ARGS__) + + +#define DO_926(MACRO, ...) \ +MACRO(926, __VA_ARGS__) \ +DO_925(MACRO, __VA_ARGS__) + + +#define DO_927(MACRO, ...) \ +MACRO(927, __VA_ARGS__) \ +DO_926(MACRO, __VA_ARGS__) + + +#define DO_928(MACRO, ...) \ +MACRO(928, __VA_ARGS__) \ +DO_927(MACRO, __VA_ARGS__) + + +#define DO_929(MACRO, ...) \ +MACRO(929, __VA_ARGS__) \ +DO_928(MACRO, __VA_ARGS__) + + +#define DO_930(MACRO, ...) \ +MACRO(930, __VA_ARGS__) \ +DO_929(MACRO, __VA_ARGS__) + + +#define DO_931(MACRO, ...) \ +MACRO(931, __VA_ARGS__) \ +DO_930(MACRO, __VA_ARGS__) + + +#define DO_932(MACRO, ...) \ +MACRO(932, __VA_ARGS__) \ +DO_931(MACRO, __VA_ARGS__) + + +#define DO_933(MACRO, ...) \ +MACRO(933, __VA_ARGS__) \ +DO_932(MACRO, __VA_ARGS__) + + +#define DO_934(MACRO, ...) \ +MACRO(934, __VA_ARGS__) \ +DO_933(MACRO, __VA_ARGS__) + + +#define DO_935(MACRO, ...) \ +MACRO(935, __VA_ARGS__) \ +DO_934(MACRO, __VA_ARGS__) + + +#define DO_936(MACRO, ...) \ +MACRO(936, __VA_ARGS__) \ +DO_935(MACRO, __VA_ARGS__) + + +#define DO_937(MACRO, ...) \ +MACRO(937, __VA_ARGS__) \ +DO_936(MACRO, __VA_ARGS__) + + +#define DO_938(MACRO, ...) \ +MACRO(938, __VA_ARGS__) \ +DO_937(MACRO, __VA_ARGS__) + + +#define DO_939(MACRO, ...) \ +MACRO(939, __VA_ARGS__) \ +DO_938(MACRO, __VA_ARGS__) + + +#define DO_940(MACRO, ...) \ +MACRO(940, __VA_ARGS__) \ +DO_939(MACRO, __VA_ARGS__) + + +#define DO_941(MACRO, ...) \ +MACRO(941, __VA_ARGS__) \ +DO_940(MACRO, __VA_ARGS__) + + +#define DO_942(MACRO, ...) \ +MACRO(942, __VA_ARGS__) \ +DO_941(MACRO, __VA_ARGS__) + + +#define DO_943(MACRO, ...) \ +MACRO(943, __VA_ARGS__) \ +DO_942(MACRO, __VA_ARGS__) + + +#define DO_944(MACRO, ...) \ +MACRO(944, __VA_ARGS__) \ +DO_943(MACRO, __VA_ARGS__) + + +#define DO_945(MACRO, ...) \ +MACRO(945, __VA_ARGS__) \ +DO_944(MACRO, __VA_ARGS__) + + +#define DO_946(MACRO, ...) \ +MACRO(946, __VA_ARGS__) \ +DO_945(MACRO, __VA_ARGS__) + + +#define DO_947(MACRO, ...) \ +MACRO(947, __VA_ARGS__) \ +DO_946(MACRO, __VA_ARGS__) + + +#define DO_948(MACRO, ...) \ +MACRO(948, __VA_ARGS__) \ +DO_947(MACRO, __VA_ARGS__) + + +#define DO_949(MACRO, ...) \ +MACRO(949, __VA_ARGS__) \ +DO_948(MACRO, __VA_ARGS__) + + +#define DO_950(MACRO, ...) \ +MACRO(950, __VA_ARGS__) \ +DO_949(MACRO, __VA_ARGS__) + + +#define DO_951(MACRO, ...) \ +MACRO(951, __VA_ARGS__) \ +DO_950(MACRO, __VA_ARGS__) + + +#define DO_952(MACRO, ...) \ +MACRO(952, __VA_ARGS__) \ +DO_951(MACRO, __VA_ARGS__) + + +#define DO_953(MACRO, ...) \ +MACRO(953, __VA_ARGS__) \ +DO_952(MACRO, __VA_ARGS__) + + +#define DO_954(MACRO, ...) \ +MACRO(954, __VA_ARGS__) \ +DO_953(MACRO, __VA_ARGS__) + + +#define DO_955(MACRO, ...) \ +MACRO(955, __VA_ARGS__) \ +DO_954(MACRO, __VA_ARGS__) + + +#define DO_956(MACRO, ...) \ +MACRO(956, __VA_ARGS__) \ +DO_955(MACRO, __VA_ARGS__) + + +#define DO_957(MACRO, ...) \ +MACRO(957, __VA_ARGS__) \ +DO_956(MACRO, __VA_ARGS__) + + +#define DO_958(MACRO, ...) \ +MACRO(958, __VA_ARGS__) \ +DO_957(MACRO, __VA_ARGS__) + + +#define DO_959(MACRO, ...) \ +MACRO(959, __VA_ARGS__) \ +DO_958(MACRO, __VA_ARGS__) + + +#define DO_960(MACRO, ...) \ +MACRO(960, __VA_ARGS__) \ +DO_959(MACRO, __VA_ARGS__) + + +#define DO_961(MACRO, ...) \ +MACRO(961, __VA_ARGS__) \ +DO_960(MACRO, __VA_ARGS__) + + +#define DO_962(MACRO, ...) \ +MACRO(962, __VA_ARGS__) \ +DO_961(MACRO, __VA_ARGS__) + + +#define DO_963(MACRO, ...) \ +MACRO(963, __VA_ARGS__) \ +DO_962(MACRO, __VA_ARGS__) + + +#define DO_964(MACRO, ...) \ +MACRO(964, __VA_ARGS__) \ +DO_963(MACRO, __VA_ARGS__) + + +#define DO_965(MACRO, ...) \ +MACRO(965, __VA_ARGS__) \ +DO_964(MACRO, __VA_ARGS__) + + +#define DO_966(MACRO, ...) \ +MACRO(966, __VA_ARGS__) \ +DO_965(MACRO, __VA_ARGS__) + + +#define DO_967(MACRO, ...) \ +MACRO(967, __VA_ARGS__) \ +DO_966(MACRO, __VA_ARGS__) + + +#define DO_968(MACRO, ...) \ +MACRO(968, __VA_ARGS__) \ +DO_967(MACRO, __VA_ARGS__) + + +#define DO_969(MACRO, ...) \ +MACRO(969, __VA_ARGS__) \ +DO_968(MACRO, __VA_ARGS__) + + +#define DO_970(MACRO, ...) \ +MACRO(970, __VA_ARGS__) \ +DO_969(MACRO, __VA_ARGS__) + + +#define DO_971(MACRO, ...) \ +MACRO(971, __VA_ARGS__) \ +DO_970(MACRO, __VA_ARGS__) + + +#define DO_972(MACRO, ...) \ +MACRO(972, __VA_ARGS__) \ +DO_971(MACRO, __VA_ARGS__) + + +#define DO_973(MACRO, ...) \ +MACRO(973, __VA_ARGS__) \ +DO_972(MACRO, __VA_ARGS__) + + +#define DO_974(MACRO, ...) \ +MACRO(974, __VA_ARGS__) \ +DO_973(MACRO, __VA_ARGS__) + + +#define DO_975(MACRO, ...) \ +MACRO(975, __VA_ARGS__) \ +DO_974(MACRO, __VA_ARGS__) + + +#define DO_976(MACRO, ...) \ +MACRO(976, __VA_ARGS__) \ +DO_975(MACRO, __VA_ARGS__) + + +#define DO_977(MACRO, ...) \ +MACRO(977, __VA_ARGS__) \ +DO_976(MACRO, __VA_ARGS__) + + +#define DO_978(MACRO, ...) \ +MACRO(978, __VA_ARGS__) \ +DO_977(MACRO, __VA_ARGS__) + + +#define DO_979(MACRO, ...) \ +MACRO(979, __VA_ARGS__) \ +DO_978(MACRO, __VA_ARGS__) + + +#define DO_980(MACRO, ...) \ +MACRO(980, __VA_ARGS__) \ +DO_979(MACRO, __VA_ARGS__) + + +#define DO_981(MACRO, ...) \ +MACRO(981, __VA_ARGS__) \ +DO_980(MACRO, __VA_ARGS__) + + +#define DO_982(MACRO, ...) \ +MACRO(982, __VA_ARGS__) \ +DO_981(MACRO, __VA_ARGS__) + + +#define DO_983(MACRO, ...) \ +MACRO(983, __VA_ARGS__) \ +DO_982(MACRO, __VA_ARGS__) + + +#define DO_984(MACRO, ...) \ +MACRO(984, __VA_ARGS__) \ +DO_983(MACRO, __VA_ARGS__) + + +#define DO_985(MACRO, ...) \ +MACRO(985, __VA_ARGS__) \ +DO_984(MACRO, __VA_ARGS__) + + +#define DO_986(MACRO, ...) \ +MACRO(986, __VA_ARGS__) \ +DO_985(MACRO, __VA_ARGS__) + + +#define DO_987(MACRO, ...) \ +MACRO(987, __VA_ARGS__) \ +DO_986(MACRO, __VA_ARGS__) + + +#define DO_988(MACRO, ...) \ +MACRO(988, __VA_ARGS__) \ +DO_987(MACRO, __VA_ARGS__) + + +#define DO_989(MACRO, ...) \ +MACRO(989, __VA_ARGS__) \ +DO_988(MACRO, __VA_ARGS__) + + +#define DO_990(MACRO, ...) \ +MACRO(990, __VA_ARGS__) \ +DO_989(MACRO, __VA_ARGS__) + + +#define DO_991(MACRO, ...) \ +MACRO(991, __VA_ARGS__) \ +DO_990(MACRO, __VA_ARGS__) + + +#define DO_992(MACRO, ...) \ +MACRO(992, __VA_ARGS__) \ +DO_991(MACRO, __VA_ARGS__) + + +#define DO_993(MACRO, ...) \ +MACRO(993, __VA_ARGS__) \ +DO_992(MACRO, __VA_ARGS__) + + +#define DO_994(MACRO, ...) \ +MACRO(994, __VA_ARGS__) \ +DO_993(MACRO, __VA_ARGS__) + + +#define DO_995(MACRO, ...) \ +MACRO(995, __VA_ARGS__) \ +DO_994(MACRO, __VA_ARGS__) + + +#define DO_996(MACRO, ...) \ +MACRO(996, __VA_ARGS__) \ +DO_995(MACRO, __VA_ARGS__) + + +#define DO_997(MACRO, ...) \ +MACRO(997, __VA_ARGS__) \ +DO_996(MACRO, __VA_ARGS__) + + +#define DO_998(MACRO, ...) \ +MACRO(998, __VA_ARGS__) \ +DO_997(MACRO, __VA_ARGS__) + + +#define DO_999(MACRO, ...) \ +MACRO(999, __VA_ARGS__) \ +DO_998(MACRO, __VA_ARGS__) + + +#define DO_1000(MACRO, ...) \ +MACRO(1000, __VA_ARGS__) \ +DO_999(MACRO, __VA_ARGS__) + + +#define DO_1001(MACRO, ...) \ +MACRO(1001, __VA_ARGS__) \ +DO_1000(MACRO, __VA_ARGS__) + + +#define DO_1002(MACRO, ...) \ +MACRO(1002, __VA_ARGS__) \ +DO_1001(MACRO, __VA_ARGS__) + + +#define DO_1003(MACRO, ...) \ +MACRO(1003, __VA_ARGS__) \ +DO_1002(MACRO, __VA_ARGS__) + + +#define DO_1004(MACRO, ...) \ +MACRO(1004, __VA_ARGS__) \ +DO_1003(MACRO, __VA_ARGS__) + + +#define DO_1005(MACRO, ...) \ +MACRO(1005, __VA_ARGS__) \ +DO_1004(MACRO, __VA_ARGS__) + + +#define DO_1006(MACRO, ...) \ +MACRO(1006, __VA_ARGS__) \ +DO_1005(MACRO, __VA_ARGS__) + + +#define DO_1007(MACRO, ...) \ +MACRO(1007, __VA_ARGS__) \ +DO_1006(MACRO, __VA_ARGS__) + + +#define DO_1008(MACRO, ...) \ +MACRO(1008, __VA_ARGS__) \ +DO_1007(MACRO, __VA_ARGS__) + + +#define DO_1009(MACRO, ...) \ +MACRO(1009, __VA_ARGS__) \ +DO_1008(MACRO, __VA_ARGS__) + + +#define DO_1010(MACRO, ...) \ +MACRO(1010, __VA_ARGS__) \ +DO_1009(MACRO, __VA_ARGS__) + + +#define DO_1011(MACRO, ...) \ +MACRO(1011, __VA_ARGS__) \ +DO_1010(MACRO, __VA_ARGS__) + + +#define DO_1012(MACRO, ...) \ +MACRO(1012, __VA_ARGS__) \ +DO_1011(MACRO, __VA_ARGS__) + + +#define DO_1013(MACRO, ...) \ +MACRO(1013, __VA_ARGS__) \ +DO_1012(MACRO, __VA_ARGS__) + + +#define DO_1014(MACRO, ...) \ +MACRO(1014, __VA_ARGS__) \ +DO_1013(MACRO, __VA_ARGS__) + + +#define DO_1015(MACRO, ...) \ +MACRO(1015, __VA_ARGS__) \ +DO_1014(MACRO, __VA_ARGS__) + + +#define DO_1016(MACRO, ...) \ +MACRO(1016, __VA_ARGS__) \ +DO_1015(MACRO, __VA_ARGS__) + + +#define DO_1017(MACRO, ...) \ +MACRO(1017, __VA_ARGS__) \ +DO_1016(MACRO, __VA_ARGS__) + + +#define DO_1018(MACRO, ...) \ +MACRO(1018, __VA_ARGS__) \ +DO_1017(MACRO, __VA_ARGS__) + + +#define DO_1019(MACRO, ...) \ +MACRO(1019, __VA_ARGS__) \ +DO_1018(MACRO, __VA_ARGS__) + + +#define DO_1020(MACRO, ...) \ +MACRO(1020, __VA_ARGS__) \ +DO_1019(MACRO, __VA_ARGS__) + + +#define DO_1021(MACRO, ...) \ +MACRO(1021, __VA_ARGS__) \ +DO_1020(MACRO, __VA_ARGS__) + + +#define DO_1022(MACRO, ...) \ +MACRO(1022, __VA_ARGS__) \ +DO_1021(MACRO, __VA_ARGS__) + + +#define DO_1023(MACRO, ...) \ +MACRO(1023, __VA_ARGS__) \ +DO_1022(MACRO, __VA_ARGS__) + + +#define DO_1024(MACRO, ...) \ +MACRO(1024, __VA_ARGS__) \ +DO_1023(MACRO, __VA_ARGS__) + + + +#define DO(TIMES, MACRO, ...) C2(DO_, TIMES)(MACRO, __VA_ARGS__) + + +/* we need some sort of macro that does: +IF(0, "true", "false") => "false" +IF(1, "true", "false") => "true" +IF(X, "true", "false") => "true" +*/ + +#define INTERNALIF(x) INTERNALIF##x +#define INTERNALIF0 + +#define ISZERO(x) COUNT_ARG(INTERNALIF(x)) + +#define IF(condition, trueBranch, falseBranch) C2(IF,ISZERO(condition))(trueBranch, falseBranch) +#define IF0(trueBranch, falseBranch) falseBranch +#define IF1(trueBranch, falseBranch) trueBranch + + + +#define DEFINE_ENUMERATION_CONSTANT(x) x, +/*DEFINE_ENUM goes to header*/ +#define DEFINE_ENUM(enumName, ...) typedef enum C2(enumName, _TAG) { FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; \ + extern const char* C2(enumName,Strings)(enumName value); \ + extern int C2(enumName, _FromString)(const char* enumAsString, enumName* destination); + + +#define DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING(x) C2(L, TOSTRING(x)) , +#define DEFINE_ENUMERATION_CONSTANT_AS_STRING(x) TOSTRING(x) , +/*DEFINE_ENUM_STRINGS goes to .c*/ +#define DEFINE_ENUM_STRINGS(enumName, ...) const char* C2(enumName, StringStorage)[COUNT_ARG(__VA_ARGS__)] = {FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_STRING, __VA_ARGS__)}; \ +const char* C2(enumName,Strings)(enumName value) \ +{ \ + if((int)value<0 || (int)value>=COUNT_ARG(__VA_ARGS__)) \ + { \ + /*this is an error case*/ \ + return NULL; \ + } \ + else \ + { \ + return C2(enumName, StringStorage)[value]; \ + } \ +} \ +int C2(enumName, _FromString)(const char* enumAsString, enumName* destination) \ +{ \ + if( \ + (enumAsString==NULL) || (destination==NULL) \ + ) \ + { \ + return __FAILURE__; \ + } \ + else \ + { \ + size_t i; \ + for(i=0;i<COUNT_ARG(__VA_ARGS__);i++) \ + { \ + if(strcmp(enumAsString, C2(enumName, StringStorage)[i])==0) \ + { \ + *destination = (enumName)i; \ + return 0; \ + } \ + } \ + return __FAILURE__; \ + } \ +} \ + +#define DEFINE_LOCAL_ENUM(enumName, ...) typedef enum C2(enumName, _TAG) { FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; \ +static const char* C2(enumName, StringStorage)[COUNT_ARG(__VA_ARGS__)] = {FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_STRING, __VA_ARGS__)}; \ +static const char* C2(enumName,Strings)(enumName value) \ +{ \ + if((int)value<0 || (int)value>=COUNT_ARG(__VA_ARGS__)) \ + { \ + /*this is an error case*/ \ + return NULL; \ + } \ + else \ + { \ + return C2(enumName, StringStorage)[value]; \ + } \ +} + + +#define ENUM_TO_STRING(enumName, enumValue) C2(enumName, Strings)(enumValue) +#define STRING_TO_ENUM(stringValue, enumName, addressOfEnumVariable) C2(enumName, _FromString)(stringValue, addressOfEnumVariable) + +#define DEFINE_MICROMOCK_ENUM_TO_STRING(type, ...) MICROMOCK_ENUM_TO_STRING(type, FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING, __VA_ARGS__)); + +#define EMPTY() +#define MACRO_UTILS_DELAY(id) id EMPTY LPAREN ) + +#endif /*MACRO_UTILS_H*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umock_c.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCK_C_H +#define UMOCK_C_H + +#ifdef __cplusplus +extern "C" { +#include <cstdlib> +#else +#include <stdlib.h> +#endif +#include "macro_utils.h" +#include "umockcallrecorder.h" + +/* Define UMOCK_STATIC to static to make mocks private to compilation unit */ +#ifndef UMOCK_STATIC +#define UMOCK_STATIC +#endif + +#define UMOCK_C_ERROR_CODE_VALUES \ + UMOCK_C_ARG_INDEX_OUT_OF_RANGE, \ + UMOCK_C_MALLOC_ERROR, \ + UMOCK_C_INVALID_ARGUMENT_BUFFER, \ + UMOCK_C_COMPARE_CALL_ERROR, \ + UMOCK_C_RESET_CALLS_ERROR, \ + UMOCK_C_CAPTURE_RETURN_ALREADY_USED, \ + UMOCK_C_NULL_ARGUMENT, \ + UMOCK_C_INVALID_PAIRED_CALLS, \ + UMOCK_C_REGISTER_TYPE_FAILED, \ + UMOCK_C_ERROR + +DEFINE_ENUM(UMOCK_C_ERROR_CODE, UMOCK_C_ERROR_CODE_VALUES) + +/* This is the type for the error callback by which umock_c shall indicate errors to the user */ +typedef void(*ON_UMOCK_C_ERROR)(UMOCK_C_ERROR_CODE error_code); + +#define IGNORED_PTR_ARG (NULL) +#define IGNORED_NUM_ARG (0) + +#define REGISTER_GLOBAL_MOCK_HOOK(mock_function, mock_hook_function) \ + C2(set_global_mock_hook_,mock_function)(mock_hook_function); + +#define REGISTER_GLOBAL_MOCK_RETURN(mock_function, return_value) \ + C2(set_global_mock_return_,mock_function)(return_value); + +#define REGISTER_GLOBAL_MOCK_FAIL_RETURN(mock_function, fail_return_value) \ + C2(set_global_mock_fail_return_,mock_function)(fail_return_value); + +#define REGISTER_GLOBAL_MOCK_RETURNS(mock_function, return_value, fail_return_value) \ + C2(set_global_mock_returns_,mock_function)(return_value, fail_return_value); + +/* Codes_SRS_UMOCK_C_LIB_01_013: [STRICT_EXPECTED_CALL shall record that a certain call is expected.] */ +#define STRICT_EXPECTED_CALL(call) \ + C2(get_auto_ignore_args_function_, call)(C2(umock_c_strict_expected_,call), #call) + +#define EXPECTED_CALL(call) \ + C2(umock_c_expected_,call) + +#define DECLARE_UMOCK_POINTER_TYPE_FOR_TYPE(value_type, alias) \ + char* C3(stringify_func_,alias,ptr)(const value_type** value) \ + { \ + char temp_buffer[32]; \ + char* result; \ + size_t length = sprintf(temp_buffer, "%p", (void*)*value); \ + if (length < 0) \ + { \ + result = NULL; \ + } \ + else \ + { \ + result = (char*)malloc(length + 1); \ + if (result != NULL) \ + { \ + (void)memcpy(result, temp_buffer, length + 1); \ + } \ + } \ + return result; \ + } \ + int C3(are_equal_func_,alias,ptr)(const value_type** left, const value_type** right) \ + { \ + return *left == *right; \ + } \ + int C3(copy_func_,alias,ptr)(value_type** destination, const value_type** source) \ + { \ + *destination = (value_type*)*source; \ + return 0; \ + } \ + void C3(free_func_,alias,ptr)(value_type** value) \ + { \ + (void)value; \ + } \ + +extern void umock_c_indicate_error(UMOCK_C_ERROR_CODE error_code); +extern int umock_c_init(ON_UMOCK_C_ERROR on_umock_c_error); +extern void umock_c_deinit(void); +extern void umock_c_reset_all_calls(void); +extern const char* umock_c_get_actual_calls(void); +extern const char* umock_c_get_expected_calls(void); +extern UMOCKCALLRECORDER_HANDLE umock_c_get_call_recorder(void); +extern int umock_c_set_call_recorder(UMOCKCALLRECORDER_HANDLE umockc_call_recorder); + +/* Codes_SRS_UMOCK_C_LIB_01_065: [REGISTER_UMOCK_VALUE_TYPE shall register the type identified by value_type to be usable by umock_c for argument and return types and instruct umock_c which functions to use for getting the stringify, are_equal, copy and free.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_197: [ If REGISTER_UMOCK_VALUE_TYPE fails, the on_error callback shall be called with UMOCK_C_REGISTER_TYPE_FAILED. ]*/ +#define REGISTER_UMOCK_VALUE_TYPE_ALL(value_type, stringify_func, are_equal_func, copy_func, free_func) \ +{ \ + char* stringify_func(const value_type* value); \ + int are_equal_func(const value_type* left, const value_type* right); \ + int copy_func(value_type* destination, const value_type* source); \ + void free_func(value_type* value); \ + if (umocktypes_register_type(TOSTRING(value_type), (UMOCKTYPE_STRINGIFY_FUNC)stringify_func, (UMOCKTYPE_ARE_EQUAL_FUNC)are_equal_func, (UMOCKTYPE_COPY_FUNC)copy_func, (UMOCKTYPE_FREE_FUNC)free_func) != 0) \ + { \ + umock_c_indicate_error(UMOCK_C_REGISTER_TYPE_FAILED); \ + } \ +} + +/* Codes_SRS_UMOCK_C_LIB_01_066: [If only the value_type is specified in the macro invocation then the stringify, are_equal, copy and free function names shall be automatically derived from the type as: umockvalue_stringify_value_type, umockvalue_are_equal_value_type, umockvalue_copy_value_type, umockvalue_free_value_type.]*/ +#define REGISTER_UMOCK_VALUE_TYPE_ONLY_TYPE(value_type) \ + REGISTER_UMOCK_VALUE_TYPE_ALL (value_type, C2(umock_stringify_,value_type), C2(umock_are_equal_,value_type), C2(umock_copy_,value_type), C2(umock_free_,value_type)) + +#if _MSC_VER +#define REGISTER_UMOCK_VALUE_TYPE(...) \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), REGISTER_UMOCK_VALUE_TYPE_ALL, REGISTER_UMOCK_VALUE_TYPE_ONLY_TYPE) LPAREN __VA_ARGS__) +#else +#define REGISTER_UMOCK_VALUE_TYPE(...) \ + IF(DIV2(COUNT_ARG(__VA_ARGS__)), REGISTER_UMOCK_VALUE_TYPE_ALL, REGISTER_UMOCK_VALUE_TYPE_ONLY_TYPE) (__VA_ARGS__) +#endif + +/* Codes_SRS_UMOCK_C_LIB_01_149: [ REGISTER_UMOCK_ALIAS_TYPE registers a new alias type for another type. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_198: [ If REGISTER_UMOCK_ALIAS_TYPE fails, the on_error callback shall be called with UMOCK_C_REGISTER_TYPE_FAILED. ]*/ +#define REGISTER_UMOCK_ALIAS_TYPE(value_type, is_value_type) \ + if (umocktypes_register_alias_type(TOSTRING(value_type), TOSTRING(is_value_type)) != 0) \ + { \ + umock_c_indicate_error(UMOCK_C_REGISTER_TYPE_FAILED); \ + } \ + +#define UMOCK_TYPE(value_type) \ + (const char*)(const void*)(const value_type*)(const void*)TOSTRING(value_type) + +#include "umock_c_internal.h" +#include "umock_c_prod.h" + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCK_C_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umock_c_internal.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1310 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCK_C_INTERNAL_H +#define UMOCK_C_INTERNAL_H + +#ifdef __cplusplus +#include <cstdlib> +extern "C" { +#else +#include <stdlib.h> +#endif + +#include <stdio.h> + +#include "macro_utils.h" +#include "umocktypes.h" +#include "umockcall.h" +#include "umockcallrecorder.h" +#include "umock_c.h" +#include "umock_log.h" +#include "umockalloc.h" +#include "umockcallpairs.h" +#include "umockstring.h" +#include "umockautoignoreargs.h" + +extern void umock_c_indicate_error(UMOCK_C_ERROR_CODE error_code); +extern UMOCKCALL_HANDLE umock_c_get_last_expected_call(void); +extern int umock_c_add_expected_call(UMOCKCALL_HANDLE mock_call); +extern int umock_c_add_actual_call(UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call); + +typedef struct ARG_BUFFER_TAG +{ + void* bytes; + size_t length; +} ARG_BUFFER; + +typedef int(*TRACK_CREATE_FUNC_TYPE)(PAIRED_HANDLES* paired_handles, const void* handle, const char* handle_type, size_t handle_type_size); +typedef int(*TRACK_DESTROY_FUNC_TYPE)(PAIRED_HANDLES* paired_handles, const void* handle); + +#define MOCK_ENABLED 0 +#define MOCK_DISABLED 1 + +#define ARG_IS_IGNORED -1 +#define ARG_IS_NOT_IGNORED 0 + +#define FAIL_RETURN_VALUE_SET -1 +#define FAIL_RETURN_VALUE_NOT_SET 0 + +#define RETURN_VALUE_SET -1 +#define RETURN_VALUE_NOT_SET 0 + +#define COUNT_OF(A) (sizeof(A) / sizeof((A)[0])) + +#define GET_USED_ARGUMENT_TYPE(mock_call, arg_name, arg_type) \ + mock_call->C2(override_argument_type_,arg_name) != NULL ? mock_call->C2(override_argument_type_,arg_name) : #arg_type + +#define COPY_ARG_TO_MOCK_STRUCT(arg_type, arg_name) umocktypes_copy(#arg_type, (void*)&mock_call_data->arg_name, (void*)&arg_name); +#define DECLARE_MOCK_CALL_STRUCT_STACK(arg_type, arg_name) arg_type arg_name; +#define MARK_ARG_AS_NOT_IGNORED(arg_type, arg_name) mock_call_data->C2(is_ignored_, arg_name) = ARG_IS_NOT_IGNORED; +#define CLEAR_VALIDATE_ARG_VALUE(arg_type, arg_name) mock_call_data->C2(validate_arg_value_pointer_, arg_name) = NULL; +#define CLEAR_OVERRIDE_ARGUMENT_TYPE(arg_type, arg_name) mock_call_data->C2(override_argument_type_, arg_name) = NULL; +#define MARK_ARG_AS_IGNORED(arg_type, arg_name) mock_call_data->C2(is_ignored_, arg_name) = ARG_IS_IGNORED; +#define CLEAR_OUT_ARG_BUFFERS(count, arg_type, arg_name) \ + C2(mock_call_data->out_arg_buffer_,arg_name).bytes = NULL; \ + mock_call_data->out_arg_buffers[COUNT_OF(mock_call_data->out_arg_buffers) - DIV2(count)] = &C2(mock_call_data->out_arg_buffer_,arg_name); +#define CLEAR_VALIDATE_ARG_BUFFERS(count, arg_type, arg_name) mock_call_data->validate_arg_buffers[COUNT_OF(mock_call_data->validate_arg_buffers) - DIV2(count)].bytes = NULL; +#define FREE_ARG_VALUE(count, arg_type, arg_name) umocktypes_free(GET_USED_ARGUMENT_TYPE(typed_mock_call_data, arg_name, arg_type), (void*)&typed_mock_call_data->arg_name); +#define FREE_OUT_ARG_BUFFERS(count, arg_type, arg_name) umockalloc_free(typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->bytes); +#define FREE_VALIDATE_ARG_BUFFERS(count, arg_type, arg_name) umockalloc_free(typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].bytes); +#define FREE_OVERRIDE_ARGUMENT_TYPE(count, arg_type, arg_name) umockalloc_free(typed_mock_call_data->C2(override_argument_type_, arg_name)); + +#define COPY_IGNORE_ARG(arg_type, arg_name) \ + result->C2(is_ignored_, arg_name) = typed_mock_call_data->C2(is_ignored_, arg_name); + +#define COPY_VALIDATE_ARG_VALUE(arg_type, arg_name) \ + result->C2(validate_arg_value_pointer_, arg_name) = typed_mock_call_data->C2(validate_arg_value_pointer_, arg_name); + +#define COPY_OVERRIDE_ARGUMENT_TYPE(arg_type, arg_name) \ + result->C2(override_argument_type_, arg_name) = (typed_mock_call_data->C2(override_argument_type_, arg_name) == NULL) ? NULL : umockstring_clone(typed_mock_call_data->C2(override_argument_type_, arg_name)); + +#define COPY_ARG_VALUE(arg_type, arg_name) umocktypes_copy(GET_USED_ARGUMENT_TYPE(typed_mock_call_data, arg_name, arg_type), (void*)&result->arg_name, (void*)&typed_mock_call_data->arg_name); +#define COPY_OUT_ARG_BUFFERS(count, arg_type, arg_name) \ + result->out_arg_buffers[COUNT_OF(result->out_arg_buffers) - DIV2(count)] = &result->C2(out_arg_buffer_,arg_name); \ + result->out_arg_buffers[COUNT_OF(result->out_arg_buffers) - DIV2(count)]->length = typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->length; \ + if (typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->bytes != NULL) \ + { \ + result->out_arg_buffers[COUNT_OF(result->out_arg_buffers) - DIV2(count)]->bytes = umockalloc_malloc(typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->length); \ + (void)memcpy(result->out_arg_buffers[COUNT_OF(result->out_arg_buffers) - DIV2(count)]->bytes, typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->bytes, typed_mock_call_data->out_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)]->length); \ + } \ + else \ + { \ + result->out_arg_buffers[COUNT_OF(result->out_arg_buffers) - DIV2(count)]->bytes = NULL; \ + } + +#define COPY_VALIDATE_ARG_BUFFERS(count, arg_type, arg_name) \ + result->validate_arg_buffers[COUNT_OF(result->validate_arg_buffers) - DIV2(count)].length = typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].length; \ + if (typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].bytes != NULL) \ + { \ + result->validate_arg_buffers[COUNT_OF(result->validate_arg_buffers) - DIV2(count)].bytes = umockalloc_malloc(typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].length); \ + (void)memcpy(result->validate_arg_buffers[COUNT_OF(result->validate_arg_buffers) - DIV2(count)].bytes, typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].bytes, typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].length); \ + } \ + else \ + { \ + result->validate_arg_buffers[COUNT_OF(result->validate_arg_buffers) - DIV2(count)].bytes = NULL; \ + } + +#define ONLY_FIRST_ARG(arg0type, arg0name, ...) arg0name +#define FILL_ARG_IN_METADATA(arg_type, arg_name) { TOSTRING(arg_type), TOSTRING(arg_name) }, +#define ARG_IN_SIGNATURE(count, arg_type, arg_name) arg_type arg_name IFCOMMA(count) +#define ARG_NAME_ONLY_IN_CALL(count, arg_type, arg_name) arg_name IFCOMMA(count) +#define ONLY_FIRST_ARG_NAME_IN_CALL(count, arg_type, arg_name) count +#define ARG_ASSIGN_IN_ARRAY(arg_type, arg_name) arg_name_local +#define DECLARE_VALIDATE_ARG_VALUE(arg_type, arg_name) void* C2(validate_arg_value_pointer_,arg_name); +#define DECLARE_IGNORE_FLAG_FOR_ARG(arg_type, arg_name) int C2(is_ignored_,arg_name) : 1; +#define DECLARE_OVERRIDE_ARGUMENT_TYPE_FOR_ARG(arg_type, arg_name) char* C2(override_argument_type_,arg_name); +#define DECLARE_OUT_ARG_BUFFER_FOR_ARG(arg_type, arg_name) ARG_BUFFER C2(out_arg_buffer_,arg_name); +#define COPY_IGNORE_ARG_BY_NAME_TO_MODIFIER(name, arg_type, arg_name) C2(mock_call_modifier->IgnoreArgument_,arg_name) = C4(ignore_argument_func_,name,_,arg_name); +#define COPY_VALIDATE_ARG_BY_NAME_TO_MODIFIER(name, arg_type, arg_name) C2(mock_call_modifier->ValidateArgument_,arg_name) = C4(validate_argument_func_,name,_,arg_name); +#define COPY_COPY_OUT_ARGUMENT_BUFFER_BY_NAME_TO_MODIFIER(name, arg_type, arg_name) C2(mock_call_modifier->CopyOutArgumentBuffer_,arg_name) = C4(copy_out_argument_buffer_func_,name,_,arg_name); +#define COPY_VALIDATE_ARGUMENT_VALUE_BY_NAME_TO_MODIFIER(name, arg_type, arg_name) C2(mock_call_modifier->ValidateArgumentValue_,arg_name) = C4(validate_argument_value_func_,name,_,arg_name); +#define COPY_VALIDATE_ARGUMENT_VALUE_AS_TYPE_BY_NAME_TO_MODIFIER(name, arg_type, arg_name) C3(mock_call_modifier->ValidateArgumentValue_,arg_name,_AsType) = C4(validate_argument_value_as_type_func_,name,_,arg_name); +#define COPY_OUT_ARG_VALUE_FROM_MATCHED_CALL(count, arg_type, arg_name) \ + if (matched_call_data->out_arg_buffers[COUNT_OF(matched_call_data->out_arg_buffers) - DIV2(count)]->bytes != NULL) \ + { \ + (void)memcpy(*((void**)(&arg_name)), matched_call_data->out_arg_buffers[COUNT_OF(matched_call_data->out_arg_buffers) - DIV2(count)]->bytes, matched_call_data->out_arg_buffers[COUNT_OF(matched_call_data->out_arg_buffers) - DIV2(count)]->length); \ + } \ + +#define STRINGIFY_ARGS_DECLARE_RESULT_VAR(count, arg_type, arg_name) \ + char* C2(arg_name,_stringified) \ + = (C2(typed_mock_call_data->validate_arg_value_pointer_, arg_name) != NULL) ? \ + umocktypes_stringify(TOSTRING(arg_type), (void*)C2(typed_mock_call_data->validate_arg_value_pointer_, arg_name)) : \ + ((typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->out_arg_buffers) - DIV2(count)].bytes != NULL) ? \ + umockc_stringify_buffer(typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].bytes, typed_mock_call_data->validate_arg_buffers[COUNT_OF(typed_mock_call_data->validate_arg_buffers) - DIV2(count)].length) : \ + umocktypes_stringify(TOSTRING(arg_type), (void*)&typed_mock_call_data->arg_name)); + +#define STRINGIFY_ARGS_CHECK_ARG_STRINGIFY_SUCCESS(arg_type, arg_name) if (C2(arg_name,_stringified) == NULL) is_error = 1; +#define STRINGIFY_ARGS_DECLARE_ARG_STRING_LENGTH(arg_type, arg_name) size_t C2(arg_name,_stringified_length) = strlen(C2(arg_name,_stringified)); +#define STRINGIFY_ARGS_COUNT_LENGTH(arg_type, arg_name) args_string_length += C2(arg_name,_stringified_length); +#define STRINGIFY_ARGS_FREE_STRINGIFIED_ARG(arg_type, arg_name) umockalloc_free(C2(arg_name,_stringified)); +#define STRINGIFY_ARGS_COPY_ARG_STRING(arg_type, arg_name) \ + if (arg_index > 0) \ + { \ + result[current_pos] = ','; \ + current_pos++; \ + } \ + (void)memcpy(result + current_pos, C2(arg_name,_stringified), C2(arg_name,_stringified_length) + 1); \ + current_pos += C2(arg_name, _stringified_length); \ + arg_index++; + +/* Codes_SRS_UMOCK_C_LIB_01_096: [If the content of the code under test buffer and the buffer supplied to ValidateArgumentBuffer does not match then this should be treated as a mismatch in argument comparison for that argument.]*/ +#define ARE_EQUAL_FOR_ARG(count, arg_type, arg_name) \ + if (result && \ + (((typed_left->validate_arg_buffers[COUNT_OF(typed_left->validate_arg_buffers) - DIV2(count)].bytes != NULL) && (memcmp(*((void**)&typed_right->arg_name), typed_left->validate_arg_buffers[COUNT_OF(typed_left->validate_arg_buffers) - DIV2(count)].bytes, typed_left->validate_arg_buffers[COUNT_OF(typed_left->validate_arg_buffers) - DIV2(count)].length) != 0)) \ + || ((typed_right->validate_arg_buffers[COUNT_OF(typed_right->validate_arg_buffers) - DIV2(count)].bytes != NULL) && (memcmp(*((void**)&typed_left->arg_name), typed_right->validate_arg_buffers[COUNT_OF(typed_right->validate_arg_buffers) - DIV2(count)].bytes, typed_right->validate_arg_buffers[COUNT_OF(typed_right->validate_arg_buffers) - DIV2(count)].length) != 0))) \ + ) \ + { \ + result = 0; \ + } \ + if ((result == 1) && (C2(typed_left->is_ignored_, arg_name) == ARG_IS_NOT_IGNORED) \ + && (C2(typed_right->is_ignored_, arg_name) == ARG_IS_NOT_IGNORED)) \ + { \ + void* left_value; \ + void* right_value; \ + if (C2(typed_left->validate_arg_value_pointer_, arg_name) != NULL) \ + { \ + left_value = (void*)C2(typed_left->validate_arg_value_pointer_, arg_name); \ + } \ + else \ + { \ + left_value = (void*)&typed_left->arg_name; \ + } \ + if (C2(typed_right->validate_arg_value_pointer_, arg_name) != NULL) \ + { \ + right_value = (void*)C2(typed_right->validate_arg_value_pointer_, arg_name); \ + } \ + else \ + { \ + right_value = (void*)&typed_right->arg_name; \ + } \ + result = umocktypes_are_equal(GET_USED_ARGUMENT_TYPE(typed_left, arg_name, arg_type), left_value, right_value); \ + } + +#define DECLARE_MOCK_CALL_MODIFIER(name, ...) \ + C2(mock_call_modifier_,name) mock_call_modifier; \ + C2(fill_mock_call_modifier_,name)(&mock_call_modifier); + +#define DECLARE_IGNORE_ARGUMENT_FUNCTION_PROTOTYPE(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(ignore_argument_func_,name,_,arg_name)(void); + +#define DECLARE_VALIDATE_ARGUMENT_FUNCTION_PROTOTYPE(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_func_,name,_,arg_name)(void); + +#define DECLARE_COPY_OUT_ARGUMENT_BUFFER_FUNCTION_PROTOTYPE(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(copy_out_argument_buffer_func_,name,_,arg_name)(const void* bytes, size_t length); + +#define DECLARE_VALIDATE_ARGUMENT_VALUE_FUNCTION_PROTOTYPE(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_value_func_,name,_,arg_name)(arg_type* arg_value); + +#define DECLARE_VALIDATE_ARGUMENT_VALUE_AS_TYPE_FUNCTION_PROTOTYPE(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_value_as_type_func_,name,_,arg_name)(const char* type_name); + +#define IGNORE_ARGUMENT_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + C4(ignore_argument_func_type_,name,_,arg_name) C2(IgnoreArgument_,arg_name); + +#define VALIDATE_ARGUMENT_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + C4(validate_argument_func_type_,name,_,arg_name) C2(ValidateArgument_,arg_name); + +#define COPY_OUT_ARGUMENT_BUFFER_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + C4(copy_out_argument_buffer_func_type_,name,_,arg_name) C2(CopyOutArgumentBuffer_,arg_name); + +#define VALIDATE_ARGUMENT_VALUE_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + C4(validate_argument_value_func_type_,name,_,arg_name) C2(ValidateArgumentValue_,arg_name); + +#define VALIDATE_ARGUMENT_VALUE_AS_TYPE_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + C4(validate_argument_value_as_type_func_type_,name,_,arg_name) C3(ValidateArgumentValue_,arg_name,_AsType); + +#define ARG_RELATED_FUNCTIONS_IN_MODIFIERS(name, arg_type, arg_name) \ + IGNORE_ARGUMENT_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + VALIDATE_ARGUMENT_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + COPY_OUT_ARGUMENT_BUFFER_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + VALIDATE_ARGUMENT_VALUE_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + VALIDATE_ARGUMENT_VALUE_AS_TYPE_FUNCTION_IN_MODIFIERS(name, arg_type, arg_name) \ + +#define DECLARE_IGNORE_ARGUMENT_FUNCTION_TYPE(name, arg_type, arg_name) \ + typedef struct C2(_mock_call_modifier_,name) (*C4(ignore_argument_func_type_,name,_,arg_name))(void); + +#define DECLARE_VALIDATE_ARGUMENT_FUNCTION_TYPE(name, arg_type, arg_name) \ + typedef struct C2(_mock_call_modifier_,name) (*C4(validate_argument_func_type_,name,_,arg_name))(void); + +#define DECLARE_COPY_OUT_ARGUMENT_BUFFER_FUNCTION_TYPE(name, arg_type, arg_name) \ + typedef struct C2(_mock_call_modifier_,name) (*C4(copy_out_argument_buffer_func_type_,name,_,arg_name))(const void* bytes, size_t length); + +#define DECLARE_VALIDATE_ARGUMENT_VALUE_FUNCTION_TYPE(name, arg_type, arg_name) \ + typedef struct C2(_mock_call_modifier_,name) (*C4(validate_argument_value_func_type_,name,_,arg_name))(arg_type* arg_value); + +#define DECLARE_VALIDATE_ARGUMENT_AS_TYPE_VALUE_FUNCTION_TYPE(name, arg_type, arg_name) \ + typedef struct C2(_mock_call_modifier_,name) (*C4(validate_argument_value_as_type_func_type_,name,_,arg_name))(const char* type_name); + +#define DECLARE_ARG_RELATED_FUNCTIONS(name, arg_type, arg_name) \ + DECLARE_IGNORE_ARGUMENT_FUNCTION_TYPE(name, arg_type, arg_name) \ + DECLARE_VALIDATE_ARGUMENT_FUNCTION_TYPE(name, arg_type, arg_name) \ + DECLARE_COPY_OUT_ARGUMENT_BUFFER_FUNCTION_TYPE(name, arg_type, arg_name) \ + DECLARE_VALIDATE_ARGUMENT_VALUE_FUNCTION_TYPE(name, arg_type, arg_name) \ + DECLARE_VALIDATE_ARGUMENT_AS_TYPE_VALUE_FUNCTION_TYPE(name, arg_type, arg_name) + +#define IGNORE_ARGUMENT_FUNCTION_IN_ARRAY(name, arg_type, arg_name) \ + &C4(ignore_argument_func_,name,_,arg_name), + +#define VALIDATE_ARGUMENT_FUNCTION_IN_ARRAY(name, arg_type, arg_name) \ + &C4(validate_argument_func_,name,_,arg_name), + +/* These 2 macros are used to check if a type is "void" or not */ +#define TEST_void 0 +#define IS_NOT_VOID(x) \ + IF(C2(TEST_,x), 1, 0) + +/* Codes_SRS_UMOCK_C_LIB_01_076: [The IgnoreAllArguments call modifier shall record that for that specific call all arguments will be ignored for that specific call.] */ +#define IMPLEMENT_IGNORE_ALL_ARGUMENTS_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(ignore_all_arguments_func_,name)(void) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + IF(COUNT_ARG(__VA_ARGS__), FOR_EACH_2(MARK_ARG_AS_IGNORED, __VA_ARGS__),) \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_077: [The ValidateAllArguments call modifier shall record that for that specific call all arguments will be validated.] */ +#define IMPLEMENT_VALIDATE_ALL_ARGUMENTS_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(validate_all_arguments_func_,name)(void) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateAllArguments called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + IF(COUNT_ARG(__VA_ARGS__), FOR_EACH_2(MARK_ARG_AS_NOT_IGNORED, __VA_ARGS__), ) \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_078: [The IgnoreArgument_{arg_name} call modifier shall record that the argument identified by arg_name will be ignored for that specific call.] */ +#define IMPLEMENT_IGNORE_ARGUMENT_BY_NAME_FUNCTION(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(ignore_argument_func_,name,_,arg_name)(void) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("IgnoreArgument_%s called without having an expected call.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + C2(mock_call_data->is_ignored_,arg_name) = ARG_IS_IGNORED; \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_079: [The ValidateArgument_{arg_name} call modifier shall record that the argument identified by arg_name will be validated for that specific call.]*/ +#define IMPLEMENT_VALIDATE_ARGUMENT_BY_NAME_FUNCTION(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_func_,name,_,arg_name)(void) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgument_%s called without having an expected call.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + C2(mock_call_data->is_ignored_,arg_name) = ARG_IS_NOT_IGNORED; \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_080: [The IgnoreArgument call modifier shall record that the indexth argument will be ignored for that specific call.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_081: [If the index is out of range umock_c shall raise an error with the code UMOCK_C_ARG_INDEX_OUT_OF_RANGE.] */ +#define IMPLEMENT_IGNORE_ARGUMENT_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(ignore_argument_func_,name)(size_t arg_index) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("IgnoreArgument called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + IF(COUNT_ARG(__VA_ARGS__), \ + if ((arg_index < 1) || (arg_index > (sizeof(C2(ignore_one_argument_array_,name)) / sizeof(C2(ignore_one_argument_array_,name)[0])))) \ + { \ + UMOCK_LOG("Bad argument index in call to IgnoreArgument %lu.", arg_index); \ + umock_c_indicate_error(UMOCK_C_ARG_INDEX_OUT_OF_RANGE); \ + } \ + else \ + { \ + C2(ignore_one_argument_array_,name)[arg_index - 1](); \ + }, \ + ) \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_082: [The ValidateArgument call modifier shall record that the indexth argument will be validated for that specific call.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_083: [If the index is out of range umock_c shall raise an error with the code UMOCK_C_ARG_INDEX_OUT_OF_RANGE.]*/ +#define IMPLEMENT_VALIDATE_ARGUMENT_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(validate_argument_func_,name)(size_t arg_index) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgument called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + IF(COUNT_ARG(__VA_ARGS__), \ + if ((arg_index < 1) || (arg_index > (sizeof(C2(validate_one_argument_array_,name)) / sizeof(C2(validate_one_argument_array_,name)[0])))) \ + { \ + UMOCK_LOG("Bad argument index in call to ValidateArgument %lu.", arg_index); \ + umock_c_indicate_error(UMOCK_C_ARG_INDEX_OUT_OF_RANGE); \ + } \ + else \ + { \ + C2(validate_one_argument_array_,name)[arg_index - 1](); \ + }, \ + ) \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_084: [The SetReturn call modifier shall record that when an actual call is matched with the specific expected call, it shall return the result value to the code under test.] */ +#define IMPLEMENT_SET_RETURN_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(set_return_func_,name)(return_type return_value) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("SetReturn called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + mock_call_data->return_value_set = RETURN_VALUE_SET; \ + if (umocktypes_copy(#return_type, (void*)&mock_call_data->return_value, (void*)&return_value) != 0) \ + { \ + UMOCK_LOG("Could not copy return value of type %s.", TOSTRING(return_type)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + } \ + return mock_call_modifier; \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_085: [The SetFailReturn call modifier shall record a fail return value.]*/ +#define IMPLEMENT_SET_FAIL_RETURN_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(set_fail_return_func_,name)(return_type return_value) \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("SetFailReturn called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + mock_call_data->fail_return_value_set = FAIL_RETURN_VALUE_SET; \ + if (umocktypes_copy(#return_type, (void*)&mock_call_data->fail_return_value, (void*)&return_value) != 0) \ + { \ + UMOCK_LOG("Could not copy fail return value of type %s.", TOSTRING(return_type)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + } \ + return mock_call_modifier; \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_116: [ The argument targetted by CopyOutArgument shall also be marked as ignored. ] */ +/* Codes_SRS_UMOCK_C_LIB_01_088: [The memory shall be copied.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_091: [If the index is out of range umock_c shall raise an error with the code UMOCK_C_ARG_INDEX_OUT_OF_RANGE.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_117: [ If any memory allocation error occurs, umock_c shall raise an error with the code UMOCK_C_MALLOC_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_118: [ If any other error occurs, umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_092: [If bytes is NULL or length is 0, umock_c shall raise an error with the code UMOCK_C_INVALID_ARGUMENT_BUFFER.] */ +/* Codes_SRS_UMOCK_C_LIB_01_089: [The buffers for previous CopyOutArgumentBuffer calls shall be freed.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_133: [ If several calls to CopyOutArgumentBuffer are made, only the last buffer shall be kept. ]*/ +#define IMPLEMENT_COPY_OUT_ARGUMENT_BUFFER_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_, name) C2(copy_out_argument_buffer_func_, name)(size_t index, const void* bytes, size_t length) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if ((index < 1) || (index > DIV2(COUNT_ARG(__VA_ARGS__)))) \ + { \ + UMOCK_LOG("Bad argument index in CopyOutArgumentBuffer: %lu.", index); \ + umock_c_indicate_error(UMOCK_C_ARG_INDEX_OUT_OF_RANGE); \ + } \ + else if ((bytes == NULL) || (length == 0)) \ + { \ + UMOCK_LOG("Bad arguments to CopyOutArgumentBuffer: bytes = %p, length = %lu.", bytes, length); \ + umock_c_indicate_error(UMOCK_C_INVALID_ARGUMENT_BUFFER); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("CopyOutArgumentBuffer called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + ARG_BUFFER* arg_buffer = mock_call_data->out_arg_buffers[index - 1]; \ + umockalloc_free(arg_buffer->bytes); \ + arg_buffer->bytes = umockalloc_malloc(length); \ + if (arg_buffer->bytes == NULL) \ + { \ + UMOCK_LOG("Could not allocate memory for out argument buffers."); \ + umock_c_indicate_error(UMOCK_C_MALLOC_ERROR); \ + } \ + else \ + { \ + (void)memcpy(arg_buffer->bytes, bytes, length); \ + arg_buffer->length = length; \ + mock_call_modifier.IgnoreArgument(index); \ + } \ + } \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_154: [ The CopyOutArgumentBuffer_{arg_name} call modifier shall copy the memory pointed to by bytes and being length bytes so that it is later injected as an out argument when the code under test calls the mock function. ] */ +/* Codes_SRS_UMOCK_C_LIB_01_163: [ The buffers for previous CopyOutArgumentBuffer calls shall be freed. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_156: [ If several calls to CopyOutArgumentBuffer are made, only the last buffer shall be kept. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_155: [ The memory shall be copied. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_158: [ If bytes is NULL or length is 0, umock_c shall raise an error with the code UMOCK_C_INVALID_ARGUMENT_BUFFER. ] */ +#define IMPLEMENT_COPY_OUT_ARGUMENT_BUFFER_BY_NAME_FUNCTION(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(copy_out_argument_buffer_func_,name,_,arg_name)(const void* bytes, size_t length) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if ((bytes == NULL) || (length == 0)) \ + { \ + UMOCK_LOG("Bad arguments to CopyOutArgumentBuffer: bytes = %p, length = %lu.", bytes, length); \ + umock_c_indicate_error(UMOCK_C_INVALID_ARGUMENT_BUFFER); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("CopyOutArgumentBuffer called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + ARG_BUFFER* arg_buffer = &C2(mock_call_data->out_arg_buffer_, arg_name); \ + umockalloc_free(arg_buffer->bytes); \ + arg_buffer->bytes = umockalloc_malloc(length); \ + if (arg_buffer->bytes == NULL) \ + { \ + UMOCK_LOG("Could not allocate memory for out argument buffers."); \ + umock_c_indicate_error(UMOCK_C_MALLOC_ERROR); \ + } \ + else \ + { \ + (void)memcpy(arg_buffer->bytes, bytes, length); \ + arg_buffer->length = length; \ + C2(mock_call_modifier.IgnoreArgument_, arg_name)(); \ + } \ + } \ + } \ + return mock_call_modifier; \ + } \ + +#define IMPLEMENT_COPY_OUT_ARGUMENT_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(copy_out_argument_func_,name)(size_t arg_index, void* value) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + (void)value; \ + (void)arg_index; \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_095: [The ValidateArgumentBuffer call modifier shall copy the memory pointed to by bytes and being length bytes so that it is later compared against a pointer type argument when the code under test calls the mock function.] */ +/* Codes_SRS_UMOCK_C_LIB_01_097: [ValidateArgumentBuffer shall implicitly perform an IgnoreArgument on the indexth argument.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_099: [If the index is out of range umock_c shall raise an error with the code UMOCK_C_ARG_INDEX_OUT_OF_RANGE.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_100: [If bytes is NULL or length is 0, umock_c shall raise an error with the code UMOCK_C_INVALID_ARGUMENT_BUFFER.] */ +/* Codes_SRS_UMOCK_C_LIB_01_131: [ The memory pointed by bytes shall be copied. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_132: [ If several calls to ValidateArgumentBuffer are made, only the last buffer shall be kept. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_130: [ The buffers for previous ValidateArgumentBuffer calls shall be freed. ]*/ +#define IMPLEMENT_VALIDATE_ARGUMENT_BUFFER_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(validate_argument_buffer_func_,name)(size_t index, const void* bytes, size_t length) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if ((index < 1) || (index > DIV2(COUNT_ARG(__VA_ARGS__)))) \ + { \ + UMOCK_LOG("Bad argument index in ValidateArgumentBuffer: %lu.", index); \ + umock_c_indicate_error(UMOCK_C_ARG_INDEX_OUT_OF_RANGE); \ + } \ + else if ((bytes == NULL) || (length == 0)) \ + { \ + UMOCK_LOG("Bad arguments to ValidateArgumentBuffer: bytes = %p, length = %lu.", bytes, length); \ + umock_c_indicate_error(UMOCK_C_INVALID_ARGUMENT_BUFFER); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgumentBuffer called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + umockalloc_free(mock_call_data->validate_arg_buffers[index - 1].bytes); \ + mock_call_data->validate_arg_buffers[index - 1].bytes = umockalloc_malloc(length); \ + if (mock_call_data->validate_arg_buffers[index - 1].bytes == NULL) \ + { \ + UMOCK_LOG("Could not allocate memory for validating argument buffers."); \ + umock_c_indicate_error(UMOCK_C_MALLOC_ERROR); \ + } \ + else \ + { \ + (void)memcpy(mock_call_data->validate_arg_buffers[index - 1].bytes, bytes, length); \ + mock_call_data->validate_arg_buffers[index - 1].length = length; \ + mock_call_modifier.IgnoreArgument(index); \ + } \ + } \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_101: [The IgnoreAllCalls call modifier shall record that all calls matching the expected call shall be ignored. If no matching call occurs no missing call shall be reported.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_208: [ If no matching call occurs no missing call shall be reported. ]*/ +#define IMPLEMENT_IGNORE_ALL_CALLS_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(ignore_all_calls_func_,name)(void) \ + { \ + UMOCKCALL_HANDLE last_expected_call = umock_c_get_last_expected_call(); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (last_expected_call == NULL) \ + { \ + UMOCK_LOG("Cannot get last expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(last_expected_call); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgumentBuffer called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + if (umockcall_set_ignore_all_calls(last_expected_call, 1) != 0) \ + { \ + UMOCK_LOG("Cannot set the ignore_all_calls value on the last expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + } \ + } \ + return mock_call_modifier; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_179: [ The CaptureReturn call modifier shall copy the return value that is being returned to the code under test when an actual call is matched with the expected call. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_180: [ If CaptureReturn is called multiple times for the same call, an error shall be indicated with the code UMOCK_C_CAPTURE_RETURN_ALREADY_USED. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_182: [ If captured_return_value is NULL, umock_c shall raise an error with the code UMOCK_C_NULL_ARGUMENT. ]*/ +#define IMPLEMENT_CAPTURE_RETURN_FUNCTION(return_type, name, ...) \ + static C2(mock_call_modifier_,name) C2(capture_return_func_,name)(return_type* captured_return_value) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (captured_return_value == NULL) \ + { \ + UMOCK_LOG("NULL captured_return_value."); \ + umock_c_indicate_error(UMOCK_C_NULL_ARGUMENT); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("CaptureReturn called without having an expected call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + if (mock_call_data->captured_return_value != NULL) \ + { \ + UMOCK_LOG("CaptureReturn called multiple times."); \ + umock_c_indicate_error(UMOCK_C_CAPTURE_RETURN_ALREADY_USED); \ + } \ + else \ + { \ + mock_call_data->captured_return_value = captured_return_value; \ + } \ + } \ + } \ + return mock_call_modifier; \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_183: [ The ValidateArgumentValue_{arg_name} shall validate that the value of an argument matches the value pointed by arg_value. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_184: [ If arg_value is NULL, umock_c shall raise an error with the code UMOCK_C_NULL_ARGUMENT. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_185: [ The ValidateArgumentValue_{arg_name} modifier shall inhibit comparing with any value passed directly as an argument in the expected call. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_186: [ The ValidateArgumentValue_{arg_name} shall implicitly do a ValidateArgument for the arg_name argument, making sure the argument is not ignored. ]*/ +#define IMPLEMENT_VALIDATE_ARGUMENT_VALUE_BY_NAME_FUNCTION(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_value_func_,name,_,arg_name)(arg_type* arg_value) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (arg_value == NULL) \ + { \ + UMOCK_LOG("NULL argument to ValidateArgumentValue_%s.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_NULL_ARGUMENT); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgumentValue_%s called without having an expected call.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + mock_call_data->C2(is_ignored_, arg_name) = ARG_IS_NOT_IGNORED; \ + mock_call_data->C2(validate_arg_value_pointer_, arg_name) = (void*)arg_value; \ + } \ + } \ + return mock_call_modifier; \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_199: [ `ValidateArgumentValue_{arg_name}_AsType` shall ensure that validation of the argument `arg_name` is done as if the argument is of type `type_name`. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_200: [ If `type_name` is NULL, umock_c shall raise an error with the code UMOCK_C_NULL_ARGUMENT. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_202: [ If storing the argument value as the new type fails, umock_c shall raise an error with the code UMOCK_C_COPY_ARGUMENT_ERROR. ]*/ +#define IMPLEMENT_VALIDATE_ARGUMENT_VALUE_AS_TYPE_BY_NAME_FUNCTION(name, arg_type, arg_name) \ + static C2(mock_call_modifier_,name) C4(validate_argument_value_as_type_func_,name,_,arg_name)(const char* type_name) \ + { \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + if (type_name == NULL) \ + { \ + UMOCK_LOG("NULL argument to ValidateArgumentValue_%s_AsType.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_NULL_ARGUMENT); \ + } \ + else \ + { \ + C2(mock_call_, name)* mock_call_data = (C2(mock_call_, name)*)umockcall_get_call_data(umock_c_get_last_expected_call()); \ + if (mock_call_data == NULL) \ + { \ + UMOCK_LOG("ValidateArgumentValue_%s called without having an expected call.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + char* cloned_type_name = umockstring_clone(type_name); \ + if (cloned_type_name == NULL) \ + { \ + umockalloc_free(cloned_type_name); \ + UMOCK_LOG("Cannot allocate memory to copy type_name in ValidateArgumentValue_%s_AsType for type name %s.", TOSTRING(arg_name), type_name); \ + umock_c_indicate_error(UMOCK_C_MALLOC_ERROR); \ + } \ + else \ + { \ + void* temp = umockalloc_malloc(sizeof(arg_type)); \ + if (temp == NULL) \ + { \ + umockalloc_free(cloned_type_name); \ + UMOCK_LOG("Cannot allocate memory for the temporary argument value in ValidateArgumentValue_%s.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + if (umocktypes_copy(GET_USED_ARGUMENT_TYPE(mock_call_data, arg_name, arg_type), (void*)temp, (void*)&mock_call_data->arg_name) != 0) \ + { \ + umockalloc_free(cloned_type_name); \ + UMOCK_LOG("Cannot copy argument in ValidateArgumentValue_%s.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + umocktypes_free(GET_USED_ARGUMENT_TYPE(mock_call_data, arg_name, arg_type), (void*)&mock_call_data->arg_name); \ + if (umocktypes_copy(type_name, (void*)&mock_call_data->arg_name, (void*)temp) != 0) \ + { \ + umockalloc_free(cloned_type_name); \ + UMOCK_LOG("Cannot copy argument as new type in ValidateArgumentValue_%s.", TOSTRING(arg_name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + umocktypes_free(GET_USED_ARGUMENT_TYPE(mock_call_data, arg_name, arg_type), (void*)temp); \ + umockalloc_free(mock_call_data->C2(override_argument_type_, arg_name)); \ + mock_call_data->C2(override_argument_type_, arg_name) = cloned_type_name; \ + } \ + } \ + umockalloc_free(temp); \ + } \ + } \ + } \ + } \ + return mock_call_modifier; \ + } + +#define IMPLEMENT_MOCK_FUNCTION(function_prefix, args_ignored, return_type, name, ...) \ + C2(mock_call_modifier_,name) UMOCK_STATIC C2(function_prefix,name)(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(ARG_IN_SIGNATURE, __VA_ARGS__)) \ + { \ + UMOCKCALL_HANDLE mock_call; \ + C2(mock_call_,name)* mock_call_data = (C2(mock_call_,name)*)umockalloc_malloc(sizeof(C2(mock_call_,name))); \ + DECLARE_MOCK_CALL_MODIFIER(name) \ + FOR_EACH_2(COPY_ARG_TO_MOCK_STRUCT, __VA_ARGS__) \ + IF(args_ignored, FOR_EACH_2(MARK_ARG_AS_IGNORED, __VA_ARGS__), FOR_EACH_2(MARK_ARG_AS_NOT_IGNORED, __VA_ARGS__)) \ + FOR_EACH_2_COUNTED(CLEAR_OUT_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(CLEAR_VALIDATE_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2(CLEAR_VALIDATE_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2(CLEAR_OVERRIDE_ARGUMENT_TYPE, __VA_ARGS__) \ + IF(IS_NOT_VOID(return_type), \ + mock_call_data->return_value_set = RETURN_VALUE_NOT_SET; \ + mock_call_data->captured_return_value = NULL; \ + mock_call_data->fail_return_value_set = FAIL_RETURN_VALUE_NOT_SET; \ + ,) \ + mock_call = umockcall_create(#name, mock_call_data, C2(mock_call_data_copy_func_,name), C2(mock_call_data_free_func_,name), C2(mock_call_data_stringify_,name), C2(mock_call_data_are_equal_,name)); \ + if (mock_call == NULL) \ + { \ + UMOCK_LOG("Failed creating mock call."); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + (void)umock_c_add_expected_call(mock_call); \ + } \ + return mock_call_modifier; \ + } \ + +#define IMPLEMENT_STRICT_EXPECTED_MOCK(return_type, name, ...) \ + IMPLEMENT_MOCK_FUNCTION(umock_c_strict_expected_, 0, return_type, name, __VA_ARGS__) + +#define IMPLEMENT_EXPECTED_MOCK(return_type, name, ...) \ + IMPLEMENT_MOCK_FUNCTION(umock_c_expected_, 1, return_type, name, __VA_ARGS__) + +/* Codes_SRS_UMOCK_C_LIB_01_104: [The REGISTER_GLOBAL_MOCK_HOOK shall register a mock hook to be called every time the mocked function is called by production code.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_107: [If there are multiple invocations of REGISTER_GLOBAL_MOCK_HOOK, the last one shall take effect over the previous ones.] */ +/* Codes_SRS_UMOCK_C_LIB_01_134: [ REGISTER_GLOBAL_MOCK_HOOK called with a NULL hook unregisters a previously registered hook. ]*/ +#define IMPLEMENT_REGISTER_GLOBAL_MOCK_HOOK(return_type, name, ...) \ + UMOCK_STATIC void C2(set_global_mock_hook_,name)(C2(mock_hook_func_type_, name) mock_return_hook) \ + { \ + C2(mock_hook_,name) = mock_return_hook; \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_108: [The REGISTER_GLOBAL_MOCK_RETURN shall register a return value to always be returned by a mock function.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_109: [If there are multiple invocations of REGISTER_GLOBAL_MOCK_RETURN, the last one shall take effect over the previous ones.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_141: [ If any error occurs during REGISTER_GLOBAL_MOCK_RETURN, umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +#define IMPLEMENT_REGISTER_GLOBAL_MOCK_RETURN(return_type, name, ...) \ + IF(IS_NOT_VOID(return_type), UMOCK_STATIC void C2(set_global_mock_return_, name)(return_type return_value) \ + { \ + C2(mock_call_default_result_,name) = return_value; \ + }, ) \ + +/* Codes_SRS_UMOCK_C_LIB_01_111: [The REGISTER_GLOBAL_MOCK_FAIL_RETURN shall register a fail return value to be returned by a mock function when marked as failed in the expected calls.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_112: [If there are multiple invocations of REGISTER_GLOBAL_FAIL_MOCK_RETURN, the last one shall take effect over the previous ones.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_142: [ If any error occurs during REGISTER_GLOBAL_MOCK_FAIL_RETURN, umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +#define IMPLEMENT_REGISTER_GLOBAL_MOCK_FAIL_RETURN(return_type, name, ...) \ + IF(IS_NOT_VOID(return_type), UMOCK_STATIC void C2(set_global_mock_fail_return_, name)(return_type fail_return_value) \ + { \ + C2(mock_call_fail_result_,name) = fail_return_value; \ + }, ) \ + +/* Codes_SRS_UMOCK_C_LIB_01_113: [The REGISTER_GLOBAL_MOCK_RETURNS shall register both a success and a fail return value associated with a mock function.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_114: [If there are multiple invocations of REGISTER_GLOBAL_MOCK_RETURNS, the last one shall take effect over the previous ones.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_143: [ If any error occurs during REGISTER_GLOBAL_MOCK_RETURNS, umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +#define IMPLEMENT_REGISTER_GLOBAL_MOCK_RETURNS(return_type, name, ...) \ + IF(IS_NOT_VOID(return_type), UMOCK_STATIC void C2(set_global_mock_returns_, name)(return_type return_value, return_type fail_return_value) \ + { \ + C2(mock_call_default_result_,name) = return_value; \ + C2(mock_call_fail_result_,name) = fail_return_value; \ + }, ) \ + +#define DECLARE_VALIDATE_ONE_ARGUMENT_FUNC_TYPE(name) \ + typedef struct C2(_mock_call_modifier_, name) (*C2(validate_one_argument_func_type_, name))(void); + +#define COPY_RETURN_VALUE(return_type, name) \ + result = C2(mock_call_default_result_, name); + +typedef struct MOCK_CALL_ARG_METADATA_TAG +{ + const char* type; + const char* name; +} MOCK_CALL_ARG_METADATA; + +typedef struct MOCK_CALL_METADATA_TAG +{ + const char* return_type; + const char* name; + size_t arg_count; + const MOCK_CALL_ARG_METADATA* args; +} MOCK_CALL_METADATA; + +#define UNUSED_ARG(arg_type, arg_name) \ + (void)arg_name; + +/* Codes_SRS_UMOCK_C_LIB_01_205: [ If `IGNORED_PTR_ARG` or `IGNORED_NUM_ARG` is used as an argument value with `STRICT_EXPECTED_CALL`, the argument shall be automatically ignored. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_206: [ `IGNORED_PTR_ARG` shall be defined as NULL so that it can be used for pointer type arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_207: [ `IGNORED_NUM_ARG` shall be defined to 0 so that it can be used for numeric type arguments. ]*/ +#define AUTO_IGNORE_ARG(arg_type, arg_name) \ + if (umockautoignoreargs_is_call_argument_ignored(call_as_string, arg_index++, &is_ignored) != 0) \ + { \ + UMOCK_LOG("Failed parsing argument %s value from the call.", TOSTRING(arg_name)); \ + } \ + else \ + { \ + if (is_ignored) \ + { \ + result.C2(IgnoreArgument_, arg_name)(); \ + } \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_004: [If ENABLE_MOCKS is defined, MOCKABLE_FUNCTION shall generate the declaration of the function and code for the mocked function, thus allowing setting up of expectations in test functions.] */ +/* Codes_SRS_UMOCK_C_LIB_01_014: [For each argument the argument value shall be stored for later comparison with actual calls.] */ +/* Codes_SRS_UMOCK_C_LIB_01_017: [No arguments shall be saved by default, unless other modifiers state it.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_074: [When an expected call is recorded a call modifier interface in the form of a structure containing function pointers shall be returned to the caller.] */ +/* Codes_SRS_UMOCK_C_LIB_01_075: [The last modifier in a chain overrides previous modifiers if any collision occurs.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_127: [ IgnoreAllArguments shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_126: [ ValidateAllArguments shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_125: [ IgnoreArgument_{arg_name} shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_124: [ ValidateArgument_{arg_name} shall only be available for mock functions that have arguments. **]*/ +/* Codes_SRS_UMOCK_C_LIB_01_123: [ IgnoreArgument shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_122: [ ValidateArgument shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_121: [ SetReturn shall only be available if the return type is not void. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_120: [ SetFailReturn shall only be available if the return type is not void. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_119: [ CopyOutArgumentBuffer shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_128: [ CopyOutArgument shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_129: [ ValidateArgumentBuffer shall only be available for mock functions that have arguments. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_105: [The hook\92s result shall be returned by the mock to the production code.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_106: [The signature for the hook shall be assumed to have exactly the same arguments and return as the mocked function.]*/ +/* Codes_SRS_UMOCK_C_LIB_01_135: [ All parameters passed to the mock shall be passed down to the mock hook. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_148: [ If call comparison fails an error shall be indicated by calling the error callback with UMOCK_C_COMPARE_CALL_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_136: [ When multiple return values are set for a mock function by using different means (such as SetReturn), the following order shall be in effect: ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_137: [ - If a return value has been specified for an expected call then that value shall be returned. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_138: [ - If a global mock hook has been specified then it shall be called and its result returned. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_139: [ - If a global return value has been specified then it shall be returned. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_140: [ - Otherwise the value of a static variable of the same type as the return type shall be returned. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_188: [ The create call shall have a non-void return type. ]*/ +#define MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK_NO_CODE(return_type, name, ...) \ + typedef return_type (*C2(mock_hook_func_type_, name))(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(ARG_IN_SIGNATURE, __VA_ARGS__)); \ + static C2(mock_hook_func_type_,name) C2(mock_hook_,name) = NULL; \ + static TRACK_CREATE_FUNC_TYPE C2(track_create_destroy_pair_malloc_,name) = NULL; \ + static TRACK_DESTROY_FUNC_TYPE C2(track_create_destroy_pair_free_,name) = NULL; \ + static PAIRED_HANDLES C2(paired_handles_,name); \ + static PAIRED_HANDLES* C2(used_paired_handles_,name) = NULL; \ + static const MOCK_CALL_ARG_METADATA C2(mock_call_args_metadata_,name)[IF(COUNT_ARG(__VA_ARGS__), DIV2(COUNT_ARG(__VA_ARGS__)), 1)] \ + = { IF(COUNT_ARG(__VA_ARGS__),,NULL) FOR_EACH_2(FILL_ARG_IN_METADATA, __VA_ARGS__) }; \ + static const MOCK_CALL_METADATA C2(mock_call_metadata_,name) = { TOSTRING(return_type), TOSTRING(name), DIV2(COUNT_ARG(__VA_ARGS__)), \ + C2(mock_call_args_metadata_,name) }; \ + struct C2(_mock_call_modifier_,name); \ + IF(IS_NOT_VOID(return_type),typedef struct C2(_mock_call_modifier_,name) (*C2(set_return_func_type_,name))(return_type return_value); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(set_fail_return_func_type_,name))(return_type return_value); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(capture_return_func_type_,name))(return_type* captured_return_value);,) \ + typedef struct C2(_mock_call_modifier_,name) (*C2(ignore_all_calls_func_type_,name))(void); \ + IF(COUNT_ARG(__VA_ARGS__),typedef struct C2(_mock_call_modifier_,name) (*C2(ignore_all_arguments_func_type_,name))(void); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(validate_all_arguments_func_type_,name))(void); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(copy_out_argument_func_type_,name))(size_t arg_index, void* value); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(ignore_argument_func_type_,name))(size_t arg_index); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(validate_argument_func_type_,name))(size_t arg_index); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(validate_argument_buffer_func_type_,name))(size_t index, const void* bytes, size_t length); \ + typedef struct C2(_mock_call_modifier_,name) (*C2(copy_out_argument_buffer_func_type_,name))(size_t index, const void* bytes, size_t length); \ + FOR_EACH_2_KEEP_1(DECLARE_ARG_RELATED_FUNCTIONS, name, __VA_ARGS__),) \ + typedef struct C2(_mock_call_modifier_,name) \ + { \ + C2(ignore_all_calls_func_type_,name) IgnoreAllCalls; \ + IF(IS_NOT_VOID(return_type),C2(set_return_func_type_,name) SetReturn; \ + C2(set_fail_return_func_type_,name) SetFailReturn; \ + C2(capture_return_func_type_,name) CaptureReturn;,) \ + IF(COUNT_ARG(__VA_ARGS__),C2(ignore_all_arguments_func_type_,name) IgnoreAllArguments; \ + C2(validate_all_arguments_func_type_,name) ValidateAllArguments; \ + C2(copy_out_argument_func_type_,name) CopyOutArgument; \ + C2(ignore_argument_func_type_,name) IgnoreArgument; \ + C2(validate_argument_func_type_,name) ValidateArgument; \ + C2(validate_argument_buffer_func_type_,name) ValidateArgumentBuffer; \ + C2(copy_out_argument_buffer_func_type_,name) CopyOutArgumentBuffer; \ + FOR_EACH_2_KEEP_1(ARG_RELATED_FUNCTIONS_IN_MODIFIERS, name, __VA_ARGS__),) \ + } C2(mock_call_modifier_,name); \ + static C2(mock_call_modifier_,name) C2(ignore_all_calls_func_,name)(void); \ + IF(IS_NOT_VOID(return_type),static C2(mock_call_modifier_,name) C2(set_return_func_,name)(return_type return_value); \ + static C2(mock_call_modifier_,name) C2(set_fail_return_func_,name)(return_type return_value); \ + static C2(mock_call_modifier_,name) C2(capture_return_func_,name)(return_type* captured_return_value);,) \ + IF(COUNT_ARG(__VA_ARGS__),static C2(mock_call_modifier_,name) C2(ignore_all_arguments_func_,name)(void); \ + static C2(mock_call_modifier_,name) C2(validate_all_arguments_func_,name)(void); \ + static C2(mock_call_modifier_,name) C2(copy_out_argument_func_,name)(size_t arg_index, void* value); \ + static C2(mock_call_modifier_,name) C2(ignore_argument_func_,name)(size_t arg_index); \ + static C2(mock_call_modifier_,name) C2(validate_argument_func_,name)(size_t arg_index); \ + static C2(mock_call_modifier_,name) C2(validate_argument_buffer_func_,name)(size_t index, const void* bytes, size_t length); \ + static C2(mock_call_modifier_,name) C2(copy_out_argument_buffer_func_,name)(size_t index, const void* bytes, size_t length); \ + FOR_EACH_2_KEEP_1(DECLARE_IGNORE_ARGUMENT_FUNCTION_PROTOTYPE, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(DECLARE_VALIDATE_ARGUMENT_FUNCTION_PROTOTYPE, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(DECLARE_COPY_OUT_ARGUMENT_BUFFER_FUNCTION_PROTOTYPE, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(DECLARE_VALIDATE_ARGUMENT_VALUE_FUNCTION_PROTOTYPE, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(DECLARE_VALIDATE_ARGUMENT_VALUE_AS_TYPE_FUNCTION_PROTOTYPE, name, __VA_ARGS__) \ + typedef struct C2(_mock_call_modifier_,name) (*C2(ignore_one_argument_func_type_,name))(void);,) \ + IF(COUNT_ARG(__VA_ARGS__), static const C2(ignore_one_argument_func_type_,name) C2(ignore_one_argument_array_,name)[] = \ + {,) \ + FOR_EACH_2_KEEP_1(IGNORE_ARGUMENT_FUNCTION_IN_ARRAY, name, __VA_ARGS__) \ + IF(COUNT_ARG(__VA_ARGS__), }; \ + DECLARE_VALIDATE_ONE_ARGUMENT_FUNC_TYPE(name) \ + static const C2(validate_one_argument_func_type_,name) C2(validate_one_argument_array_,name)[] = \ + {,) \ + FOR_EACH_2_KEEP_1(VALIDATE_ARGUMENT_FUNCTION_IN_ARRAY, name, __VA_ARGS__) \ + IF(COUNT_ARG(__VA_ARGS__),};,) \ + static void C2(fill_mock_call_modifier_,name)(C2(mock_call_modifier_,name)* mock_call_modifier) \ + { \ + IF(IS_NOT_VOID(return_type),mock_call_modifier->SetReturn = C2(set_return_func_,name); \ + mock_call_modifier->SetFailReturn = C2(set_fail_return_func_,name); \ + mock_call_modifier->CaptureReturn = C2(capture_return_func_,name);,) \ + IF(COUNT_ARG(__VA_ARGS__),mock_call_modifier->IgnoreAllArguments = C2(ignore_all_arguments_func_,name); \ + mock_call_modifier->ValidateAllArguments = C2(validate_all_arguments_func_,name); \ + mock_call_modifier->CopyOutArgument = C2(copy_out_argument_func_,name); \ + mock_call_modifier->IgnoreArgument = C2(ignore_argument_func_,name); \ + mock_call_modifier->ValidateArgument = C2(validate_argument_func_,name); \ + mock_call_modifier->ValidateArgumentBuffer = C2(validate_argument_buffer_func_,name); \ + mock_call_modifier->CopyOutArgumentBuffer = C2(copy_out_argument_buffer_func_,name); \ + FOR_EACH_2_KEEP_1(COPY_IGNORE_ARG_BY_NAME_TO_MODIFIER, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(COPY_VALIDATE_ARG_BY_NAME_TO_MODIFIER, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(COPY_COPY_OUT_ARGUMENT_BUFFER_BY_NAME_TO_MODIFIER, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(COPY_VALIDATE_ARGUMENT_VALUE_BY_NAME_TO_MODIFIER, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(COPY_VALIDATE_ARGUMENT_VALUE_AS_TYPE_BY_NAME_TO_MODIFIER, name, __VA_ARGS__),) \ + mock_call_modifier->IgnoreAllCalls = C2(ignore_all_calls_func_,name); \ + } \ + typedef struct C2(_mock_call_,name) \ + { \ + IF(IS_NOT_VOID(return_type),return_type return_value; \ + return_type fail_return_value; \ + return_type* captured_return_value;,) \ + IF(COUNT_ARG(__VA_ARGS__), FOR_EACH_2(DECLARE_MOCK_CALL_STRUCT_STACK, __VA_ARGS__) \ + FOR_EACH_2(DECLARE_OUT_ARG_BUFFER_FOR_ARG, __VA_ARGS__) \ + ARG_BUFFER* out_arg_buffers[IF(COUNT_ARG(__VA_ARGS__), DIV2(COUNT_ARG(__VA_ARGS__)),1)]; \ + ARG_BUFFER validate_arg_buffers[IF(COUNT_ARG(__VA_ARGS__), DIV2(COUNT_ARG(__VA_ARGS__)),1)]; \ + FOR_EACH_2(DECLARE_VALIDATE_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2(DECLARE_IGNORE_FLAG_FOR_ARG, __VA_ARGS__) \ + FOR_EACH_2(DECLARE_OVERRIDE_ARGUMENT_TYPE_FOR_ARG, __VA_ARGS__),) \ + IF(IS_NOT_VOID(return_type),int fail_return_value_set : 1; \ + int return_value_set : 1;,) \ + IF(COUNT_ARG(__VA_ARGS__), , IF(IS_NOT_VOID(return_type),, int dummy : 1;)) \ + } C2(mock_call_,name); \ + typedef C2(mock_call_modifier_,name) (*C3(auto_ignore_args_function_,name,_type))(C2(mock_call_modifier_,name) call_modifier, const char* call_as_string); \ + C2(mock_call_modifier_,name) UMOCK_STATIC C2(auto_ignore_args_function_,name)(C2(mock_call_modifier_,name) call_modifier, const char* call_as_string) \ + { \ + C2(mock_call_modifier_,name) result = call_modifier; \ + IF(COUNT_ARG(__VA_ARGS__), \ + int is_ignored; \ + int arg_index = 1; ,) \ + (void)call_as_string; \ + FOR_EACH_2(AUTO_IGNORE_ARG, __VA_ARGS__) \ + return result; \ + } \ + C3(auto_ignore_args_function_,name,_type) UMOCK_STATIC C2(get_auto_ignore_args_function_,name)(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(ARG_IN_SIGNATURE, __VA_ARGS__)) \ + { \ + FOR_EACH_2(UNUSED_ARG, __VA_ARGS__) \ + return C2(auto_ignore_args_function_,name); \ + } \ + UMOCK_STATIC char* C2(mock_call_data_stringify_,name)(void* mock_call_data) \ + { \ + char* result; \ + IF(COUNT_ARG(__VA_ARGS__), C2(mock_call_,name)* typed_mock_call_data = (C2(mock_call_,name)*)mock_call_data;,) \ + int is_error = 0; \ + size_t args_string_length = 0; \ + FOR_EACH_2_COUNTED(STRINGIFY_ARGS_DECLARE_RESULT_VAR, __VA_ARGS__) \ + IF(COUNT_ARG(__VA_ARGS__),,(void)mock_call_data;) \ + FOR_EACH_2(STRINGIFY_ARGS_CHECK_ARG_STRINGIFY_SUCCESS, __VA_ARGS__) \ + (void)mock_call_data; \ + if (is_error != 0) \ + { \ + result = NULL; \ + } \ + else \ + { \ + FOR_EACH_2(STRINGIFY_ARGS_DECLARE_ARG_STRING_LENGTH, __VA_ARGS__) \ + FOR_EACH_2(STRINGIFY_ARGS_COUNT_LENGTH, __VA_ARGS__) \ + IF(COUNT_ARG(__VA_ARGS__), args_string_length += COUNT_ARG(__VA_ARGS__) - 1;,) \ + result = (char*)umockalloc_malloc(args_string_length + 1); \ + if (result != NULL) \ + { \ + if (args_string_length == 0) \ + { \ + result[0] = '\0'; \ + } \ + else \ + { \ + IF(COUNT_ARG(__VA_ARGS__), \ + size_t current_pos = 0; \ + size_t arg_index = 0; \ + FOR_EACH_2(STRINGIFY_ARGS_COPY_ARG_STRING, __VA_ARGS__), ) \ + } \ + } \ + } \ + FOR_EACH_2(STRINGIFY_ARGS_FREE_STRINGIFIED_ARG, __VA_ARGS__) \ + return result; \ + } \ + UMOCK_STATIC int C2(mock_call_data_are_equal_,name)(void* left, void* right) \ + { \ + int result; \ + if (left == right) \ + { \ + result = 1; \ + } \ + else if ((left == NULL) || (right == NULL)) \ + { \ + result = 0; \ + } \ + else \ + { \ + result = 1; \ + IF(COUNT_ARG(__VA_ARGS__), \ + { \ + C2(mock_call_,name)* typed_left = (C2(mock_call_,name)*)left; \ + C2(mock_call_,name)* typed_right = (C2(mock_call_,name)*)right; \ + FOR_EACH_2_COUNTED(ARE_EQUAL_FOR_ARG, __VA_ARGS__) \ + }, ) \ + } \ + return result; \ + } \ + UMOCK_STATIC void C2(mock_call_data_free_func_,name)(void* mock_call_data) \ + { \ + C2(mock_call_,name)* typed_mock_call_data = (C2(mock_call_,name)*)mock_call_data; \ + FOR_EACH_2_COUNTED(FREE_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(FREE_OUT_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(FREE_VALIDATE_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(FREE_OVERRIDE_ARGUMENT_TYPE, __VA_ARGS__) \ + IF(IS_NOT_VOID(return_type),if (typed_mock_call_data->return_value_set == RETURN_VALUE_SET) \ + { \ + umocktypes_free(TOSTRING(return_type), (void*)&typed_mock_call_data->return_value); \ + } \ + if (typed_mock_call_data->fail_return_value_set == FAIL_RETURN_VALUE_SET) \ + { \ + umocktypes_free(TOSTRING(return_type), (void*)&typed_mock_call_data->fail_return_value); \ + },) \ + umockalloc_free(typed_mock_call_data); \ + } \ + UMOCK_STATIC void* C2(mock_call_data_copy_func_,name)(void* mock_call_data) \ + { \ + C2(mock_call_,name)* result = (C2(mock_call_,name)*)umockalloc_malloc(sizeof(C2(mock_call_,name))); \ + IF(COUNT_ARG(__VA_ARGS__), C2(mock_call_,name)* typed_mock_call_data = (C2(mock_call_,name)*)mock_call_data;,) \ + IF(IS_NOT_VOID(return_type), C2(mock_call_,name)* typed_mock_call_data_result = (C2(mock_call_,name)*)mock_call_data;,) \ + (void)mock_call_data; \ + FOR_EACH_2(COPY_IGNORE_ARG, __VA_ARGS__) \ + FOR_EACH_2(COPY_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(COPY_OUT_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(COPY_VALIDATE_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2(COPY_VALIDATE_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2(COPY_OVERRIDE_ARGUMENT_TYPE, __VA_ARGS__) \ + IF(IS_NOT_VOID(return_type), \ + result->return_value_set = typed_mock_call_data_result->return_value_set; \ + result->fail_return_value_set = typed_mock_call_data_result->fail_return_value_set; \ + if (typed_mock_call_data_result->return_value_set == RETURN_VALUE_SET) \ + { \ + umocktypes_copy(TOSTRING(return_type), (void*)&result->return_value, (void*)&typed_mock_call_data_result->return_value); \ + } \ + result->captured_return_value = typed_mock_call_data_result->captured_return_value; \ + if (typed_mock_call_data_result->fail_return_value_set == FAIL_RETURN_VALUE_SET) \ + { \ + umocktypes_copy(TOSTRING(return_type), (void*)&result->fail_return_value, (void*)&typed_mock_call_data_result->fail_return_value); \ + },) \ + return result; \ + } \ + IF(IS_NOT_VOID(return_type),static return_type C2(mock_call_default_result_,name); \ + static return_type C2(mock_call_fail_result_,name); \ + IMPLEMENT_SET_RETURN_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_SET_FAIL_RETURN_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_CAPTURE_RETURN_FUNCTION(return_type, name, __VA_ARGS__),) \ + IF(COUNT_ARG(__VA_ARGS__),IMPLEMENT_IGNORE_ALL_ARGUMENTS_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_VALIDATE_ALL_ARGUMENTS_FUNCTION(return_type, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(IMPLEMENT_IGNORE_ARGUMENT_BY_NAME_FUNCTION, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(IMPLEMENT_VALIDATE_ARGUMENT_BY_NAME_FUNCTION, name, __VA_ARGS__) \ + IMPLEMENT_IGNORE_ARGUMENT_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_VALIDATE_ARGUMENT_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_COPY_OUT_ARGUMENT_BUFFER_FUNCTION(return_type, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(IMPLEMENT_COPY_OUT_ARGUMENT_BUFFER_BY_NAME_FUNCTION, name, __VA_ARGS__) \ + IMPLEMENT_COPY_OUT_ARGUMENT_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_VALIDATE_ARGUMENT_BUFFER_FUNCTION(return_type, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(IMPLEMENT_VALIDATE_ARGUMENT_VALUE_BY_NAME_FUNCTION, name, __VA_ARGS__) \ + FOR_EACH_2_KEEP_1(IMPLEMENT_VALIDATE_ARGUMENT_VALUE_AS_TYPE_BY_NAME_FUNCTION, name, __VA_ARGS__),) \ + IMPLEMENT_IGNORE_ALL_CALLS_FUNCTION(return_type, name, __VA_ARGS__) \ + IMPLEMENT_REGISTER_GLOBAL_MOCK_HOOK(return_type, name, __VA_ARGS__) \ + IMPLEMENT_REGISTER_GLOBAL_MOCK_RETURN(return_type, name, __VA_ARGS__) \ + IMPLEMENT_REGISTER_GLOBAL_MOCK_FAIL_RETURN(return_type, name, __VA_ARGS__) \ + IMPLEMENT_REGISTER_GLOBAL_MOCK_RETURNS(return_type, name, __VA_ARGS__) \ + IMPLEMENT_STRICT_EXPECTED_MOCK(return_type, name, __VA_ARGS__) \ + IMPLEMENT_EXPECTED_MOCK(return_type, name, __VA_ARGS__) \ + +/* Codes_SRS_UMOCK_C_LIB_01_193: [ When a destroy_call happens the memory block associated with the argument passed to it shall be freed. ] */ +/* Codes_SRS_UMOCK_C_LIB_01_195: [ If any error occurs during the destroy_call related then umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_194: [ If the first argument passed to destroy_call is not found in the list of tracked handles (returned by create_call) then umock_c shall raise an error with the code UMOCK_C_INVALID_PAIRED_CALLS. ]*/ +#define MOCKABLE_FUNCTION_BODY_WITHOUT_RETURN(modifiers, return_type, name, ...) \ + UMOCK_STATIC return_type modifiers name(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(ARG_IN_SIGNATURE, __VA_ARGS__)) \ + { \ + UMOCKCALL_HANDLE mock_call; \ + UMOCKCALL_HANDLE matched_call; \ + IF(IS_NOT_VOID(return_type), \ + unsigned int result_value_set = 0; \ + unsigned int fail_result_value_set = 0; \ + void* captured_return_value = NULL;,) \ + IF(IS_NOT_VOID(return_type),TRACK_CREATE_FUNC_TYPE track_create_destroy_pair_malloc_local = C2(track_create_destroy_pair_malloc_,name); \ + PAIRED_HANDLES* used_paired_handles_local = C2(used_paired_handles_,name); \ + const char* return_type_string = TOSTRING(return_type);,) \ + IF(IS_NOT_VOID(return_type),return_type result = C2(mock_call_default_result_,name);,) \ + C2(mock_call_,name)* matched_call_data; \ + C2(mock_call_,name)* mock_call_data = (C2(mock_call_,name)*)umockalloc_malloc(sizeof(C2(mock_call_,name))); \ + FOR_EACH_2(COPY_ARG_TO_MOCK_STRUCT, __VA_ARGS__) \ + FOR_EACH_2(MARK_ARG_AS_NOT_IGNORED, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(CLEAR_OUT_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2_COUNTED(CLEAR_VALIDATE_ARG_BUFFERS, __VA_ARGS__) \ + FOR_EACH_2(CLEAR_VALIDATE_ARG_VALUE, __VA_ARGS__) \ + FOR_EACH_2(CLEAR_OVERRIDE_ARGUMENT_TYPE, __VA_ARGS__) \ + IF(IS_NOT_VOID(return_type),mock_call_data->return_value_set = RETURN_VALUE_NOT_SET; \ + mock_call_data->captured_return_value = NULL; \ + mock_call_data->fail_return_value_set = FAIL_RETURN_VALUE_NOT_SET;,) \ + mock_call = umockcall_create(#name, mock_call_data, C2(mock_call_data_copy_func_,name), C2(mock_call_data_free_func_,name), C2(mock_call_data_stringify_,name), C2(mock_call_data_are_equal_,name)); \ + if (mock_call == NULL) \ + { \ + IF(IS_NOT_VOID(return_type),COPY_RETURN_VALUE(return_type, name),) \ + UMOCK_LOG("Could not create a mock call in the actual call for %s.", TOSTRING(name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + else \ + { \ + if (umock_c_add_actual_call(mock_call, &matched_call) != 0) \ + { \ + umockcall_destroy(mock_call); \ + UMOCK_LOG("Could not add an actual call for %s.", TOSTRING(name)); \ + umock_c_indicate_error(UMOCK_C_COMPARE_CALL_ERROR); \ + } \ + if (matched_call != NULL) \ + { \ + matched_call_data = (C2(mock_call_,name)*)umockcall_get_call_data(matched_call); \ + IF(IS_NOT_VOID(return_type),if (matched_call_data != NULL) \ + { \ + captured_return_value = (void*)matched_call_data->captured_return_value; \ + if (umockcall_get_fail_call(matched_call)) \ + { \ + if (matched_call_data->fail_return_value_set == FAIL_RETURN_VALUE_SET) \ + { \ + result = matched_call_data->fail_return_value; \ + } \ + else \ + { \ + result = C2(mock_call_fail_result_, name); \ + } \ + result_value_set = 1; \ + fail_result_value_set = 1; \ + } \ + else if (matched_call_data->return_value_set == RETURN_VALUE_SET) \ + { \ + result = matched_call_data->return_value; \ + result_value_set = 1; \ + } \ + else \ + { \ + if (C2(mock_hook_, name) != NULL) \ + { \ + IF(IS_NOT_VOID(return_type),result =,) C2(mock_hook_, name)(FOR_EACH_2_COUNTED(ARG_NAME_ONLY_IN_CALL, __VA_ARGS__)); \ + IF(IS_NOT_VOID(return_type),result_value_set = 1;,) \ + } \ + } \ + }, \ + if (C2(mock_hook_, name) != NULL) \ + { \ + C2(mock_hook_, name)(FOR_EACH_2_COUNTED(ARG_NAME_ONLY_IN_CALL, __VA_ARGS__)); \ + } \ + ) \ + IF(COUNT_ARG(__VA_ARGS__), FOR_EACH_2_COUNTED(COPY_OUT_ARG_VALUE_FROM_MATCHED_CALL, __VA_ARGS__),) \ + } \ + else \ + { \ + if (C2(mock_hook_, name) != NULL) \ + { \ + IF(IS_NOT_VOID(return_type),result =,) C2(mock_hook_, name)(FOR_EACH_2_COUNTED(ARG_NAME_ONLY_IN_CALL, __VA_ARGS__)); \ + IF(IS_NOT_VOID(return_type),result_value_set = 1;,) \ + } \ + } \ + } \ + IF(COUNT_ARG(__VA_ARGS__), if (C2(track_create_destroy_pair_free_, name) != NULL) \ + { \ + if (C2(track_create_destroy_pair_free_, name)(C2(used_paired_handles_, name), (void*)&ONLY_FIRST_ARG(__VA_ARGS__, 1)) != 0) \ + { \ + UMOCK_LOG("Could not track the destroy call for %s.", TOSTRING(name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + },) \ + { + +/* Codes_SRS_UMOCK_C_LIB_01_188: [ The create call shall have a non-void return type. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_191: [ At each create_call a memory block shall be allocated so that it can be reported as a leak by any memory checker. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_192: [ If any error occurs during the create_call related then umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_204: [ Tracking of paired calls shall not be done if the actual call to the `create_call` is using the `SetFailReturn` call modifier. ]*/ +#define MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK(modifiers, return_type, name, ...) \ + MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK_NO_CODE(return_type, name, __VA_ARGS__) \ + MOCKABLE_FUNCTION_BODY_WITHOUT_RETURN(modifiers, return_type, name, __VA_ARGS__) \ + IF(IS_NOT_VOID(return_type), \ + if (result_value_set == 0) \ + { \ + COPY_RETURN_VALUE(return_type, name) \ + }; \ + if (captured_return_value != NULL) \ + { \ + (void)memcpy(captured_return_value, (void*)&result, sizeof(result)); \ + } \ + if ((track_create_destroy_pair_malloc_local != NULL) && (fail_result_value_set == 0)) \ + { \ + if (track_create_destroy_pair_malloc_local(used_paired_handles_local, (const void*)&result, return_type_string, sizeof(result)) != 0) \ + { \ + UMOCK_LOG("Could not track the create call for %s.", TOSTRING(name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + } \ + return result;,) \ + } \ + } \ + +/* Codes_SRS_UMOCK_C_LIB_01_150: [ MOCK_FUNCTION_WITH_CODE shall define a mock function and allow the user to embed code between this define and a MOCK_FUNCTION_END call. ]*/ +#define MOCK_FUNCTION_WITH_CODE(modifiers, return_type, name, ...) \ + MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK_NO_CODE(return_type, name, __VA_ARGS__) \ + MOCKABLE_FUNCTION_BODY_WITHOUT_RETURN(modifiers, return_type, name, __VA_ARGS__) \ + +/* Codes_SRS_UMOCK_C_LIB_01_188: [ The create call shall have a non-void return type. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_191: [ At each create_call a memory block shall be allocated so that it can be reported as a leak by any memory checker. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_192: [ If any error occurs during the create_call related then umock_c shall raise an error with the code UMOCK_C_ERROR. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_204: [ Tracking of paired calls shall not be done if the actual call to the `create_call` is using the `SetFailReturn` call modifier. ]*/ +#define MOCK_FUNCTION_END(...) \ + IF(COUNT_ARG(__VA_ARGS__), if (result_value_set == 0) \ + { \ + result = __VA_ARGS__; \ + }; \ + if (captured_return_value != NULL) \ + { \ + (void)memcpy(captured_return_value, (void*)&result, sizeof(result)); \ + } \ + if ((track_create_destroy_pair_malloc_local != NULL) && (fail_result_value_set == 0)) \ + { \ + if (track_create_destroy_pair_malloc_local(used_paired_handles_local, (const void*)&result, return_type_string, sizeof(result)) != 0) \ + { \ + UMOCK_LOG("Could not track the create call for %s.", TOSTRING(name)); \ + umock_c_indicate_error(UMOCK_C_ERROR); \ + } \ + } \ + return result;,) \ + } \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_187: [ REGISTER_UMOCKC_PAIRED_CREATE_DESTROY_CALLS shall register with umock two calls that are expected to be paired. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_190: [ If create_call or destroy_call do not obey these rules, at the time of calling REGISTER_UMOCKC_PAIRED_CREATE_DESTROY_CALLS umock_c shall raise an error with the code UMOCK_C_INVALID_PAIRED_CALLS. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_189: [ The destroy call shall take as argument at least one argument. The type of the first argument shall be of the same type as the return type for the create_call. ]*/ +#define REGISTER_UMOCKC_PAIRED_CREATE_DESTROY_CALLS(create_call, destroy_call) \ + if ((strcmp(C2(mock_call_metadata_,create_call).return_type, "void") == 0) || \ + (C2(mock_call_metadata_,destroy_call).arg_count == 0) || \ + (strcmp(C2(mock_call_metadata_,create_call).return_type, C2(mock_call_metadata_, destroy_call).args[0].type) != 0)) \ + { \ + umock_c_indicate_error(UMOCK_C_INVALID_PAIRED_CALLS); \ + } \ + else \ + { \ + C2(track_create_destroy_pair_malloc_, create_call) = umockcallpairs_track_create_paired_call; \ + C2(track_create_destroy_pair_free_, destroy_call) = umockcallpairs_track_destroy_paired_call; \ + C2(used_paired_handles_, create_call) = &C2(paired_handles_, create_call); \ + C2(used_paired_handles_, destroy_call) = &C2(paired_handles_, create_call); \ + } + +#ifdef __cplusplus + } +#endif + +#endif /* UMOCK_C_INTERNAL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umock_c_negative_tests.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCK_C_NEGATIVE_TESTS_H +#define UMOCK_C_NEGATIVE_TESTS_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + + extern int umock_c_negative_tests_init(void); + extern void umock_c_negative_tests_deinit(void); + extern void umock_c_negative_tests_snapshot(void); + extern void umock_c_negative_tests_reset(void); + extern void umock_c_negative_tests_fail_call(size_t index); + extern size_t umock_c_negative_tests_call_count(void); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCK_C_NEGATIVE_TESTS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umock_c_prod.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#undef MOCKABLE_FUNCTION + +/* This header is meant to be included by production code headers, so that the MOCKABLE_FUNCTION gets enabled. */ +/* + If you are porting to a new platform and do not want to build the tests, but only the production code, + simply make sure that this file is in the include path (either by copying it to your inc folder or + by adjusting the include paths). +*/ + +#ifdef ENABLE_MOCKS + +#ifdef ENABLE_MOCK_FILTERING +#define ENABLE_MOCK_FILTERING_SWITCH 1 +#else +#define ENABLE_MOCK_FILTERING_SWITCH 0 +#endif + +#include "macro_utils.h" + +#define UMOCK_C_PROD_ARG_IN_SIGNATURE(count, arg_type, arg_name) arg_type arg_name IFCOMMA(count) + +#define MOCKABLE_FUNCTION_DISABLED(modifiers, result, function, ...) \ + result modifiers function(IF(COUNT_ARG(__VA_ARGS__), , void) FOR_EACH_2_COUNTED(UMOCK_C_PROD_ARG_IN_SIGNATURE, __VA_ARGS__)); + +/* Codes_SRS_UMOCK_C_LIB_01_001: [MOCKABLE_FUNCTION shall be used to wrap function definition allowing the user to declare a function that can be mocked.]*/ +#define MOCKABLE_FUNCTION(modifiers, result, function, ...) \ + IF(ENABLE_MOCK_FILTERING_SWITCH,IF(C2(please_mock_, function),MOCKABLE_FUNCTION_DISABLED,MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK), MOCKABLE_FUNCTION_UMOCK_INTERNAL_WITH_MOCK) (modifiers, result, function, __VA_ARGS__) + +#include "umock_c.h" + +#else + +#include "macro_utils.h" + +#define UMOCK_C_PROD_ARG_IN_SIGNATURE(count, arg_type, arg_name) arg_type arg_name IFCOMMA(count) + +/* Codes_SRS_UMOCK_C_LIB_01_002: [The macro shall generate a function signature in case ENABLE_MOCKS is not defined.] */ +/* Codes_SRS_UMOCK_C_LIB_01_005: [**If ENABLE_MOCKS is not defined, MOCKABLE_FUNCTION shall only generate a declaration for the function.] */ +/* Codes_SRS_UMOCK_C_LIB_01_001: [MOCKABLE_FUNCTION shall be used to wrap function definition allowing the user to declare a function that can be mocked.]*/ +#define MOCKABLE_FUNCTION(modifiers, result, function, ...) \ + result modifiers function(IF(COUNT_ARG(__VA_ARGS__),,void) FOR_EACH_2_COUNTED(UMOCK_C_PROD_ARG_IN_SIGNATURE, __VA_ARGS__)); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umock_log.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCK_LOG_H +#define UMOCK_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + + void UMOCK_LOG(const char* format, ...); + +#ifdef __cplusplus +} +#endif + + + +#endif /* UMOCK_LOG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockalloc.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKALLOC_H +#define UMOCKALLOC_H + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif + +extern void* umockalloc_malloc(size_t size); +extern void* umockalloc_realloc(void* ptr, size_t size); +extern void umockalloc_free(void* ptr); + +extern char* umockc_stringify_buffer(const void* bytes, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKALLOC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockautoignoreargs.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKAUTOIGNOREARGS_H +#define UMOCKAUTOIGNOREARGS_H + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif + +extern int umockautoignoreargs_is_call_argument_ignored(const char* call, size_t argument_index, int* is_argument_ignored); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKAUTOIGNOREARGS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockcall.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKCALL_H +#define UMOCKCALL_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + + typedef struct UMOCKCALL_TAG* UMOCKCALL_HANDLE; + typedef void*(*UMOCKCALL_DATA_COPY_FUNC)(void* umockcall_data); + typedef void(*UMOCKCALL_DATA_FREE_FUNC)(void* umockcall_data); + typedef char*(*UMOCKCALL_DATA_STRINGIFY_FUNC)(void* umockcall_data); + typedef int(*UMOCKCALL_DATA_ARE_EQUAL_FUNC)(void* left, void* right); + + extern UMOCKCALL_HANDLE umockcall_create(const char* function_name, void* umockcall_data, UMOCKCALL_DATA_COPY_FUNC umockcall_data_copy, UMOCKCALL_DATA_FREE_FUNC umockcall_data_free, UMOCKCALL_DATA_STRINGIFY_FUNC umockcall_data_stringify, UMOCKCALL_DATA_ARE_EQUAL_FUNC umockcall_data_are_equal); + extern void umockcall_destroy(UMOCKCALL_HANDLE umockcall); + extern int umockcall_are_equal(UMOCKCALL_HANDLE left, UMOCKCALL_HANDLE right); + extern char* umockcall_stringify(UMOCKCALL_HANDLE umockcall); + extern void* umockcall_get_call_data(UMOCKCALL_HANDLE umockcall); + extern UMOCKCALL_HANDLE umockcall_clone(UMOCKCALL_HANDLE umockcall); + extern int umockcall_set_fail_call(UMOCKCALL_HANDLE umockcall, int fail_call); + extern int umockcall_get_fail_call(UMOCKCALL_HANDLE umockcall); + extern int umockcall_set_ignore_all_calls(UMOCKCALL_HANDLE umockcall, int ignore_all_calls); + extern int umockcall_get_ignore_all_calls(UMOCKCALL_HANDLE umockcall); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKCALL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockcallpairs.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKCALLPAIRS_H +#define UMOCKCALLPAIRS_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +typedef struct PAIRED_HANDLE_TAG +{ + void* handle_value; + char* handle_type; +} PAIRED_HANDLE; + +typedef struct PAIRED_HANDLES_TAG +{ + PAIRED_HANDLE* paired_handles; + size_t paired_handle_count; +} PAIRED_HANDLES; + +extern int umockcallpairs_track_create_paired_call(PAIRED_HANDLES* paired_handles, const void* handle, const char* handle_type, size_t handle_type_size); +extern int umockcallpairs_track_destroy_paired_call(PAIRED_HANDLES* paired_handles, const void* handle); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKCALLPAIRS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockcallrecorder.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKCALLRECORDER_H +#define UMOCKCALLRECORDER_H + +#include "umockcall.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct UMOCKCALLRECORDER_TAG* UMOCKCALLRECORDER_HANDLE; + + extern UMOCKCALLRECORDER_HANDLE umockcallrecorder_create(void); + extern void umockcallrecorder_destroy(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern int umockcallrecorder_reset_all_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern int umockcallrecorder_add_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call); + extern int umockcallrecorder_add_actual_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call); + extern const char* umockcallrecorder_get_actual_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern const char* umockcallrecorder_get_expected_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern UMOCKCALL_HANDLE umockcallrecorder_get_last_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern UMOCKCALLRECORDER_HANDLE umockcallrecorder_clone(UMOCKCALLRECORDER_HANDLE umock_call_recorder); + extern int umockcallrecorder_get_expected_call_count(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t* expected_call_count); + extern int umockcallrecorder_fail_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t index); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKCALLRECORDER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umockstring.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKSTRING_H +#define UMOCKSTRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern char* umockstring_clone(const char* source); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKSTRING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypename.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPENAME_H +#define UMOCKTYPENAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern char* umocktypename_normalize(const char* type_name); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPENAME_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypes.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPES_H +#define UMOCKTYPES_H + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif + +#include "umockalloc.h" + + typedef char*(*UMOCKTYPE_STRINGIFY_FUNC)(const void* value); + typedef int(*UMOCKTYPE_COPY_FUNC)(void* destination, const void* source); + typedef void(*UMOCKTYPE_FREE_FUNC)(void* value); + typedef int(*UMOCKTYPE_ARE_EQUAL_FUNC)(const void* left, const void* right); + + extern int umocktypes_init(void); + extern void umocktypes_deinit(void); + extern int umocktypes_register_type(const char* type, UMOCKTYPE_STRINGIFY_FUNC stringify_func, UMOCKTYPE_ARE_EQUAL_FUNC are_equal_func, UMOCKTYPE_COPY_FUNC copy_func, UMOCKTYPE_FREE_FUNC free_func); + extern int umocktypes_register_alias_type(const char* type, const char* alias_type); + + extern char* umocktypes_stringify(const char* type, const void* value); + extern int umocktypes_are_equal(const char* type, const void* left, const void* right); + extern int umocktypes_copy(const char* type, void* destination, const void* source); + extern void umocktypes_free(const char* type, void* value); + + /* This is a convenience macro that allows registering a type by simply specifying the name and a function_postfix*/ +#define REGISTER_TYPE(type, function_postfix) \ + umocktypes_register_type(TOSTRING(type), (UMOCKTYPE_STRINGIFY_FUNC)C2(umocktypes_stringify_, function_postfix), \ + (UMOCKTYPE_ARE_EQUAL_FUNC)C2(umocktypes_are_equal_,function_postfix), \ + (UMOCKTYPE_COPY_FUNC)C2(umocktypes_copy_,function_postfix), \ + (UMOCKTYPE_FREE_FUNC)C2(umocktypes_free_,function_postfix)) + +/* Codes_SRS_UMOCK_C_LIB_01_181: [ If a value that is not part of the enum is used, it shall be treated as an int value. ]*/ +#define IMPLEMENT_UMOCK_C_ENUM_STRINGIFY(type, ...) \ + UMOCK_STATIC char* C2(umocktypes_stringify_,type)(const type* value) \ + { \ + char* result; \ + static const char *C2(enum_name,_strings)[]= \ + { \ + FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT_AS_STRING, __VA_ARGS__) \ + }; \ + if (value == NULL) \ + { \ + result = NULL; \ + } \ + else \ + { \ + if ((int)*value < (int)(sizeof(C2(enum_name,_strings)) / sizeof(C2(enum_name,_strings)[0]))) \ + { \ + size_t length = strlen(C2(enum_name_, strings)[*value]); \ + if (length == 0) \ + { \ + result = NULL; \ + } \ + else \ + { \ + result = (char*)umockalloc_malloc(length + 1); \ + if (result != NULL) \ + { \ + (void)memcpy(result, C2(enum_name_, strings)[*value], length + 1); \ + } \ + } \ + } \ + else \ + { \ + result = (char*)umockalloc_malloc(64); \ + if (result != NULL) \ + { \ + (void)sprintf(result, "%d", (int)*value); \ + } \ + } \ + } \ + return result; \ + } + +#define IMPLEMENT_UMOCK_C_ENUM_ARE_EQUAL(type) \ + UMOCK_STATIC int C2(umocktypes_are_equal_,type)(const type* left, const type* right) \ + { \ + int result; \ + if ((left == NULL) || (right == NULL)) \ + { \ + result = -1; \ + } \ + else \ + { \ + result = ((*left) == (*right)) ? 1 : 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_UMOCK_C_ENUM_COPY(type) \ + UMOCK_STATIC int C2(umocktypes_copy_,type)(type* destination, const type* source) \ + { \ + int result; \ + if ((destination == NULL) || \ + (source == NULL)) \ + { \ + result = __LINE__; \ + } \ + else \ + { \ + *destination = *source; \ + result = 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_UMOCK_C_ENUM_FREE(type) \ + UMOCK_STATIC void C2(umocktypes_free_,type)(type* value) \ + { \ + (void)value; \ + } + +/* Codes_SRS_UMOCK_C_LIB_01_179: [ IMPLEMENT_UMOCK_C_ENUM_TYPE shall implement umock_c handlers for an enum type. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_180: [ The variable arguments are a list making up the enum values. ]*/ +#define IMPLEMENT_UMOCK_C_ENUM_TYPE(type, ...) \ + IMPLEMENT_UMOCK_C_ENUM_STRINGIFY(type, __VA_ARGS__) \ + IMPLEMENT_UMOCK_C_ENUM_ARE_EQUAL(type) \ + IMPLEMENT_UMOCK_C_ENUM_COPY(type) \ + IMPLEMENT_UMOCK_C_ENUM_FREE(type) + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypes_bool.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPES_BOOL_H +#define UMOCKTYPES_BOOL_H + +#include "macro_utils.h" + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif + + extern int umocktypes_bool_register_types(void); + + extern char* umocktypes_stringify_bool(const bool* value); \ + extern int umocktypes_are_equal_bool(const bool* left, const bool* right); \ + extern int umocktypes_copy_bool(bool* destination, const bool* source); \ + extern void umocktypes_free_bool(bool* value); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPES_BOOL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypes_c.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPES_C_H +#define UMOCKTYPES_C_H + +#include "macro_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + + extern int umocktypes_c_register_types(void); + +#define UMOCKTYPES_HANDLERS(type, function_postfix) \ + extern char* C2(umocktypes_stringify_,function_postfix)(type* value); \ + extern int C2(umocktypes_are_equal_, function_postfix)(type* left, type* right); \ + extern int C2(umocktypes_copy_, function_postfix)(type* destination, type* source); \ + extern void C2(umocktypes_free_, function_postfix)(type* value); + +UMOCKTYPES_HANDLERS(char, char) +UMOCKTYPES_HANDLERS(unsigned char, unsignedchar) +UMOCKTYPES_HANDLERS(short, short) +UMOCKTYPES_HANDLERS(unsigned short, unsignedshort) +UMOCKTYPES_HANDLERS(int, int) +UMOCKTYPES_HANDLERS(unsigned int, unsignedint) +UMOCKTYPES_HANDLERS(long, long) +UMOCKTYPES_HANDLERS(unsigned long, unsignedlong) +UMOCKTYPES_HANDLERS(long long, longlong) +UMOCKTYPES_HANDLERS(unsigned long long, unsignedlonglong) +UMOCKTYPES_HANDLERS(float, float) +UMOCKTYPES_HANDLERS(double, double) +UMOCKTYPES_HANDLERS(long double, longdouble) +UMOCKTYPES_HANDLERS(size_t, size_t) +UMOCKTYPES_HANDLERS(void*, void_ptr) + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPES_C_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypes_charptr.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPES_CHARPTR_H +#define UMOCKTYPES_CHARPTR_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* Codes_SRS_UMOCK_C_LIB_01_067: [char\* and const char\* shall be supported out of the box through a separate header, umockvalue_charptr.h.]*/ + /* Codes_SRS_UMOCK_C_LIB_01_069: [The signature shall be: ...*/ + extern int umocktypes_charptr_register_types(void); + + extern char* umocktypes_stringify_charptr(const char** value); + extern int umocktypes_are_equal_charptr(const char** left, const char** right); + extern int umocktypes_copy_charptr(char** destination, const char** source); + extern void umocktypes_free_charptr(char** value); + + extern char* umocktypes_stringify_const_charptr(const char** value); + extern int umocktypes_are_equal_const_charptr(const char** left, const char** right); + extern int umocktypes_copy_const_charptr(const char** destination, const char** source); + extern void umocktypes_free_const_charptr(const char** value); + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPES_CHARPTR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/inc/umocktypes_stdint.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UMOCKTYPES_STDINT_H +#define UMOCKTYPES_STDINT_H + +#include "macro_utils.h" +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#endif + + extern int umocktypes_stdint_register_types(void); + +#define UMOCKTYPES_STDINT_HANDLERS(type, function_postfix) \ + extern char* C2(umocktypes_stringify_,function_postfix)(type* value); \ + extern int C2(umocktypes_are_equal_, function_postfix)(type* left, type* right); \ + extern int C2(umocktypes_copy_, function_postfix)(type* destination, type* source); \ + extern void C2(umocktypes_free_, function_postfix)(type* value); + +UMOCKTYPES_STDINT_HANDLERS(uint8_t, uint8_t) +UMOCKTYPES_STDINT_HANDLERS(int8_t, int8_t) +UMOCKTYPES_STDINT_HANDLERS(uint16_t, uint16_t) +UMOCKTYPES_STDINT_HANDLERS(int16_t, int16_t) +UMOCKTYPES_STDINT_HANDLERS(uint32_t, uint32_t) +UMOCKTYPES_STDINT_HANDLERS(int32_t, int32_t) +UMOCKTYPES_STDINT_HANDLERS(uint64_t, uint64_t) +UMOCKTYPES_STDINT_HANDLERS(int64_t, int64_t) + +#ifdef __cplusplus +} +#endif + +#endif /* UMOCKTYPES_STDINT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umock_c.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,279 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "umock_c.h" +#include "umockcall.h" +#include "umocktypes.h" +#include "umocktypes_c.h" +#include "umockcallrecorder.h" +#include "umock_log.h" + +typedef enum UMOCK_C_STATE_TAG +{ + UMOCK_C_STATE_NOT_INITIALIZED, + UMOCK_C_STATE_INITIALIZED +} UMOCK_C_STATE; + +static ON_UMOCK_C_ERROR on_umock_c_error_function; +static UMOCK_C_STATE umock_c_state = UMOCK_C_STATE_NOT_INITIALIZED; +static UMOCKCALLRECORDER_HANDLE umock_call_recorder = NULL; + +int umock_c_init(ON_UMOCK_C_ERROR on_umock_c_error) +{ + int result; + + if (umock_c_state != UMOCK_C_STATE_NOT_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_007: [ umock_c_init when umock is already initialized shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umock_c: umock_c already initialized."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_LIB_01_144: [ Out of the box umock_c shall support the following types through the header umocktypes_c.h: ]*/ + /* Codes_SRS_UMOCK_C_LIB_01_028: [**char**] */ + /* Codes_SRS_UMOCK_C_LIB_01_029 : [**unsigned char**] */ + /* Codes_SRS_UMOCK_C_LIB_01_030 : [**short**] */ + /* Codes_SRS_UMOCK_C_LIB_01_031 : [**unsigned short**] */ + /* Codes_SRS_UMOCK_C_LIB_01_032 : [**int**] */ + /* Codes_SRS_UMOCK_C_LIB_01_033 : [**unsigned int**] */ + /* Codes_SRS_UMOCK_C_LIB_01_034 : [**long**] */ + /* Codes_SRS_UMOCK_C_LIB_01_035 : [**unsigned long**] */ + /* Codes_SRS_UMOCK_C_LIB_01_036 : [**long long**] */ + /* Codes_SRS_UMOCK_C_LIB_01_037 : [**unsigned long long**] */ + /* Codes_SRS_UMOCK_C_LIB_01_038 : [**float**] */ + /* Codes_SRS_UMOCK_C_LIB_01_039 : [**double**] */ + /* Codes_SRS_UMOCK_C_LIB_01_040 : [**long double**] */ + /* Codes_SRS_UMOCK_C_LIB_01_041 : [**size_t**] */ + /* Codes_SRS_UMOCK_C_LIB_01_151: [ void\* ]*/ + /* Codes_SRS_UMOCK_C_LIB_01_152: [ const void\* ]*/ + /* Codes_SRS_UMOCK_C_01_023: [ umock_c_init shall initialize the umock types by calling umocktypes_init. ]*/ + if ((umocktypes_init() != 0) || + /* Codes_SRS_UMOCK_C_01_002: [ umock_c_init shall register the C naive types by calling umocktypes_c_register_types. ]*/ + (umocktypes_c_register_types() != 0)) + { + /* Codes_SRS_UMOCK_C_01_005: [ If any of the calls fails, umock_c_init shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umock_c: Could not register standard C types with umock_c."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_01_003: [ umock_c_init shall create a call recorder by calling umockcallrecorder_create. ]*/ + umock_call_recorder = umockcallrecorder_create(); + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_01_005: [ If any of the calls fails, umock_c_init shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umock_c: Could not create the call recorder."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_01_024: [ on_umock_c_error shall be optional. ]*/ + /* Codes_SRS_UMOCK_C_01_006: [ The on_umock_c_error callback shall be stored to be used for later error callbacks. ]*/ + on_umock_c_error_function = on_umock_c_error; + + /* Codes_SRS_UMOCK_C_01_001: [umock_c_init shall initialize the umock library.] */ + umock_c_state = UMOCK_C_STATE_INITIALIZED; + + /* Codes_SRS_UMOCK_C_01_004: [ On success, umock_c_init shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +void umock_c_deinit(void) +{ + /* Codes_SRS_UMOCK_C_LIB_01_012: [If umock_c was not initialized, umock_c_deinit shall do nothing.] */ + /* Codes_SRS_UMOCK_C_01_010: [ If the module is not initialized, umock_c_deinit shall do nothing. ] */ + if (umock_c_state == UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_009: [ umock_c_deinit shall free the call recorder created in umock_c_init. ]*/ + umockcallrecorder_destroy(umock_call_recorder); + umock_c_state = UMOCK_C_STATE_NOT_INITIALIZED; + + /* Codes_SRS_UMOCK_C_01_008: [ umock_c_deinit shall deinitialize the umock types by calling umocktypes_deinit. ]*/ + umocktypes_deinit(); + } +} + +void umock_c_reset_all_calls(void) +{ + /* Codes_SRS_UMOCK_C_01_012: [ If the module is not initialized, umock_c_reset_all_calls shall do nothing. ]*/ + if (umock_c_state == UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_011: [ umock_c_reset_all_calls shall reset all calls by calling umockcallrecorder_reset_all_calls on the call recorder created in umock_c_init. ]*/ + if (umockcallrecorder_reset_all_calls(umock_call_recorder) != 0) + { + /* Codes_SRS_UMOCK_C_01_025: [ If the underlying umockcallrecorder_reset_all_calls fails, the on_umock_c_error callback shall be triggered with UMOCK_C_RESET_CALLS_ERROR. ]*/ + umock_c_indicate_error(UMOCK_C_RESET_CALLS_ERROR); + } + } +} + +int umock_c_add_expected_call(UMOCKCALL_HANDLE mock_call) +{ + int result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_020: [ If the module is not initialized, umock_c_add_expected_call shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c: Cannot add an expected call, umock_c not initialized."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_01_019: [ umock_c_add_expected_call shall add an expected call by calling umockcallrecorder_add_expected_call on the call recorder created in umock_c_init. ]*/ + result = umockcallrecorder_add_expected_call(umock_call_recorder, mock_call); + } + + return result; +} + +int umock_c_add_actual_call(UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call) +{ + int result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_022: [ If the module is not initialized, umock_c_add_actual_call shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c: Cannot add an actual call, umock_c not initialized."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_01_021: [ umock_c_add_actual_call shall add an actual call by calling umockcallrecorder_add_actual_call on the call recorder created in umock_c_init. ]*/ + result = umockcallrecorder_add_actual_call(umock_call_recorder, mock_call, matched_call); + } + + return result; +} + +const char* umock_c_get_expected_calls(void) +{ + const char* result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_016: [ If the module is not initialized, umock_c_get_expected_calls shall return NULL. ]*/ + UMOCK_LOG("umock_c: Cannot get the expected calls, umock_c not initialized."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCK_C_01_015: [ umock_c_get_expected_calls shall return the string for the recorded expected calls by calling umockcallrecorder_get_expected_calls on the call recorder created in umock_c_init. ]*/ + result = umockcallrecorder_get_expected_calls(umock_call_recorder); + } + + return result; +} + +const char* umock_c_get_actual_calls(void) +{ + const char* result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_014: [ If the module is not initialized, umock_c_get_actual_calls shall return NULL. ]*/ + UMOCK_LOG("umock_c: Cannot get the actual calls, umock_c not initialized."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCK_C_01_013: [ umock_c_get_actual_calls shall return the string for the recorded actual calls by calling umockcallrecorder_get_actual_calls on the call recorder created in umock_c_init. ]*/ + result = umockcallrecorder_get_actual_calls(umock_call_recorder); + } + + return result; +} + +UMOCKCALL_HANDLE umock_c_get_last_expected_call(void) +{ + UMOCKCALL_HANDLE result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_018: [ If the module is not initialized, umock_c_get_last_expected_call shall return NULL. ]*/ + UMOCK_LOG("umock_c: Cannot get the last expected call, umock_c not initialized."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCK_C_01_017: [ umock_c_get_last_expected_call shall return the last expected call by calling umockcallrecorder_get_last_expected_call on the call recorder created in umock_c_init. ]*/ + result = umockcallrecorder_get_last_expected_call(umock_call_recorder); + } + + return result; +} + +void umock_c_indicate_error(UMOCK_C_ERROR_CODE error_code) +{ + if (on_umock_c_error_function != NULL) + { + on_umock_c_error_function(error_code); + } +} + +UMOCKCALLRECORDER_HANDLE umock_c_get_call_recorder(void) +{ + UMOCKCALLRECORDER_HANDLE result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_027: [ If the module is not initialized, umock_c_get_call_recorder shall return NULL. ]*/ + UMOCK_LOG("umock_c_get_call_recorder: Cannot get the call recorder, umock_c not initialized."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCK_C_01_026: [ umock_c_get_call_recorder shall return the handle to the currently used call recorder. ]*/ + result = umock_call_recorder; + } + + return result; +} + +int umock_c_set_call_recorder(UMOCKCALLRECORDER_HANDLE call_recorder) +{ + int result; + + if (umock_c_state != UMOCK_C_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_01_033: [ If the module is not initialized, umock_c_set_call_recorder shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c_set_call_recorder: Cannot set the call recorder, umock_c not initialized."); + result = __LINE__; + } + else if (call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_01_030: [ If call_recorder is NULL, umock_c_set_call_recorder shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c_set_call_recorder: NULL call_recorder."); + result = __LINE__; + } + else + { + UMOCKCALLRECORDER_HANDLE new_call_recorder; + + /* Codes_SRS_UMOCK_C_01_028: [ umock_c_set_call_recorder shall replace the currently used call recorder with the one identified by the call_recorder argument. ]*/ + /* Codes_SRS_UMOCK_C_01_031: [ umock_c_set_call_recorder shall make a copy of call_recorder by calling umockcallrecorder_clone and use the copy for future actions. ]*/ + new_call_recorder = umockcallrecorder_clone(call_recorder); + if (new_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_01_032: [ If umockcallrecorder_clone fails, umock_c_set_call_recorder shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c_set_call_recorder: Failed cloning call recorder."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_01_034: [ The previously used call recorder shall be destroyed by calling umockcallrecorder_destroy. ]*/ + umockcallrecorder_destroy(umock_call_recorder); + umock_call_recorder = new_call_recorder; + + /* Codes_SRS_UMOCK_C_01_029: [ On success, umock_c_set_call_recorder shall return 0. ]*/ + result = 0; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umock_c_negative_tests.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include "umock_c_negative_tests.h" +#include "umock_c.h" +#include "umockcall.h" +#include "umockcallrecorder.h" +#include "umock_log.h" + +static UMOCKCALLRECORDER_HANDLE snapshot_call_recorder; +typedef enum UMOCK_C_NEGATIVE_TESTS_STATE_TAG +{ + UMOCK_C_NEGATIVE_TESTS_STATE_NOT_INITIALIZED, + UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED +} UMOCK_C_NEGATIVE_TESTS_STATE; + +static UMOCK_C_NEGATIVE_TESTS_STATE umock_c_negative_tests_state = UMOCK_C_NEGATIVE_TESTS_STATE_NOT_INITIALIZED; + +int umock_c_negative_tests_init(void) +{ + int result; + + if (umock_c_negative_tests_state != UMOCK_C_NEGATIVE_TESTS_STATE_NOT_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_005: [ If the module has already been initialized, umock_c_negative_tests_init shall return a non-zero value. ]*/ + UMOCK_LOG("umock_c_negative_tests_init: Already initialized."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_004: [ On success, umock_c_negative_tests_init shall return 0. ]*/ + umock_c_negative_tests_state = UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED; + result = 0; + } + + return result; +} + +void umock_c_negative_tests_deinit(void) +{ + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_003: [ If the module was not previously initialized, umock_c_negative_tests_deinit shall do nothing. ]*/ + if (umock_c_negative_tests_state == UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_002: [ umock_c_negative_tests_deinit shall free all resources associated with the negative tests module. ]*/ + if (snapshot_call_recorder != NULL) + { + umockcallrecorder_destroy(snapshot_call_recorder); + snapshot_call_recorder = NULL; + } + + umock_c_negative_tests_state = UMOCK_C_NEGATIVE_TESTS_STATE_NOT_INITIALIZED; + } +} + +/* Codes_SRS_UMOCK_C_LIB_01_167: [ umock_c_negative_tests_snapshot shall take a snapshot of the current setup of expected calls (a.k.a happy path). ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_168: [ If umock_c_negative_tests_snapshot is called without the module being initialized, it shall do nothing. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_169: [ All errors shall be reported by calling the umock_c on error function. ]*/ +/* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_006: [ umock_c_negative_tests_snapshot shall make a copy of the current call recorder for umock_c with all its recorded calls. ]*/ +void umock_c_negative_tests_snapshot(void) +{ + if (umock_c_negative_tests_state != UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_015: [ If the module was not previously initialized, umock_c_negative_tests_snapshot shall do nothing. ]*/ + UMOCK_LOG("umock_c_negative_tests_snapshot: Not initialized."); + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_007: [ The current call recorder shall be obtained by calling umock_c_get_call_recorder. ]*/ + UMOCKCALLRECORDER_HANDLE current_call_recorder = umock_c_get_call_recorder(); + if (current_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_009: [ If getting the call recorder fails, umock_c_negative_tests_snapshot shall indicate the error via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_snapshot: Error getting the current call recorder."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_008: [ The copy of the recorder shall be made by calling umockcallrecorder_clone. ]*/ + UMOCKCALLRECORDER_HANDLE new_call_recorder = umockcallrecorder_clone(current_call_recorder); + if (new_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_010: [ If copying the call recorder fails, umock_c_negative_tests_snapshot shall indicate the error via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_snapshot: Error cloning the call recorder."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + else + { + if (snapshot_call_recorder != NULL) + { + umockcallrecorder_destroy(snapshot_call_recorder); + } + + snapshot_call_recorder = new_call_recorder; + } + } + } +} + +/* Codes_SRS_UMOCK_C_LIB_01_170: [ umock_c_negative_tests_reset shall bring umock_c expected and actual calls to the state recorded when umock_c_negative_tests_snapshot was called. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_171: [ If umock_c_negative_tests_reset is called without the module being initialized, it shall do nothing. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_172: [ All errors shall be reported by calling the umock_c on error function. ]*/ +void umock_c_negative_tests_reset(void) +{ + if (umock_c_negative_tests_state != UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_016: [ If the module was not previously initialized, umock_c_negative_tests_reset shall do nothing. ]*/ + UMOCK_LOG("umock_c_negative_tests_reset: Not initialized."); + } + else + { + if (snapshot_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_012: [ If no call has been made to umock_c_negative_tests_snapshot, umock_c_negative_tests_reset shall indicate a failure via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_reset: No snapshot was done, use umock_c_negative_tests_snapshot."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_011: [ umock_c_negative_tests_reset shall reset the call recorder used by umock_c to the call recorder stored in umock_c_negative_tests_reset. ]*/ + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_013: [ The reset shall be done by calling umock_c_set_call_recorder and passing the call recorder stored in umock_c_negative_tests_reset as argument. ]*/ + if (umock_c_set_call_recorder(snapshot_call_recorder) != 0) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_014: [ If umock_c_set_call_recorder fails, umock_c_negative_tests_reset shall indicate a failure via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_reset: Failed setting the call recorder to the snapshot one."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + } + } +} + +/* Codes_SRS_UMOCK_C_LIB_01_173: [ umock_c_negative_tests_fail_call shall instruct the negative tests module to fail a specific call. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_174: [ If umock_c_negative_tests_fail_call is called without the module being initialized, it shall do nothing. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_175: [ All errors shall be reported by calling the umock_c on error function. ]*/ +void umock_c_negative_tests_fail_call(size_t index) +{ + if (umock_c_negative_tests_state != UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_020: [ If the module was not previously initialized, umock_c_negative_tests_fail_call shall do nothing. ]*/ + UMOCK_LOG("umock_c_negative_tests_fail_call: Not initialized."); + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_017: [ umock_c_negative_tests_fail_call shall call umockcallrecorder_fail_call on the currently used call recorder used by umock_c. ]*/ + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_018: [ The currently used recorder shall be obtained by calling umock_c_get_call_recorder. ]*/ + UMOCKCALLRECORDER_HANDLE call_recorder = umock_c_get_call_recorder(); + if (call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_019: [ If umock_c_get_call_recorder fails, umock_c_negative_tests_fail_call shall indicate the error via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_fail_call: Cannot get call recorder."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + else + { + if (umockcallrecorder_fail_call(call_recorder, index) != 0) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_025: [ If failing the call by calling umockcallrecorder_fail_call fails, umock_c_negative_tests_fail_call shall indicate the error via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_fail_call: Cannot get call recorder."); + umock_c_indicate_error(UMOCK_C_ERROR); + } + } + } +} + +/* Codes_SRS_UMOCK_C_LIB_01_176: [ umock_c_negative_tests_call_count shall provide the number of expected calls, so that the test code can iterate through all negative cases. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_177: [ If umock_c_negative_tests_fail_call is called without the module being initialized, it shall return 0. ]*/ +/* Codes_SRS_UMOCK_C_LIB_01_178: [ All errors shall be reported by calling the umock_c on error function. ]*/ +size_t umock_c_negative_tests_call_count(void) +{ + size_t result; + + if (umock_c_negative_tests_state != UMOCK_C_NEGATIVE_TESTS_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_024: [ If the module was not previously initialized, umock_c_negative_tests_call_count shall return 0. ]*/ + UMOCK_LOG("umock_c_negative_tests_call_count: Not initialized."); + result = 0; + } + else + { + if (snapshot_call_recorder == NULL) + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_022: [ If no call has been made to umock_c_negative_tests_snapshot, umock_c_negative_tests_fail_call shall return 0 and indicate the error via the umock error callback with error code UMOCK_C_ERROR. ]*/ + UMOCK_LOG("umock_c_negative_tests_call_count: No snapshot, use umock_c_negative_tests_snapshot."); + umock_c_indicate_error(UMOCK_C_ERROR); + result = 0; + } + else + { + /* Codes_SRS_UMOCK_C_NEGATIVE_TESTS_01_021: [ umock_c_negative_tests_call_count shall return the count of expected calls for the current snapshot call recorder by calling umockcallrecorder_get_expected_call_count. ]*/ + if (umockcallrecorder_get_expected_call_count(snapshot_call_recorder, &result) != 0) + { + UMOCK_LOG("umock_c_negative_tests_call_count: Getting the expected call count from the recorder failed."); + umock_c_indicate_error(UMOCK_C_ERROR); + result = 0; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umock_log.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,19 @@ + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdio.h> +#include <stdarg.h> + +#include "umock_log.h" +#include "umock_c_internal.h" + +void UMOCK_LOG(const char* format, ...) +{ + va_list params; + va_start(params, format); + (void)vprintf(format, params); + va_end(params); + printf("\r\n"); + umock_c_indicate_error(UMOCK_C_ERROR); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockalloc.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include "umockalloc.h" + +void* umockalloc_malloc(size_t size) +{ + /* Codes_SRS_UMOCKALLOC_01_001: [ umockalloc_malloc shall call malloc, while passing the size argument to malloc. ] */ + /* Codes_SRS_UMOCKALLOC_01_002: [ umockalloc_malloc shall return the result of malloc. ]*/ + return malloc(size); +} + +void* umockalloc_realloc(void* ptr, size_t size) +{ + /* Codes_SRS_UMOCKALLOC_01_003: [ umockalloc_realloc shall call realloc, while passing the ptr and size arguments to realloc. ] */ + /* Codes_SRS_UMOCKALLOC_01_004: [ umockalloc_realloc shall return the result of realloc. ]*/ + return realloc(ptr, size); +} + +void umockalloc_free(void* ptr) +{ + /* Codes_SRS_UMOCKALLOC_01_005: [ umockalloc_free shall call free, while passing the ptr argument to free. ]*/ + free(ptr); +} + +char* umockc_stringify_buffer(const void* bytes, size_t length) +{ + size_t string_length = 2 + (4 * length); + char* result; + if (length > 1) + { + string_length += length - 1; + } + + result = (char*)umockalloc_malloc(string_length + 1); + if (result != NULL) + { + size_t i; + + result[0] = '['; + for (i = 0; i < length; i++) + { + if (sprintf(result + 1 + (i * 5), "0x%02X ", ((const unsigned char*)bytes)[i]) < 0) + { + break; + } + } + + if (i < length) + { + umockalloc_free(result); + result = NULL; + } + else + { + result[string_length - 1] = ']'; + result[string_length] = '\0'; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockautoignoreargs.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include <string.h> +#include <ctype.h> +#include "umockalloc.h" +#include "umockautoignoreargs.h" +#include "umock_log.h" + +static const char ignore_ptr_arg_string[] = "IGNORED_PTR_ARG"; +static const char ignore_num_arg_string[] = "IGNORED_NUM_ARG"; + +#define PARSER_STACK_DEPTH 256 + +/* Codes_SRS_UMOCKAUTOIGNOREARGS_01_001: [ `umockautoignoreargs_is_call_argument_ignored` shall determine whether argument `argument_index` shall be ignored or not. ]*/ +int umockautoignoreargs_is_call_argument_ignored(const char* call, size_t argument_index, int* is_argument_ignored) +{ + int result; + + if ((call == NULL) || + (is_argument_ignored == NULL)) + { + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_002: [ If `call` or `is_argument_ignored` is NULL, `umockautoignoreargs_is_call_argument_ignored` shall fail and return a non-zero value. ]*/ + result = __LINE__; + } + else + { + const char* cur_pos = strchr(call, '('); + + if (cur_pos == NULL) + { + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_004: [ If `umockautoignoreargs_is_call_argument_ignored` fails parsing the `call` argument it shall fail and return a non-zero value. ]*/ + result = __LINE__; + } + else + { + size_t argument_count = 1; + unsigned char argument_found = 0; + + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_009: [ If the number of arguments parsed from `call` is less than `argument_index`, `umockautoignoreargs_is_call_argument_ignored` shall fail and return a non-zero value. ]*/ + cur_pos++; + + while (*cur_pos != '\0') + { + /* clear out all spaces */ + while ((*cur_pos != '\0') && isspace(*cur_pos)) + { + cur_pos++; + } + + if (*cur_pos != '\0') + { + if (*cur_pos == ')') + { + /* no more args and we did not get to the arg we wanted */ + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_009: [ If the number of arguments parsed from `call` is less than `argument_index`, `umockautoignoreargs_is_call_argument_ignored` shall fail and return a non-zero value. ]*/ + result = __LINE__; + break; + } + else + { + if (argument_count == argument_index) + { + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_006: [ If the argument value is `IGNORED_PTR_ARG` then `is_argument_ignored` shall be set to 1. ]*/ + if ((strncmp(cur_pos, ignore_ptr_arg_string, sizeof(ignore_ptr_arg_string) - 1) == 0) || + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_007: [ If the argument value is `IGNORED_NUM_ARG` then `is_argument_ignored` shall be set to 1. ]*/ + (strncmp(cur_pos, ignore_num_arg_string, sizeof(ignore_num_arg_string) - 1) == 0)) + { + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_005: [ If `umockautoignoreargs_is_call_argument_ignored` was able to parse the `argument_index`th argument it shall succeed and return 0, while writing whether the argument is ignored in the `is_argument_ignored` output argument. ]*/ + *is_argument_ignored = 1; + } + else + { + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_008: [ If the argument value is any other value then `is_argument_ignored` shall be set to 0. ]*/ + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_005: [ If `umockautoignoreargs_is_call_argument_ignored` was able to parse the `argument_index`th argument it shall succeed and return 0, while writing whether the argument is ignored in the `is_argument_ignored` output argument. ]*/ + *is_argument_ignored = 0; + } + + argument_found = 1; + break; + } + else + { + unsigned char parser_stack[PARSER_STACK_DEPTH]; + size_t parser_stack_index = 0; + int parse_error = 0; + + /* Codes_SRS_UMOCKAUTOIGNOREARGS_01_003: [ `umockautoignoreargs_is_call_argument_ignored` shall parse the `call` string as a function call: function_name(arg1, arg2, ...). ]*/ + while ((*cur_pos != '\0') && + ((*cur_pos != ',') || (parser_stack_index > 0))) + { + if (*cur_pos == '(') + { + if (parser_stack_index == PARSER_STACK_DEPTH) + { + /* error*/ + parse_error = 1; + break; + } + else + { + parser_stack[parser_stack_index] = 1; + parser_stack_index++; + } + } + if (*cur_pos == ')') + { + if (parser_stack_index == 0) + { + /* error*/ + parse_error = 1; + break; + } + else + { + if (parser_stack[parser_stack_index - 1] == 1) + { + parser_stack_index--; + } + } + } + if (*cur_pos == '{') + { + if (parser_stack_index == PARSER_STACK_DEPTH) + { + /* error*/ + parse_error = 1; + break; + } + else + { + parser_stack[parser_stack_index] = 2; + parser_stack_index++; + } + } + if (*cur_pos == '}') + { + if (parser_stack_index == 0) + { + /* error*/ + parse_error = 1; + break; + } + else + { + if (parser_stack[parser_stack_index - 1] == 2) + { + parser_stack_index--; + } + } + } + + cur_pos++; + } + + if ((*cur_pos == '\0') || + parse_error) + { + break; + } + else + { + argument_count++; + cur_pos++; + } + } + } + } + } + + if (argument_found) + { + result = 0; + } + else + { + result = __LINE__; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockcall.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,374 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include <string.h> +#include "umockcall.h" +#include "umockalloc.h" +#include "umock_log.h" + +typedef struct UMOCKCALL_TAG +{ + char* function_name; + void* umockcall_data; + UMOCKCALL_DATA_COPY_FUNC umockcall_data_copy; + UMOCKCALL_DATA_FREE_FUNC umockcall_data_free; + UMOCKCALL_DATA_STRINGIFY_FUNC umockcall_data_stringify; + UMOCKCALL_DATA_ARE_EQUAL_FUNC umockcall_data_are_equal; + unsigned int fail_call : 1; + unsigned int ignore_all_calls : 1; +} UMOCKCALL; + +UMOCKCALL_HANDLE umockcall_create(const char* function_name, void* umockcall_data, UMOCKCALL_DATA_COPY_FUNC umockcall_data_copy, UMOCKCALL_DATA_FREE_FUNC umockcall_data_free, UMOCKCALL_DATA_STRINGIFY_FUNC umockcall_data_stringify, UMOCKCALL_DATA_ARE_EQUAL_FUNC umockcall_data_are_equal) +{ + UMOCKCALL* result; + + if ((function_name == NULL) || + (umockcall_data == NULL) || + (umockcall_data_copy == NULL) || + (umockcall_data_free == NULL) || + (umockcall_data_stringify == NULL) || + (umockcall_data_are_equal == NULL)) + { + /* Codes_SRS_UMOCKCALL_01_003: [ If any of the arguments are NULL, umockcall_create shall fail and return NULL. ] */ + UMOCK_LOG("umockcall: Cannot create call, invalid arguments: function_name = %p, umockcall_data = %p, umockcall_data_free = %p, umockcall_data_stringify = %p, umockcall_data_are_equal = %p.", + function_name, umockcall_data, umockcall_data_free, umockcall_data_stringify, umockcall_data_are_equal); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALL_01_001: [ umockcall_create shall create a new instance of a umock call and on success it shall return a non-NULL handle to it. ] */ + result = (UMOCKCALL*)umockalloc_malloc(sizeof(UMOCKCALL)); + /* Codes_SRS_UMOCKCALL_01_002: [ If allocating memory for the umock call instance fails, umockcall_create shall return NULL. ] */ + if (result != NULL) + { + size_t function_name_length = strlen(function_name); + result->function_name = (char*)umockalloc_malloc(function_name_length + 1); + if (result->function_name == NULL) + { + /* Codes_SRS_UMOCKCALL_01_002: [ If allocating memory for the umock call instance fails, umockcall_create shall return NULL. ] */ + UMOCK_LOG("umockcall: Cannot allocate memory for the call function name."); + umockalloc_free(result); + result = NULL; + } + else + { + (void)memcpy(result->function_name, function_name, function_name_length + 1); + result->umockcall_data = umockcall_data; + result->umockcall_data_copy = umockcall_data_copy; + result->umockcall_data_free = umockcall_data_free; + result->umockcall_data_stringify = umockcall_data_stringify; + result->umockcall_data_are_equal = umockcall_data_are_equal; + result->fail_call = 0; + result->ignore_all_calls = 0; + } + } + } + + return result; +} + +void umockcall_destroy(UMOCKCALL_HANDLE umockcall) +{ + /* Codes_SRS_UMOCKCALL_01_005: [ If the umockcall argument is NULL then umockcall_destroy shall do nothing. ]*/ + if (umockcall != NULL) + { + /* Codes_SRS_UMOCKCALL_01_004: [ umockcall_destroy shall free a previously allocated umock call instance. ] */ + umockcall->umockcall_data_free(umockcall->umockcall_data); + umockalloc_free(umockcall->function_name); + umockalloc_free(umockcall); + } +} + +/* Codes_SRS_UMOCKCALL_01_006: [ umockcall_are_equal shall compare the two mock calls and return whether they are equal or not. ] */ +int umockcall_are_equal(UMOCKCALL_HANDLE left, UMOCKCALL_HANDLE right) +{ + int result; + + if (left == right) + { + /* Codes_SRS_UMOCKCALL_01_024: [ If both left and right pointers are equal, umockcall_are_equal shall return 1. ] */ + result = 1; + } + else if ((left == NULL) || (right == NULL)) + { + /* Codes_SRS_UMOCKCALL_01_015: [ If only one of the left or right arguments are NULL, umockcall_are_equal shall return 0. ] */ + result = 0; + } + else + { + if (left->umockcall_data_are_equal != right->umockcall_data_are_equal) + { + /* Codes_SRS_UMOCKCALL_01_014: [ If the two calls have different are_equal functions that have been passed to umockcall_create then the calls shall be considered different and 0 shall be returned. ] */ + result = 0; + } + else if (strcmp(left->function_name, right->function_name) != 0) + { + /* Codes_SRS_UMOCKCALL_01_025: [ If the function name does not match for the 2 calls, umockcall_are_equal shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_UMOCKCALL_01_026: [ The call data shall be evaluated by calling the umockcall_data_are_equal function passed in umockcall_create while passing as arguments the umockcall_data associated with each call handle. ]*/ + switch (left->umockcall_data_are_equal(left->umockcall_data, right->umockcall_data)) + { + default: + /* Codes_SRS_UMOCKCALL_01_029: [ If the underlying umockcall_data_are_equal fails (returns anything else than 0 or 1), then umockcall_are_equal shall fail and return -1. ] */ + UMOCK_LOG("umockcall: comparing call data failed."); + result = -1; + break; + case 1: + /* Codes_SRS_UMOCKCALL_01_027: [ If the underlying umockcall_data_are_equal returns 1, then umockcall_are_equal shall return 1. ]*/ + result = 1; + break; + case 0: + /* Codes_SRS_UMOCKCALL_01_028: [ If the underlying umockcall_data_are_equal returns 0, then umockcall_are_equal shall return 0. ]*/ + result = 0; + break; + } + } + } + + return result; +} + +char* umockcall_stringify(UMOCKCALL_HANDLE umockcall) +{ + char* result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_020: [ If the underlying umockcall_data_stringify call fails, umockcall_stringify shall fail and return NULL. ]*/ + UMOCK_LOG("umockcall: NULL umockcall in stringify."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALL_01_019: [ To obtain the arguments string, umockcall_stringify shall call the umockcall_data_stringify function passed to umockcall_create and pass to it the umockcall_data pointer (also given in umockcall_create). ]*/ + char* stringified_args = umockcall->umockcall_data_stringify(umockcall->umockcall_data); + if (stringified_args == NULL) + { + /* Codes_SRS_UMOCKCALL_01_020: [ If the underlying umockcall_data_stringify call fails, umockcall_stringify shall fail and return NULL. ]*/ + UMOCK_LOG("umockcall: umockcall data stringify failed."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALL_01_016: [ umockcall_stringify shall return a string representation of the mock call in the form \[function_name(arguments)\]. ] */ + size_t function_name_length = strlen(umockcall->function_name); + size_t stringified_args_length = strlen(stringified_args); + + /* 4 because () and [] */ + size_t call_length = function_name_length + stringified_args_length + 4; + + /* Codes_SRS_UMOCKCALL_01_018: [ The returned string shall be a newly allocated string and it is to be freed by the caller. ]*/ + result = (char*)umockalloc_malloc(call_length + 1); + /* Codes_SRS_UMOCKCALL_01_021: [ If not enough memory can be allocated for the string to be returned, umockcall_stringify shall fail and return NULL. ]*/ + if (result != NULL) + { + result[0] = '['; + (void)memcpy(&result[1], umockcall->function_name, function_name_length); + result[function_name_length + 1] = '('; + (void)memcpy(&result[function_name_length + 2], stringified_args, stringified_args_length); + result[function_name_length + stringified_args_length + 2] = ')'; + result[function_name_length + stringified_args_length + 3] = ']'; + result[function_name_length + stringified_args_length + 4] = '\0'; + } + + /* Codes_SRS_UMOCKCALL_01_030: [ umockcall_stringify shall free the string obtained from umockcall_data_stringify. ]*/ + umockalloc_free(stringified_args); + } + } + + return result; +} + +void* umockcall_get_call_data(UMOCKCALL_HANDLE umockcall) +{ + void* umockcall_data; + + if (umockcall == NULL) + { + UMOCK_LOG("umockcall: NULL umockcall in getting call data."); + umockcall_data = NULL; + } + else + { + umockcall_data = umockcall->umockcall_data; + } + + return umockcall_data; +} + +/* Codes_SRS_UMOCKCALL_01_031: [ umockcall_clone shall clone a umock call and on success it shall return a handle to the newly cloned call. ] */ +UMOCKCALL_HANDLE umockcall_clone(UMOCKCALL_HANDLE umockcall) +{ + UMOCKCALL_HANDLE result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_032: [ If umockcall is NULL, umockcall_clone shall return NULL. ]*/ + UMOCK_LOG("umockcall_clone: NULL umockcall in getting call data."); + result = NULL; + } + else + { + result = (UMOCKCALL*)umockalloc_malloc(sizeof(UMOCKCALL)); + if (result == NULL) + { + /* Codes_SRS_UMOCKCALL_01_043: [ If allocating memory for the new umock call fails, umockcall_clone shall return NULL. ]*/ + UMOCK_LOG("umockcall_clone: Failed allocating memory for new copied call."); + } + else + { + size_t function_name_length = strlen(umockcall->function_name); + result->function_name = (char*)umockalloc_malloc(function_name_length + 1); + if (result->function_name == NULL) + { + /* Codes_SRS_UMOCKCALL_01_036: [ If allocating memory for the function name fails, umockcall_clone shall return NULL. ]*/ + UMOCK_LOG("umockcall_clone: Failed allocating memory for new copied call function name."); + umockalloc_free(result); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALL_01_035: [ umockcall_clone shall copy also the function name. ]*/ + (void)memcpy(result->function_name, umockcall->function_name, function_name_length + 1); + + /* Codes_SRS_UMOCKCALL_01_033: [ The call data shall be cloned by calling the umockcall_data_copy function passed in umockcall_create and passing as argument the umockcall_data value passed in umockcall_create. ]*/ + result->umockcall_data = umockcall->umockcall_data_copy(umockcall->umockcall_data); + if (result->umockcall_data == NULL) + { + /* Codes_SRS_UMOCKCALL_01_034: [ If umockcall_data_copy fails then umockcall_clone shall return NULL. ]*/ + UMOCK_LOG("umockcall_clone: Failed copying call data."); + umockalloc_free(result->function_name); + umockalloc_free(result); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALL_01_037: [ umockcall_clone shall also copy all the functions passed to umockcall_create (umockcall_data_copy, umockcall_data_free, umockcall_data_are_equal, umockcall_data_stringify). ]*/ + result->umockcall_data_are_equal = umockcall->umockcall_data_are_equal; + result->umockcall_data_copy = umockcall->umockcall_data_copy; + result->umockcall_data_free = umockcall->umockcall_data_free; + result->umockcall_data_stringify = umockcall->umockcall_data_stringify; + result->ignore_all_calls = umockcall->ignore_all_calls; + result->fail_call = umockcall->fail_call; + } + } + } + } + + return result; +} + +int umockcall_set_fail_call(UMOCKCALL_HANDLE umockcall, int fail_call) +{ + int result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_039: [ If umockcall is NULL, umockcall_set_fail_call shall return a non-zero value. ]*/ + UMOCK_LOG("umockcall_set_fail_call: NULL umockcall."); + result = __LINE__; + } + else + { + switch (fail_call) + { + default: + UMOCK_LOG("umockcall_set_fail_call: Invalid fail_call value: %d.", fail_call); + result = __LINE__; + break; + case 0: + /* Codes_SRS_UMOCKCALL_01_038: [ umockcall_set_fail_call shall store the fail_call value, associating it with the umockcall call instance. ]*/ + umockcall->fail_call = 0; + /* Codes_SRS_UMOCKCALL_01_044: [ On success umockcall_set_fail_call shall return 0. ]*/ + result = 0; + break; + case 1: + /* Codes_SRS_UMOCKCALL_01_038: [ umockcall_set_fail_call shall store the fail_call value, associating it with the umockcall call instance. ]*/ + umockcall->fail_call = 1; + /* Codes_SRS_UMOCKCALL_01_044: [ On success umockcall_set_fail_call shall return 0. ]*/ + result = 0; + break; + } + } + + return result; +} + +int umockcall_get_fail_call(UMOCKCALL_HANDLE umockcall) +{ + int result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_042: [ If umockcall is NULL, umockcall_get_fail_call shall return -1. ]*/ + UMOCK_LOG("NULL umokcall argument."); + result = -1; + } + else + { + /* Codes_SRS_UMOCKCALL_01_041: [ umockcall_get_fail_call shall retrieve the fail_call value, associated with the umockcall call instance. ]*/ + result = umockcall->fail_call ? 1 : 0; + } + + return result; +} + +int umockcall_set_ignore_all_calls(UMOCKCALL_HANDLE umockcall, int ignore_all_calls) +{ + int result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_047: [ If umockcall is NULL, umockcall_set_ignore_all_calls shall return a non-zero value. ]*/ + UMOCK_LOG("umockcall_set_fail_call: NULL umockcall."); + result = __LINE__; + } + else + { + switch (ignore_all_calls) + { + default: + /* Codes_SRS_UMOCKCALL_01_048: [ If a value different than 0 and 1 is passed as ignore_all_calls, umockcall_set_ignore_all_calls shall return a non-zero value. ]*/ + UMOCK_LOG("umockcall_set_fail_call: Invalid ignore_all_calls value: %d.", ignore_all_calls); + result = __LINE__; + break; + case 0: + /* Codes_SRS_UMOCKCALL_01_045: [ umockcall_set_ignore_all_calls shall store the ignore_all_calls value, associating it with the umockcall call instance. ]*/ + umockcall->ignore_all_calls = 0; + /* Codes_SRS_UMOCKCALL_01_046: [ On success umockcall_set_ignore_all_calls shall return 0. ]*/ + result = 0; + break; + case 1: + /* Codes_SRS_UMOCKCALL_01_045: [ umockcall_set_ignore_all_calls shall store the ignore_all_calls value, associating it with the umockcall call instance. ]*/ + umockcall->ignore_all_calls = 1; + /* Codes_SRS_UMOCKCALL_01_046: [ On success umockcall_set_ignore_all_calls shall return 0. ]*/ + result = 0; + break; + } + } + + return result; +} + +int umockcall_get_ignore_all_calls(UMOCKCALL_HANDLE umockcall) +{ + int result; + + if (umockcall == NULL) + { + /* Codes_SRS_UMOCKCALL_01_050: [ If umockcall is NULL, umockcall_get_ignore_all_calls shall return -1. ]*/ + UMOCK_LOG("NULL umokcall argument."); + result = -1; + } + else + { + /* Codes_SRS_UMOCKCALL_01_049: [ umockcall_get_ignore_all_calls shall retrieve the ignore_all_calls value, associated with the umockcall call instance. ]*/ + result = umockcall->ignore_all_calls ? 1 : 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockcallpairs.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <string.h> +#include <stddef.h> +#include "umockcallpairs.h" +#include "umockalloc.h" +#include "umock_log.h" +#include "umocktypes.h" + +int umockcallpairs_track_create_paired_call(PAIRED_HANDLES* paired_handles, const void* handle, const char* handle_type, size_t handle_type_size) +{ + PAIRED_HANDLE* new_paired_handles; + int result; + + if ((paired_handles == NULL) || + (handle == NULL) || + (handle_type == NULL)) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_004: [ If any of the arguments paired_handles, handle or handle_type is NULL, umockcallpairs_track_create_paired_call shallfail and return a non-zero value. ]*/ + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_malloc: NULL paired_handles"); + } + else + { + /* Codes_SRS_UMOCKCALLPAIRS_01_001: [ umockcallpairs_track_create_paired_call shall add a new entry to the PAIRED_HANDLES array and on success it shall return 0. ]*/ + new_paired_handles = (PAIRED_HANDLE*)umockalloc_realloc(paired_handles->paired_handles, sizeof(PAIRED_HANDLE) * (paired_handles->paired_handle_count + 1)); + if (new_paired_handles == NULL) + { + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_malloc: Allocation failed"); + } + else + { + paired_handles->paired_handle_count++; + paired_handles->paired_handles = new_paired_handles; + + /* Codes_SRS_UMOCKCALLPAIRS_01_003: [ umockcallpairs_track_create_paired_call shall allocate a memory block and store a pointer to it in the memory field of the new entry. ]*/ + paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_value = umockalloc_malloc(handle_type_size); + if (paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_value == NULL) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_005: [ If allocating memory fails, umockcallpairs_track_create_paired_call shall fail and return a non-zero value. ]*/ + paired_handles->paired_handle_count--; + if (paired_handles->paired_handle_count == 0) + { + umockalloc_free(paired_handles->paired_handles); + paired_handles->paired_handles = NULL; + } + + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_malloc: Failed allocating memory for handle value for create"); + } + else + { + size_t handle_type_length = strlen(handle_type); + + paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_type = (char*)umockalloc_malloc(handle_type_length + 1); + if (paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_type == NULL) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_005: [ If allocating memory fails, umockcallpairs_track_create_paired_call shall fail and return a non-zero value. ]*/ + umockalloc_free(paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_value); + paired_handles->paired_handle_count--; + if (paired_handles->paired_handle_count == 0) + { + umockalloc_free(paired_handles->paired_handles); + paired_handles->paired_handles = NULL; + } + + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_malloc: Failed allocating memory for handle type for create"); + } + else + { + (void)memcpy(paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_type, handle_type, handle_type_length + 1); + + /* Codes_SRS_UMOCKCALLPAIRS_01_002: [ umockcallpairs_track_create_paired_call shall copy the handle_value to the handle_value member of the new entry. ] */ + /* Codes_SRS_UMOCKCALLPAIRS_01_006: [ The handle value shall be copied by using umocktypes_copy. ]*/ + if (umocktypes_copy(handle_type, paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_value, handle) != 0) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_007: [ If umocktypes_copy fails, umockcallpairs_track_create_paired_call shall fail and return a non-zero value. ]*/ + umockalloc_free(paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_type); + umockalloc_free(paired_handles->paired_handles[paired_handles->paired_handle_count - 1].handle_value); + paired_handles->paired_handle_count--; + if (paired_handles->paired_handle_count == 0) + { + umockalloc_free(paired_handles->paired_handles); + paired_handles->paired_handles = NULL; + } + + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_malloc: Failed copying handle"); + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int umockcallpairs_track_destroy_paired_call(PAIRED_HANDLES* paired_handles, const void* handle) +{ + size_t i; + int result; + + if ((paired_handles == NULL) || + (handle == NULL)) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_010: [ If any of the arguments is NULL, umockcallpairs_track_destroy_paired_call shall fail and return a non-zero value. ]*/ + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_free: are_equal failed"); + } + else + { + unsigned char is_error = 0; + + /* Codes_SRS_UMOCKCALLPAIRS_01_008: [ umockcallpairs_track_destroy_paired_call shall remove from the paired handles array pointed by the paired_handles field the entry that is associated with the handle passed in the handle argument. ]*/ + for (i = 0; i < paired_handles->paired_handle_count; i++) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_013: [ When looking up which entry to remove, the comparison of the handle values shall be done by calling umocktypes_are_equal. ]*/ + int are_equal_result = umocktypes_are_equal(paired_handles->paired_handles[i].handle_type, paired_handles->paired_handles[i].handle_value, handle); + if (are_equal_result < 0) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_014: [ If umocktypes_are_equal fails, umockcallpairs_track_destroy_paired_call shall fail and return a non-zero value. ]*/ + result = __LINE__; + is_error = 1; + UMOCK_LOG("umock_track_create_destroy_paired_calls_free: are_equal failed"); + break; + } + else + { + if (are_equal_result == 1) + { + break; + } + } + } + + if (i == paired_handles->paired_handle_count) + { + result = __LINE__; + UMOCK_LOG("umock_track_create_destroy_paired_calls_free: could not find handle"); + } + else + { + if (!is_error) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_011: [ umockcallpairs_track_destroy_paired_call shall free the memory pointed by the memory field in the PAIRED_HANDLES array entry associated with handle. ]*/ + umocktypes_free(paired_handles->paired_handles[i].handle_type, paired_handles->paired_handles[i].handle_value); + umockalloc_free(paired_handles->paired_handles[i].handle_type); + umockalloc_free(paired_handles->paired_handles[i].handle_value); + (void)memmove(&paired_handles->paired_handles[i], &paired_handles->paired_handles[i + 1], sizeof(PAIRED_HANDLE) * (paired_handles->paired_handle_count - 1)); + paired_handles->paired_handle_count--; + if (paired_handles->paired_handle_count == 0) + { + /* Codes_SRS_UMOCKCALLPAIRS_01_012: [ If the array paired handles array is empty after removing the entry, the paired_handles field shall be freed and set to NULL. ]*/ + umockalloc_free(paired_handles->paired_handles); + paired_handles->paired_handles = NULL; + } + + /* Codes_SRS_UMOCKCALLPAIRS_01_009: [ On success umockcallpairs_track_destroy_paired_call shall return 0. ]*/ + result = 0; + } + else + { + result = __LINE__; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockcallrecorder.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,635 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include <string.h> +#include "umockcallrecorder.h" +#include "umockcall.h" +#include "umocktypes.h" +#include "umockalloc.h" +#include "umock_log.h" + +typedef struct UMOCK_EXPECTED_CALL_TAG +{ + UMOCKCALL_HANDLE umockcall; + unsigned int is_matched : 1; +} UMOCK_EXPECTED_CALL; + +typedef struct UMOCKCALLRECORDER_TAG +{ + size_t expected_call_count; + UMOCK_EXPECTED_CALL* expected_calls; + size_t actual_call_count; + UMOCKCALL_HANDLE* actual_calls; + char* expected_calls_string; + char* actual_calls_string; +} UMOCKCALLRECORDER; + +UMOCKCALLRECORDER_HANDLE umockcallrecorder_create(void) +{ + UMOCKCALLRECORDER_HANDLE result; + + /* Codes_SRS_UMOCKCALLRECORDER_01_001: [ umockcallrecorder_create shall create a new instance of a call recorder and return a non-NULL handle to it on success. ]*/ + result = (UMOCKCALLRECORDER_HANDLE)umockalloc_malloc(sizeof(UMOCKCALLRECORDER)); + /* Codes_SRS_UMOCKCALLRECORDER_01_002: [ If allocating memory for the call recorder fails, umockcallrecorder_create shall return NULL. ]*/ + if (result != NULL) + { + result->expected_call_count = 0; + result->expected_calls = NULL; + result->expected_calls_string = NULL; + result->actual_call_count = 0; + result->actual_calls = NULL; + result->actual_calls_string = NULL; + } + + return result; +} + +void umockcallrecorder_destroy(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + /* Codes_SRS_UMOCKCALLRECORDER_01_004: [ If umock_call_recorder is NULL, umockcallrecorder_destroy shall do nothing. ]*/ + if (umock_call_recorder != NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_003: [ umockcallrecorder_destroy shall free the resources associated with a the call recorder identified by the umock_call_recorder argument. ]*/ + (void)umockcallrecorder_reset_all_calls(umock_call_recorder); + umockalloc_free(umock_call_recorder->actual_calls_string); + umockalloc_free(umock_call_recorder->expected_calls_string); + umockalloc_free(umock_call_recorder); + } +} + +int umockcallrecorder_reset_all_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + int result; + + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_007: [ If umock_call_recorder is NULL, umockcallrecorder_reset_all_calls shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umockcallrecorder: Reset all calls failed: NULL umock_call_recorder."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_005: [ umockcallrecorder_reset_all_calls shall free all the expected and actual calls for the call recorder identified by umock_call_recorder. ]*/ + if (umock_call_recorder->expected_calls != NULL) + { + size_t i; + for (i = 0; i < umock_call_recorder->expected_call_count; i++) + { + umockcall_destroy(umock_call_recorder->expected_calls[i].umockcall); + } + + umockalloc_free(umock_call_recorder->expected_calls); + umock_call_recorder->expected_calls = NULL; + } + umock_call_recorder->expected_call_count = 0; + + if (umock_call_recorder->actual_calls != NULL) + { + size_t i; + for (i = 0; i < umock_call_recorder->actual_call_count; i++) + { + umockcall_destroy(umock_call_recorder->actual_calls[i]); + } + + umockalloc_free(umock_call_recorder->actual_calls); + umock_call_recorder->actual_calls = NULL; + } + umock_call_recorder->actual_call_count = 0; + + /* Codes_SRS_UMOCKCALLRECORDER_01_006: [ On success umockcallrecorder_reset_all_calls shall return 0. ]*/ + result = 0; + } + + return result; +} + +int umockcallrecorder_add_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call) +{ + int result; + + if ((umock_call_recorder == NULL) || + (mock_call == NULL)) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_012: [ If any of the arguments is NULL, umockcallrecorder_add_expected_call shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umockcallrecorder: Bad arguments in add expected call: umock_call_recorder = %p, mock_call = %p.", + umock_call_recorder, mock_call); + result = __LINE__; + } + else + { + UMOCK_EXPECTED_CALL* new_expected_calls = (UMOCK_EXPECTED_CALL*)umockalloc_realloc(umock_call_recorder->expected_calls, sizeof(UMOCK_EXPECTED_CALL) * (umock_call_recorder->expected_call_count + 1)); + if (new_expected_calls == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_013: [ If allocating memory for the expected calls fails, umockcallrecorder_add_expected_call shall fail and return a non-zero value. ] */ + UMOCK_LOG("umockcallrecorder: Cannot allocate memory in add expected call."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_008: [ umockcallrecorder_add_expected_call shall add the mock_call call to the expected call list maintained by the call recorder identified by umock_call_recorder. ]*/ + umock_call_recorder->expected_calls = new_expected_calls; + umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count].umockcall = mock_call; + umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count++].is_matched = 0; + + /* Codes_SRS_UMOCKCALLRECORDER_01_009: [ On success umockcallrecorder_add_expected_call shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int umockcallrecorder_add_actual_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call) +{ + int result; + + if ((umock_call_recorder == NULL) || + (mock_call == NULL) || + (matched_call == NULL)) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_019: [ If any of the arguments is NULL, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umockcallrecorder: Bad arguments in add actual call: umock_call_recorder = %p, mock_call = %p, matched_call = %p.", + umock_call_recorder, mock_call, matched_call); + result = __LINE__; + } + else + { + size_t i; + unsigned int is_error = 0; + + *matched_call = NULL; + + /* Codes_SRS_UMOCK_C_LIB_01_115: [ umock_c shall compare calls in order. ]*/ + for (i = 0; i < umock_call_recorder->expected_call_count; i++) + { + int ignore_all_calls = umockcall_get_ignore_all_calls(umock_call_recorder->expected_calls[i].umockcall); + if (ignore_all_calls < 0) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_058: [ If getting `ignore_all_calls` by calling `umockcall_get_ignore_all_calls` fails, `umockcallrecorder_add_actual_call` shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umockcallrecorder: Cannot get the ignore_all_calls flag."); + is_error = 1; + break; + } + else + { + if ((umock_call_recorder->expected_calls[i].is_matched == 0) || + /* Codes_SRS_UMOCKCALLRECORDER_01_057: [ If any expected call has `ignore_all_calls` set and the actual call is equal to it when comparing the 2 calls, then the call shall be considered matched and not added to the actual calls list. ]*/ + (ignore_all_calls > 0)) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_017: [ Comparing the calls shall be done by calling umockcall_are_equal. ]*/ + int are_equal_result = umockcall_are_equal(umock_call_recorder->expected_calls[i].umockcall, mock_call); + if (are_equal_result == 1) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_016: [ If the call matches one of the expected calls, a handle to the matched call shall be filled into the matched_call argument. ]*/ + *matched_call = umock_call_recorder->expected_calls[i].umockcall; + break; + } + /* Codes_SRS_UMOCKCALLRECORDER_01_021: [ If umockcall_are_equal fails, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/ + else if (are_equal_result != 0) + { + is_error = 1; + break; + } + else + { + if (ignore_all_calls == 0) + { + i = umock_call_recorder->expected_call_count; + break; + } + } + } + } + } + + if ((umock_call_recorder->actual_call_count == 0) && (i < umock_call_recorder->expected_call_count)) + { + umock_call_recorder->expected_calls[i].is_matched = 1; + } + else + { + i = umock_call_recorder->expected_call_count; + } + + if (is_error) + { + UMOCK_LOG("umockcallrecorder: Error in finding a matched call."); + result = __LINE__; + } + else + { + if (i == umock_call_recorder->expected_call_count) + { + /* an unexpected call */ + /* Codes_SRS_UMOCKCALLRECORDER_01_014: [ umockcallrecorder_add_actual_call shall check whether the call mock_call matches any of the expected calls maintained by umock_call_recorder. ]*/ + UMOCKCALL_HANDLE* new_actual_calls = (UMOCKCALL_HANDLE*)umockalloc_realloc(umock_call_recorder->actual_calls, sizeof(UMOCKCALL_HANDLE) * (umock_call_recorder->actual_call_count + 1)); + if (new_actual_calls == NULL) + { + UMOCK_LOG("umockcallrecorder: Cannot allocate memory for actual calls."); + result = __LINE__; + } + else + { + umock_call_recorder->actual_calls = new_actual_calls; + umock_call_recorder->actual_calls[umock_call_recorder->actual_call_count++] = mock_call; + + /* Codes_SRS_UMOCKCALLRECORDER_01_018: [ When no error is encountered, umockcallrecorder_add_actual_call shall return 0. ]*/ + result = 0; + } + } + else + { + umockcall_destroy(mock_call); + + /* Codes_SRS_UMOCKCALLRECORDER_01_018: [ When no error is encountered, umockcallrecorder_add_actual_call shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +const char* umockcallrecorder_get_expected_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + const char* result; + + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_029: [ If the umock_call_recorder is NULL, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get expected calls."); + result = NULL; + } + else + { + size_t i; + char* new_expected_calls_string; + size_t current_length = 0; + + for (i = 0; i < umock_call_recorder->expected_call_count; i++) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_055: [ Getting the `ignore_all_calls` property shall be done by calling `umockcall_get_ignore_all_calls`. ]*/ + int ignore_all_calls = umockcall_get_ignore_all_calls(umock_call_recorder->expected_calls[i].umockcall); + if (ignore_all_calls < 0) + { + UMOCK_LOG("umockcallrecorder: Cannot get the ignore_all_calls flag."); + break; + } + else + { + if ((umock_call_recorder->expected_calls[i].is_matched == 0) && + /* Codes_SRS_UMOCKCALLRECORDER_01_054: [ Calls that have the `ignore_all_calls` property set shall not be reported in the expected call list. ]*/ + (ignore_all_calls == 0)) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_028: [ The string for each call shall be obtained by calling umockcall_stringify. ]*/ + char* stringified_call = umockcall_stringify(umock_call_recorder->expected_calls[i].umockcall); + if (stringified_call == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_030: [ If umockcall_stringify fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/ + break; + } + else + { + size_t stringified_call_length = strlen(stringified_call); + new_expected_calls_string = (char*)umockalloc_realloc(umock_call_recorder->expected_calls_string, current_length + stringified_call_length + 1); + if (new_expected_calls_string == NULL) + { + umockalloc_free(stringified_call); + + /* Codes_SRS_UMOCKCALLRECORDER_01_031: [ If allocating memory for the resulting string fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/ + break; + } + else + { + umock_call_recorder->expected_calls_string = new_expected_calls_string; + (void)memcpy(umock_call_recorder->expected_calls_string + current_length, stringified_call, stringified_call_length + 1); + current_length += stringified_call_length; + } + + umockalloc_free(stringified_call); + } + } + } + } + + if (i < umock_call_recorder->expected_call_count) + { + result = NULL; + } + else + { + if (current_length == 0) + { + new_expected_calls_string = (char*)umockalloc_realloc(umock_call_recorder->expected_calls_string, 1); + if (new_expected_calls_string == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_031: [ If allocating memory for the resulting string fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: Cannot allocate memory for expected calls."); + result = NULL; + } + else + { + umock_call_recorder->expected_calls_string = new_expected_calls_string; + umock_call_recorder->expected_calls_string[0] = '\0'; + + /* Codes_SRS_UMOCKCALLRECORDER_01_027: [ umockcallrecorder_get_expected_calls shall return a pointer to the string representation of all the expected calls. ]*/ + result = umock_call_recorder->expected_calls_string; + } + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_027: [ umockcallrecorder_get_expected_calls shall return a pointer to the string representation of all the expected calls. ]*/ + result = umock_call_recorder->expected_calls_string; + } + } + } + + return result; +} + +const char* umockcallrecorder_get_actual_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + const char* result; + + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_024: [ If the umock_call_recorder is NULL, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get actual calls."); + result = NULL; + } + else + { + size_t i; + char* new_actual_calls_string; + + if (umock_call_recorder->actual_call_count == 0) + { + new_actual_calls_string = (char*)umockalloc_realloc(umock_call_recorder->actual_calls_string, 1); + if (new_actual_calls_string == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_026: [ If allocating memory for the resulting string fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: Cannot allocate memory for actual calls."); + result = NULL; + } + else + { + umock_call_recorder->actual_calls_string = new_actual_calls_string; + umock_call_recorder->actual_calls_string[0] = '\0'; + + /* Codes_SRS_UMOCKCALLRECORDER_01_022: [ umockcallrecorder_get_actual_calls shall return a pointer to the string representation of all the actual calls. ]*/ + result = umock_call_recorder->actual_calls_string; + } + } + else + { + size_t current_length = 0; + + for (i = 0; i < umock_call_recorder->actual_call_count; i++) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_023: [ The string for each call shall be obtained by calling umockcall_stringify. ]*/ + char* stringified_call = umockcall_stringify(umock_call_recorder->actual_calls[i]); + if (stringified_call == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_025: [ If umockcall_stringify fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/ + break; + } + else + { + size_t stringified_call_length = strlen(stringified_call); + new_actual_calls_string = (char*)umockalloc_realloc(umock_call_recorder->actual_calls_string, current_length + stringified_call_length + 1); + if (new_actual_calls_string == NULL) + { + umockalloc_free(stringified_call); + + /* Codes_SRS_UMOCKCALLRECORDER_01_026: [ If allocating memory for the resulting string fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/ + break; + } + else + { + umock_call_recorder->actual_calls_string = new_actual_calls_string; + (void)memcpy(umock_call_recorder->actual_calls_string + current_length, stringified_call, stringified_call_length + 1); + current_length += stringified_call_length; + } + + umockalloc_free(stringified_call); + } + } + + if (i < umock_call_recorder->actual_call_count) + { + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_022: [ umockcallrecorder_get_actual_calls shall return a pointer to the string representation of all the actual calls. ]*/ + result = umock_call_recorder->actual_calls_string; + } + } + } + + return result; +} + +UMOCKCALL_HANDLE umockcallrecorder_get_last_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + UMOCKCALL_HANDLE result; + + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_033: [ If umock_call_recorder is NULL, umockcallrecorder_get_last_expected_call shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get last expected calls."); + result = NULL; + } + else + { + if (umock_call_recorder->expected_call_count == 0) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_034: [ If no expected call has been recorded for umock_call_recorder then umockcallrecorder_get_last_expected_call shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder: No expected calls recorded."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_032: [ umockcallrecorder_get_last_expected_call shall return the last expected call for the umock_call_recorder call recorder. ]*/ + result = umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count - 1].umockcall; + } + } + + return result; +} + +/* Codes_SRS_UMOCKCALLRECORDER_01_035: [ umockcallrecorder_clone shall clone a call recorder and return a handle to the newly cloned call recorder. ]*/ +UMOCKCALLRECORDER_HANDLE umockcallrecorder_clone(UMOCKCALLRECORDER_HANDLE umock_call_recorder) +{ + UMOCKCALLRECORDER_HANDLE result; + + if (umock_call_recorder == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_036: [ If the umock_call_recorder argument is NULL, umockcallrecorder_clone shall fail and return NULL. ]*/ + UMOCK_LOG("umockcallrecorder_clone: NULL umock_call_recorder."); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_037: [ If allocating memory for the new umock call recorder instance fails, umockcallrecorder_clone shall fail and return NULL. ]*/ + result = umockcallrecorder_create(); + if (result != NULL) + { + size_t i; + + result->expected_calls = (UMOCK_EXPECTED_CALL*)umockalloc_malloc(sizeof(UMOCK_EXPECTED_CALL) * umock_call_recorder->expected_call_count); + if (result->expected_calls == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_052: [ If allocating memory for the expected calls fails, umockcallrecorder_clone shall fail and return NULL. ]*/ + umockcallrecorder_destroy(result); + result = NULL; + UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot allocate expected_calls."); + } + else + { + for (i = 0; i < umock_call_recorder->expected_call_count; i++) + { + result->expected_calls[i].umockcall = NULL; + } + + /* Codes_SRS_UMOCKCALLRECORDER_01_038: [ umockcallrecorder_clone shall clone all the expected calls. ]*/ + for (i = 0; i < umock_call_recorder->expected_call_count; i++) + { + result->expected_calls[i].is_matched = umock_call_recorder->expected_calls[i].is_matched; + + /* Codes_SRS_UMOCKCALLRECORDER_01_039: [ Each expected call shall be cloned by calling umockcall_clone. ]*/ + result->expected_calls[i].umockcall = umockcall_clone(umock_call_recorder->expected_calls[i].umockcall); + if (result->expected_calls[i].umockcall == NULL) + { + break; + } + } + + if (i < umock_call_recorder->expected_call_count) + { + size_t j; + + /* Codes_SRS_UMOCKCALLRECORDER_01_040: [ If cloning an expected call fails, umockcallrecorder_clone shall fail and return NULL. ]*/ + for (j = 0; j < i; j++) + { + umockcall_destroy(result->expected_calls[j].umockcall); + result->expected_calls[j].umockcall = NULL; + } + + umockcallrecorder_destroy(result); + result = NULL; + UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot clone all expected calls."); + } + else + { + result->expected_call_count = umock_call_recorder->expected_call_count; + + result->actual_calls = (UMOCKCALL_HANDLE*)umockalloc_malloc(sizeof(UMOCKCALL_HANDLE) * umock_call_recorder->actual_call_count); + if (result->actual_calls == NULL) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_053: [ If allocating memory for the actual calls fails, umockcallrecorder_clone shall fail and return NULL. ]*/ + umockcallrecorder_destroy(result); + result = NULL; + UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot allocate expected_calls."); + } + else + { + for (i = 0; i < umock_call_recorder->actual_call_count; i++) + { + result->actual_calls[i] = NULL; + } + + /* Codes_SRS_UMOCKCALLRECORDER_01_041: [ umockcallrecorder_clone shall clone all the actual calls. ]*/ + for (i = 0; i < umock_call_recorder->actual_call_count; i++) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_042: [ Each actual call shall be cloned by calling umockcall_clone. ]*/ + result->actual_calls[i] = umockcall_clone(umock_call_recorder->actual_calls[i]); + if (result->actual_calls[i] == NULL) + { + break; + } + } + + if (i < umock_call_recorder->actual_call_count) + { + size_t j; + + /* Codes_SRS_UMOCKCALLRECORDER_01_043: [ If cloning an actual call fails, umockcallrecorder_clone shall fail and return NULL. ]*/ + for (j = 0; j < i; j++) + { + umockcall_destroy(result->actual_calls[j]); + result->actual_calls[j] = NULL; + } + + umockcallrecorder_destroy(result); + result = NULL; + UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot clone all actual calls."); + } + else + { + result->actual_call_count = umock_call_recorder->actual_call_count; + } + } + } + } + } + } + + return result; +} + +int umockcallrecorder_get_expected_call_count(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t* expected_call_count) +{ + int result; + + if ((umock_call_recorder == NULL) || + (expected_call_count == NULL)) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_046: [ If any of the arguments is NULL, umockcallrecorder_get_expected_call_count shall return a non-zero value. ]*/ + result = __LINE__; + UMOCK_LOG("umockcallrecorder_get_expected_call_count: Invalid arguments, umock_call_recorder = %p, expected_call_count = %p", + umock_call_recorder, expected_call_count); + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_044: [ umockcallrecorder_get_expected_call_count shall return in the expected_call_count argument the number of expected calls associated with the call recorder. ]*/ + *expected_call_count = umock_call_recorder->expected_call_count; + /* Codes_SRS_UMOCKCALLRECORDER_01_045: [ On success umockcallrecorder_get_expected_call_count shall return 0. ]*/ + result = 0; + } + + return result; +} + +int umockcallrecorder_fail_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t index) +{ + int result; + + /* Codes_SRS_UMOCKCALLRECORDER_01_049: [ If umock_call_recorder is NULL, umockcallrecorder_fail_call shall return a non-zero value. ]*/ + if ((umock_call_recorder == NULL) || + /* Codes_SRS_UMOCKCALLRECORDER_01_050: [ If index is invalid, umockcallrecorder_fail_call shall return a non-zero value. ]*/ + (index >= umock_call_recorder->expected_call_count)) + { + result = __LINE__; + UMOCK_LOG("umockcallrecorder_fail_call: NULL Invalid arguments, umock_call_recorder = %p, index = %lu", + umock_call_recorder, index); + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_047: [ umockcallrecorder_fail_call shall mark an expected call as to be failed by calling umockcall_set_fail_call with a 1 value for fail_call. ]*/ + if (umockcall_set_fail_call(umock_call_recorder->expected_calls[index].umockcall, 1) != 0) + { + /* Codes_SRS_UMOCKCALLRECORDER_01_051: [ If umockcall_set_fail_call fails, umockcallrecorder_fail_call shall return a non-zero value. ]*/ + result = __LINE__; + UMOCK_LOG("umockcallrecorder_fail_call: umockcall_set_fail_call failed."); + } + else + { + /* Codes_SRS_UMOCKCALLRECORDER_01_048: [ On success, umockcallrecorder_fail_call shall return 0. ]*/ + result = 0; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umockstring.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stddef.h> +#include <string.h> +#include "umockalloc.h" +#include "umockstring.h" +#include "umock_log.h" + +char* umockstring_clone(const char* source) +{ + char* result; + + if (source == NULL) + { + /* Codes_UMOCK_STRING_01_005: [ If `umockstring_clone` is called with a NULL `source`, it shall return NULL. ]*/ + UMOCK_LOG("umockstring_clone called with NULL source"); + result = NULL; + } + else + { + size_t string_length = strlen(source); + + /* Codes_UMOCK_STRING_01_001: [ `umockstring_clone` shall allocate memory for the cloned string (including the NULL terminator). ]*/ + /* Codes_UMOCK_STRING_01_003: [ On success `umockstring_clone` shall return a pointer to the newly allocated memory containing the copy of the string. ]*/ + result = (char*)umockalloc_malloc(string_length + 1); + if (result == NULL) + { + /* Codes_UMOCK_STRING_01_004: [ If allocating the memory fails, `umockstring_clone` shall return NULL. ]*/ + UMOCK_LOG("Error allocating memory for string clone"); + } + else + { + /* Codes_UMOCK_STRING_01_002: [ `umockstring_clone` shall copy the string to the newly allocated memory (including the NULL terminator). ]*/ + (void)memcpy(result, source, string_length + 1); + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypename.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <ctype.h> +#include <stddef.h> +#include "umocktypename.h" +#include "umockalloc.h" +#include "umock_log.h" + +char* umocktypename_normalize(const char* type_name) +{ + char* result; + + if (type_name == NULL) + { + /* Codes_SRS_UMOCKTYPENAME_01_005: [ If typename is NULL, then umocktypename_normalize shall fail and return NULL. ]*/ + UMOCK_LOG("umocktypename: NULL type_name."); + result = NULL; + } + else + { + size_t length = 0; + size_t pos = 0; + size_t last_pos; + + /* we first compute how much space we need for the normalized version ... */ + while (type_name[pos] != '\0') + { + last_pos = pos; + + /* find all the spaces first */ + while ((type_name[pos] != '\0') && (isspace(type_name[pos]))) + { + pos++; + } + + /* we got a bunch of spaces, figure out whether we need to add any spaces to the normalized type name or not */ + /* Codes_SRS_UMOCKTYPENAME_01_002: [ umocktypename_normalize shall remove all spaces at the beginning of the typename. ]*/ + /* Codes_SRS_UMOCKTYPENAME_01_003: [ umocktypename_normalize shall remove all spaces at the end of the typename. ] */ + /* Codes_SRS_UMOCKTYPENAME_01_006: [ No space shall exist between any other token and a star. ]*/ + if ((last_pos == 0) || + (type_name[pos] == '\0') || + (type_name[last_pos - 1] == '*') || + (type_name[pos] == '*')) + { + /* do not add any spaces */ + } + else + { + /* add one space */ + /* Codes_SRS_UMOCKTYPENAME_01_004: [ umocktypename_normalize shall remove all extra spaces (more than 1 space) between elements that are part of the typename. ]*/ + length ++; + } + + /* found something that is a non-space character, add to the normalized type name */ + while ((type_name[pos] != '\0') && (!isspace(type_name[pos]))) + { + pos++; + length++; + } + } + + if (length == 0) + { + /* Codes_SRS_UMOCKTYPENAME_01_007: [ If the length of the normalized typename is 0, umocktypename_normalize shall return NULL. ]*/ + UMOCK_LOG("umocktypename: 0 length for the normalized type name."); + result = NULL; + } + else + { + /* ... then we allocate*/ + /* Codes_SRS_UMOCKTYPENAME_01_001: [ umocktypename_normalize shall return a char\* with a newly allocated string that contains the normalized typename. ]*/ + result = (char*)umockalloc_malloc(length + 1); + /* Codes_SRS_UMOCKTYPENAME_01_008: [ If allocating memory fails, umocktypename_normalize shall fail and return NULL. ]*/ + if (result != NULL) + { + pos = 0; + length = 0; + (void)last_pos; + + while (type_name[pos] != '\0') + { + last_pos = pos; + + /* find all the spaces first */ + while ((type_name[pos] != '\0') && (isspace(type_name[pos]))) + { + pos++; + } + + /* we got a bunch of spaces, figure out whether we need to add any spaces to the normalized type name or not */ + /* Codes_SRS_UMOCKTYPENAME_01_002: [ umocktypename_normalize shall remove all spaces at the beginning of the typename. ]*/ + /* Codes_SRS_UMOCKTYPENAME_01_003: [ umocktypename_normalize shall remove all spaces at the end of the typename. ] */ + /* Codes_SRS_UMOCKTYPENAME_01_006: [ No space shall exist between any other token and a star. ]*/ + if ((last_pos == 0) || + (type_name[pos] == '\0') || + (type_name[last_pos - 1] == '*') || + (type_name[pos] == '*')) + { + /* do not add any spaces */ + } + else + { + /* add one space */ + /* Codes_SRS_UMOCKTYPENAME_01_004: [ umocktypename_normalize shall remove all extra spaces (more than 1 space) between elements that are part of the typename. ]*/ + result[length++] = ' '; + } + + /* found something that is a non-space character, add to the normalized type name */ + while ((type_name[pos] != '\0') && (!isspace(type_name[pos]))) + { + result[length++] = type_name[pos++]; + } + } + + result[length] = '\0'; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypes.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,514 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <ctype.h> +#include <stddef.h> +#include <string.h> +#include "umocktypes.h" +#include "umocktypename.h" +#include "umockalloc.h" +#include "umock_log.h" + +typedef struct UMOCK_VALUE_TYPE_HANDLERS_TAG +{ + char* type; + UMOCKTYPE_STRINGIFY_FUNC stringify_func; + UMOCKTYPE_COPY_FUNC copy_func; + UMOCKTYPE_FREE_FUNC free_func; + UMOCKTYPE_ARE_EQUAL_FUNC are_equal_func; +} UMOCK_VALUE_TYPE_HANDLERS; + +typedef enum UMOCKTYPES_STATE_TAG +{ + UMOCKTYPES_STATE_NOT_INITIALIZED, + UMOCKTYPES_STATE_INITIALIZED +} UMOCKTYPES_STATE; + +static UMOCK_VALUE_TYPE_HANDLERS* type_handlers = NULL; +static size_t type_handler_count = 0; +static UMOCKTYPES_STATE umocktypes_state = UMOCKTYPES_STATE_NOT_INITIALIZED; + +static UMOCK_VALUE_TYPE_HANDLERS* get_value_type_handlers(const char* type_name) +{ + UMOCK_VALUE_TYPE_HANDLERS* result; + size_t i; + + for (i = 0; i < type_handler_count; i++) + { + if (strcmp(type_handlers[i].type, type_name) == 0) + { + break; + } + } + + if (i < type_handler_count) + { + result = &type_handlers[i]; + } + else + { + result = NULL; + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_001: [ umocktypes_init shall initialize the umocktypes module. ] */ +int umocktypes_init(void) +{ + int result; + + if (umocktypes_state == UMOCKTYPES_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCKTYPES_01_004: [ umocktypes_init after another umocktypes_init without deinitializing the module shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umock_c already initialized.\r\n"); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_002: [ After initialization the list of registered type shall be empty. ] */ + type_handlers = NULL; + type_handler_count = 0; + + umocktypes_state = UMOCKTYPES_STATE_INITIALIZED; + + /* Codes_SRS_UMOCKTYPES_01_003: [ On success umocktypes_init shall return 0. ]*/ + result = 0; + } + + return result; +} + +void umocktypes_deinit(void) +{ + /* Codes_SRS_UMOCKTYPES_01_006: [ If the module was not initialized, umocktypes_deinit shall do nothing. ]*/ + if (umocktypes_state == UMOCKTYPES_STATE_INITIALIZED) + { + size_t i; + + /* Codes_SRS_UMOCKTYPES_01_005: [ umocktypes_deinit shall free all resources associated with the registered types and shall leave the module in a state where another init is possible. ]*/ + for (i = 0; i < type_handler_count; i++) + { + umockalloc_free(type_handlers[i].type); + } + + umockalloc_free(type_handlers); + type_handlers = NULL; + type_handler_count = 0; + + /* Codes_SRS_UMOCKTYPES_01_040: [ An umocktypes_init call after deinit shall succeed provided all underlying calls succeed. ]*/ + umocktypes_state = UMOCKTYPES_STATE_NOT_INITIALIZED; + } +} + +int umocktypes_register_type(const char* type, UMOCKTYPE_STRINGIFY_FUNC stringify_func, UMOCKTYPE_ARE_EQUAL_FUNC are_equal_func, UMOCKTYPE_COPY_FUNC copy_func, UMOCKTYPE_FREE_FUNC free_func) +{ + int result; + + if ((type == NULL) || + (stringify_func == NULL) || + (are_equal_func == NULL) || + (copy_func == NULL) || + (free_func == NULL)) + { + /* Codes_SRS_UMOCKTYPES_01_009: [ If any of the arguments is NULL, umocktypes_register_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Invalid arguments when registering umock_c type: type = %p, stringify_func = %p, are_equal_func = %p, copy_func = %p, free_func = %p.\r\n", + type, stringify_func, are_equal_func, copy_func, free_func); + result = __LINE__; + } + /* Codes_SRS_UMOCKTYPES_01_050: [ If umocktypes_register_type is called when the module is not initialized, umocktypes_register_type shall fail and return a non zero value. ]*/ + else if (umocktypes_state != UMOCKTYPES_STATE_INITIALIZED) + { + UMOCK_LOG("Could not register type, umock_c not initialized.\r\n"); + result = __LINE__; + } + else + { + char* normalized_type = umocktypename_normalize(type); + if (normalized_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_045: [ If normalizing the typename fails, umocktypes_register_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Normalizing type %s failed.\r\n", type); + result = __LINE__; + } + else + { + UMOCK_VALUE_TYPE_HANDLERS* type_handler = get_value_type_handlers(normalized_type); + if (type_handler != NULL) + { + umockalloc_free(normalized_type); + + if ((stringify_func != type_handler->stringify_func) || + (are_equal_func != type_handler->are_equal_func) || + (copy_func != type_handler->copy_func) || + (free_func != type_handler->free_func)) + { + /* Codes_SRS_UMOCKTYPES_01_011: [ If the type has already been registered but at least one of the function pointers is different, umocktypes_register_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Could not register type, type %s already registered with different handlers.\r\n", type); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_010: [ If the type has already been registered with the same function pointers then umocktypes_register_type shall succeed and return 0. ]*/ + result = 0; + } + } + else + { + UMOCK_VALUE_TYPE_HANDLERS* new_type_handlers = (UMOCK_VALUE_TYPE_HANDLERS*)umockalloc_realloc(type_handlers, sizeof(UMOCK_VALUE_TYPE_HANDLERS) * (type_handler_count + 1)); + if (new_type_handlers == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_012: [ If an error occurs allocating memory for the newly registered type, umocktypes_register_type shall fail and return a non-zero value. ]*/ + umockalloc_free(normalized_type); + UMOCK_LOG("Could not register type, failed allocating memory for types.\r\n"); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_007: [ umocktypes_register_type shall register an interface made out of the stringify, are equal, copy and free functions for the type identified by the argument type. ] */ + type_handlers = new_type_handlers; + type_handlers[type_handler_count].type = normalized_type; + type_handlers[type_handler_count].stringify_func = stringify_func; + type_handlers[type_handler_count].copy_func = copy_func; + type_handlers[type_handler_count].free_func = free_func; + type_handlers[type_handler_count].are_equal_func = are_equal_func; + type_handler_count++; + + /* Codes_SRS_UMOCKTYPES_01_008: [ On success umocktypes_register_type shall return 0. ]*/ + result = 0; + } + } + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_053: [ umocktypes_register_alias_type shall register a new alias type for the type "is_type". ]*/ +int umocktypes_register_alias_type(const char* type, const char* is_type) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_01_055: [ If any of the arguments is NULL, umocktypes_register_alias_type shall fail and return a non-zero value. ]*/ + if ((type == NULL) || (is_type == NULL)) + { + UMOCK_LOG("Could not register alias type, bad arguments: type = %p, is_type = %p.\r\n", type, is_type); + result = __LINE__; + } + else if (umocktypes_state != UMOCKTYPES_STATE_INITIALIZED) + { + UMOCK_LOG("Could not register alias type, umock_c_types not initialized.\r\n"); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_058: [ Before looking it up, is_type shall be normalized by using umocktypename_normalize. ] */ + char* normalized_is_type = umocktypename_normalize(is_type); + if (normalized_is_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_060: [ If umocktypename_normalize fails, umocktypes_register_alias_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Could not register alias type, normalizing type %s failed.\r\n", is_type); + result = __LINE__; + } + else + { + UMOCK_VALUE_TYPE_HANDLERS* new_type_handlers = (UMOCK_VALUE_TYPE_HANDLERS*)umockalloc_realloc(type_handlers, sizeof(UMOCK_VALUE_TYPE_HANDLERS) * (type_handler_count + 1)); + if (new_type_handlers == NULL) + { + UMOCK_LOG("Could not register alias type, failed allocating memory.\r\n"); + result = __LINE__; + } + else + { + UMOCK_VALUE_TYPE_HANDLERS* value_type_handlers; + type_handlers = new_type_handlers; + value_type_handlers = get_value_type_handlers(normalized_is_type); + if (value_type_handlers == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_057: [ If is_type was not already registered, umocktypes_register_alias_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Could not register alias type, type %s was not previously registered.\r\n", normalized_is_type); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_059: [ Before adding it as alias, type shall be normalized by using umocktypename_normalize. ]*/ + char* normalized_type = umocktypename_normalize(type); + if (normalized_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_060: [ If umocktypename_normalize fails, umocktypes_register_alias_type shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Could not register alias type, normalizing type %s failed.\r\n", type); + result = __LINE__; + } + else + { + if (strcmp(normalized_type, normalized_is_type) == 0) + { + umockalloc_free(normalized_type); + + /* Codes_SRS_UMOCKTYPES_01_062: [ If type and is_type are the same, umocktypes_register_alias_type shall succeed and return 0. ]*/ + result = 0; + } + else + { + type_handlers[type_handler_count].type = normalized_type; + type_handlers[type_handler_count].stringify_func = value_type_handlers->stringify_func; + type_handlers[type_handler_count].copy_func = value_type_handlers->copy_func; + type_handlers[type_handler_count].free_func = value_type_handlers->free_func; + type_handlers[type_handler_count].are_equal_func = value_type_handlers->are_equal_func; + type_handler_count++; + + /* Codes_SRS_UMOCKTYPES_01_054: [ On success, umocktypes_register_alias_type shall return 0. ]*/ + result = 0; + } + } + } + } + + umockalloc_free(normalized_is_type); + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_013: [ umocktypes_stringify shall return a char\* with the string representation of the value argument. ]*/ +char* umocktypes_stringify(const char* type, const void* value) +{ + char* result; + + if ((type == NULL) || + (value == NULL)) + { + /* Codes_SRS_UMOCKTYPES_01_016: [ If any of the arguments is NULL, umocktypes_stringify shall fail and return NULL. ]*/ + UMOCK_LOG("Could not stringify type, bad arguments: type = %p, value = %p.\r\n", type, value); + result = NULL; + } + /* Codes_SRS_UMOCKTYPES_01_049: [ If umocktypes_stringify is called when the module is not initialized, umocktypes_stringify shall return NULL. ]*/ + else if (umocktypes_state != UMOCKTYPES_STATE_INITIALIZED) + { + UMOCK_LOG("Could not stringify type, umock_c_types not initialized.\r\n"); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_035: [ Before looking it up, the type string shall be normalized by calling umocktypename_normalize. ]*/ + char* normalized_type = umocktypename_normalize(type); + if (normalized_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_044: [ If normalizing the typename fails, umocktypes_stringify shall fail and return NULL. ]*/ + UMOCK_LOG("Could not stringify type, normalizing type %s failed.\r\n", type); + result = NULL; + } + else + { + size_t normalized_type_length = strlen(normalized_type); + UMOCK_VALUE_TYPE_HANDLERS* value_type_handlers = get_value_type_handlers(normalized_type); + + /* Codes_SRS_UMOCK_C_LIB_01_153: [ If no custom handler has beed registered for a pointer type, it shall be trated as void*. ] */ + if ((value_type_handlers == NULL) && (normalized_type[normalized_type_length - 1] == '*')) + { + /* Codes_SRS_UMOCKTYPES_01_063: [ If type is a pointer type and type was not registered then umocktypes_stringify shall execute as if type is void*. ]*/ + value_type_handlers = get_value_type_handlers("void*"); + } + + if (value_type_handlers == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_017: [ If type can not be found in the registered types list maintained by the module, umocktypes_stringify shall fail and return NULL. ]*/ + UMOCK_LOG("Could not stringify type, type %s not registered.\r\n", normalized_type); + result = NULL; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_014: [ The string representation shall be obtained by calling the stringify function registered for the type identified by the argument type. ]*/ + /* Codes_SRS_UMOCKTYPES_01_015: [ On success umocktypes_stringify shall return the char\* produced by the underlying stringify function for type (passed in umocktypes_register_type). ]*/ + result = value_type_handlers->stringify_func(value); + } + + umockalloc_free(normalized_type); + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_018: [ umocktypes_are_equal shall evaluate whether 2 values are equal. ]*/ +int umocktypes_are_equal(const char* type, const void* left, const void* right) +{ + int result; + + if ((type == NULL) || + (left == NULL) || + (right == NULL)) + { + /* Codes_SRS_UMOCKTYPES_01_023: [ If any of the arguments is NULL, umocktypes_are_equal shall fail and return -1. ]*/ + UMOCK_LOG("Could not compare values for type, bad arguments: type = %p, left = %p, right = %p.\r\n", type, left, right); + result = -1; + } + else if(umocktypes_state != UMOCKTYPES_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCKTYPES_01_046: [ If umocktypes_are_equal is called when the module is not initialized, umocktypes_are_equal shall return -1. ] */ + UMOCK_LOG("Could not compare values for type, umock_c_types not initialized.\r\n"); + result = -1; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_036: [ Before looking it up, the type string shall be normalized by calling umocktypename_normalize. ]*/ + char* normalized_type = umocktypename_normalize(type); + if (normalized_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_043: [ If normalizing the typename fails, umocktypes_are_equal shall fail and return -1. ]*/ + UMOCK_LOG("Could not compare values for type, normalizing type %s failed.\r\n", type); + result = -1; + } + else + { + size_t normalized_type_length = strlen(normalized_type); + UMOCK_VALUE_TYPE_HANDLERS* value_type_handlers = get_value_type_handlers(normalized_type); + + /* Codes_SRS_UMOCK_C_LIB_01_153: [ If no custom handler has beed registered for a pointer type, it shall be trated as void*. ] */ + if ((value_type_handlers == NULL) && (normalized_type[normalized_type_length - 1] == '*')) + { + /* Codes_SRS_UMOCKTYPES_01_064: [ If type is a pointer type and type was not registered then umocktypes_are_equal shall execute as if type is void*. ]*/ + value_type_handlers = get_value_type_handlers("void*"); + } + + if (value_type_handlers == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_024: [ If type can not be found in the registered types list maintained by the module, umocktypes_are_equal shall fail and return -1. ]*/ + UMOCK_LOG("Could not compare values for type, type %s not registered.\r\n", normalized_type); + result = -1; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_051: [ If the pointer values for left and right are equal, umocktypes_are_equal shall return 1 without calling the underlying are_equal function. ]*/ + if (left == right) + { + result = 1; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_019: [ umocktypes_are_equal shall call the underlying are_equal function for the type identified by the argument type (passed in umocktypes_register_type). ] */ + switch (value_type_handlers->are_equal_func(left, right)) + { + default: + /* Codes_SRS_UMOCKTYPES_01_020: [ If the underlying are_equal function fails,, umocktypes_are_equal shall fail and return -1. ] */ + UMOCK_LOG("Underlying compare failed for type %s.\r\n", normalized_type); + result = -1; + break; + + case 1: + /* Codes_SRS_UMOCKTYPES_01_021: [ If the underlying are_equal function indicates the types are equal, umocktypes_are_equal shall return 1. ]*/ + result = 1; + break; + + case 0: + /* Codes_SRS_UMOCKTYPES_01_022: [ If the underlying are_equal function indicates the types are not equal, umocktypes_are_equal shall return 0. ]*/ + result = 0; + break; + } + } + } + + umockalloc_free(normalized_type); + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_025: [ umocktypes_copy shall copy the value of the source into the destination argument. ]*/ +int umocktypes_copy(const char* type, void* destination, const void* source) +{ + int result; + + if ((type == NULL) || + (destination == NULL) || + (source == NULL)) + { + /* Codes_SRS_UMOCKTYPES_01_027: [ If any of the arguments is NULL, umocktypes_copy shall return -1. ]*/ + UMOCK_LOG("Could not copy type, bad arguments: type = %p, destination = %p, source = %p.\r\n", type, destination, source); + result = -1; + } + else if (umocktypes_state != UMOCKTYPES_STATE_INITIALIZED) + { + /* Codes_SRS_UMOCKTYPES_01_047: [ If umocktypes_copy is called when the module is not initialized, umocktypes_copy shall fail and return a non zero value. ]*/ + UMOCK_LOG("Could not copy type, umock_c_types not initialized.\r\n"); + result = -1; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_037: [ Before looking it up, the type string shall be normalized by calling umocktypename_normalize. ]*/ + char* normalized_type = umocktypename_normalize(type); + if (normalized_type == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_042: [ If normalizing the typename fails, umocktypes_copy shall fail and return a non-zero value. ]*/ + UMOCK_LOG("Could not copy type, normalizing type %s failed.\r\n", type); + result = -1; + } + else + { + size_t normalized_type_length = strlen(normalized_type); + UMOCK_VALUE_TYPE_HANDLERS* value_type_handlers = get_value_type_handlers(normalized_type); + + /* Codes_SRS_UMOCK_C_LIB_01_153: [ If no custom handler has beed registered for a pointer type, it shall be trated as void*. ] */ + if ((value_type_handlers == NULL) && (normalized_type[normalized_type_length - 1] == '*')) + { + /* Codes_SRS_UMOCKTYPES_01_065: [ If type is a pointer type and type was not registered then umocktypes_copy shall execute as if type is void*. ]*/ + value_type_handlers = get_value_type_handlers("void*"); + } + + if (value_type_handlers == NULL) + { + /* Codes_SRS_UMOCKTYPES_01_029: [ If type can not be found in the registered types list maintained by the module, umocktypes_copy shall fail and return -1. ]*/ + UMOCK_LOG("Could not copy type, type %s not registered.\r\n", normalized_type); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_01_026: [ The copy shall be done by calling the underlying copy function (passed in umocktypes_register_type) for the type identified by the type argument. ]*/ + /* Codes_SRS_UMOCKTYPES_01_052: [ On success, umocktypes_copy shall return 0. ]*/ + /* Codes_SRS_UMOCKTYPES_01_028: [ If the underlying copy fails, umocktypes_copy shall return -1. ]*/ + result = value_type_handlers->copy_func(destination, source); + } + + umockalloc_free(normalized_type); + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_01_030: [ umocktypes_free shall free a value previously allocated with umocktypes_copy. ]*/ +void umocktypes_free(const char* type, void* value) +{ + /* Codes_SRS_UMOCKTYPES_01_031: [ If any of the arguments is NULL, umocktypes_free shall do nothing. ]*/ + if ((type != NULL) && + (value != NULL) && + /* Codes_SRS_UMOCKTYPES_01_048: [ If umocktypes_free is called when the module is not initialized, umocktypes_free shall do nothing. ]*/ + (umocktypes_state == UMOCKTYPES_STATE_INITIALIZED)) + { + /* Codes_SRS_UMOCKTYPES_01_038: [ Before looking it up, the type string shall be normalized by calling umocktypename_normalize. ]*/ + char* normalized_type = umocktypename_normalize(type); + /* Codes_SRS_UMOCKTYPES_01_032: [ If type can not be found in the registered types list maintained by the module, umocktypes_free shall do nothing. ]*/ + if (normalized_type != NULL) + { + size_t normalized_type_length = strlen(normalized_type); + UMOCK_VALUE_TYPE_HANDLERS* value_type_handlers = get_value_type_handlers(normalized_type); + + /* Codes_SRS_UMOCK_C_LIB_01_153: [ If no custom handler has beed registered for a pointer type, it shall be trated as void*. ] */ + if ((value_type_handlers == NULL) && (normalized_type[normalized_type_length - 1] == '*')) + { + /* Codes_SRS_UMOCKTYPES_01_066: [ If type is a pointer type and type was not registered then umocktypes_free shall execute as if type is void*. ]*/ + value_type_handlers = get_value_type_handlers("void*"); + } + + if (value_type_handlers != NULL) + { + /* Codes_SRS_UMOCKTYPES_01_033: [ The free shall be done by calling the underlying free function (passed in umocktypes_register_type) for the type identified by the type argument. ]*/ + value_type_handlers->free_func(value); + } + + umockalloc_free(normalized_type); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypes_bool.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include "macro_utils.h" +#include "umocktypes.h" +#include "umock_c.h" +#include "umockalloc.h" +#include "umocktypes_bool.h" + +/* Codes_SRS_UMOCKTYPES_BOOL_01_002: [ umocktypes_stringify_bool shall return the string representation of value. ]*/ +char* umocktypes_stringify_bool(const bool* value) +{ + char* result; + + /* Codes_SRS_UMOCKTYPES_BOOL_01_003: [ If value is NULL, umocktypes_stringify_bool shall return NULL. ]*/ + if (value == NULL) + { + UMOCK_LOG("umocktypes_stringify_bool: NULL value."); + result = NULL; + } + else + { + const char* stringified_bool = *value ? "true" : "false"; + size_t length = strlen(stringified_bool); + + /* Codes_SRS_UMOCKTYPES_BOOL_01_004: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_bool shall return NULL. ]*/ + result = (char*)umockalloc_malloc(length + 1); + if (result == NULL) + { + UMOCK_LOG("umocktypes_stringify_bool: Cannot allocate memory for result."); + } + else + { + (void)memcpy(result, stringified_bool, length + 1); + } + } + return result; +} + +/* Codes_SRS_UMOCKTYPES_BOOL_01_006: [ umocktypes_are_equal_bool shall compare the 2 chars pointed to by left and right. ]*/ +int umocktypes_are_equal_bool(const bool* left, const bool* right) +{ + int result; + + if ((left == NULL) || (right == NULL)) + { + /* Codes_SRS_UMOCKTYPES_BOOL_01_007: [ If any of the arguments is NULL, umocktypes_are_equal_bool shall return -1. ]*/ + UMOCK_LOG("umocktypes_are_equal_bool: Bad arguments:left = %p, right = %p.", left, right); + result = -1; + } + else + { + /* Codes_SRS_UMOCKTYPES_BOOL_01_009: [ If the values pointed to by left and right are different, umocktypes_are_equal_bool shall return 0. ]*/ + /* Codes_SRS_UMOCKTYPES_BOOL_01_008: [ If the values pointed to by left and right are equal, umocktypes_are_equal_bool shall return 1. ]*/ + result = ((*left) == (*right)) ? 1 : 0; + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_BOOL_01_010: [ umocktypes_copy_bool shall copy the bool value from source to destination. ]*/ +int umocktypes_copy_bool(bool* destination, const bool* source) +{ + int result; + + if ((destination == NULL) || + (source == NULL)) + { + /* Codes_SRS_UMOCKTYPES_BOOL_01_012: [ If source or destination are NULL, umocktypes_copy_bool shall return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_copy_bool: Bad arguments:destination = %p, source = %p.", destination, source); + result = __LINE__; + } + else + { + *destination = *source; + + /* Codes_SRS_UMOCKTYPES_BOOL_01_011: [ On success umocktypes_copy_bool shall return 0. ]*/ + result = 0; + } + return result; +} + +/* Codes_SRS_UMOCKTYPES_BOOL_01_013: [ umocktypes_free_bool shall do nothing. ]*/ +void umocktypes_free_bool(bool* value) +{ + (void)value; +} + +int umocktypes_bool_register_types(void) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_BOOL_01_001: [ umocktypes_bool_register_types shall register support for all the types in the module. ]*/ + if ((umocktypes_register_type("bool", (UMOCKTYPE_STRINGIFY_FUNC)umocktypes_stringify_bool, (UMOCKTYPE_ARE_EQUAL_FUNC)umocktypes_are_equal_bool, (UMOCKTYPE_COPY_FUNC)umocktypes_copy_bool, (UMOCKTYPE_FREE_FUNC)umocktypes_free_bool) != 0) || + (umocktypes_register_type("_Bool", (UMOCKTYPE_STRINGIFY_FUNC)umocktypes_stringify_bool, (UMOCKTYPE_ARE_EQUAL_FUNC)umocktypes_are_equal_bool, (UMOCKTYPE_COPY_FUNC)umocktypes_copy_bool, (UMOCKTYPE_FREE_FUNC)umocktypes_free_bool) != 0)) + { + /* Codes_SRS_UMOCKTYPES_BOOL_01_015: [ If registering any of the types fails, umocktypes_bool_register_types shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_bool_register_types: Cannot register types."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_BOOL_01_014: [ On success, umocktypes_bool_register_types shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypes_c.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdio.h> +#include <stddef.h> +#include <string.h> +#include "macro_utils.h" +#include "umocktypes.h" +#include "umocktypes_c.h" +#include "umockalloc.h" +#include "umock_log.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +#ifdef MAX_UMOCK_TYPE_STRINGIFY_SIZE +#define IMPLEMENT_STRINGIFY(type, function_postfix, printf_specifier) \ + char* C2(umocktypes_stringify_,function_postfix)(type* value) \ + { \ + char* result; \ + if (value == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": NULL value."); \ + result = NULL; \ + } \ + else \ + { \ + char temp_buffer[MAX_UMOCK_TYPE_STRINGIFY_SIZE]; \ + size_t length = snprintf(temp_buffer, MAX_UMOCK_TYPE_STRINGIFY_SIZE, printf_specifier, *value); \ + result = (char*)umockalloc_malloc(length + 1); \ + if (result == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": Cannot allocate memory for result string."); \ + } \ + else \ + { \ + (void)snprintf(result, length + 1, printf_specifier, *value); \ + } \ + } \ + return result; \ + } +#else /* MAX_STRINGIFY_SIZE */ +#define IMPLEMENT_STRINGIFY(type, function_postfix, printf_specifier) \ + char* C2(umocktypes_stringify_,function_postfix)(type* value) \ + { \ + char* result; \ + if (value == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": NULL value."); \ + result = NULL; \ + } \ + else \ + { \ + size_t length = snprintf(NULL, 0, printf_specifier, *value); \ + result = (char*)umockalloc_malloc(length + 1); \ + if (result == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": Cannot allocate memory for result string."); \ + } \ + else \ + { \ + (void)snprintf(result, length + 1, printf_specifier, *value); \ + } \ + } \ + return result; \ + } +#endif /* MAX_STRINGIFY_SIZE */ + +#define IMPLEMENT_ARE_EQUAL(type, function_postfix) \ + int C2(umocktypes_are_equal_,function_postfix)(type* left, type* right) \ + { \ + int result; \ + if ((left == NULL) || (right == NULL)) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_are_equal_,function_postfix)) ": Bad arguments: left = %p, right = %p", left, right); \ + result = -1; \ + } \ + else \ + { \ + result = ((*left) == (*right)) ? 1 : 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_COPY(type, function_postfix) \ + int C2(umocktypes_copy_,function_postfix)(type* destination, type* source) \ + { \ + int result; \ + if ((destination == NULL) || \ + (source == NULL)) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_are_equal_,function_postfix)) ": Bad arguments: destination = %p, source = %p", destination, source); \ + result = __LINE__; \ + } \ + else \ + { \ + *destination = *source; \ + result = 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_FREE(type, function_postfix) \ + void C2(umocktypes_free_,function_postfix)(type* value) \ + { \ + (void)value; \ + } + +#define IMPLEMENT_TYPE_HANDLERS(type, function_postfix, printf_specifier) \ + IMPLEMENT_STRINGIFY(type, function_postfix, printf_specifier) \ + IMPLEMENT_ARE_EQUAL(type, function_postfix) \ + IMPLEMENT_COPY(type, function_postfix) \ + IMPLEMENT_FREE(type, function_postfix) + +/* Codes_SRS_UMOCKTYPES_C_01_002: [ umocktypes_stringify_char shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_003: [ If value is NULL, umocktypes_stringify_char shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_005: [ If any other error occurs when creating the string representation, umocktypes_stringify_char shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_004: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_char shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_006: [ umocktypes_are_equal_char shall compare the 2 chars pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_007: [ If any of the arguments is NULL, umocktypes_are_equal_char shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_008: [ If the values pointed to by left and right are equal, umocktypes_are_equal_char shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_009: [ If the values pointed to by left and right are different, umocktypes_are_equal_char shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_010: [ umocktypes_copy_char shall copy the char value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_012: [ If source or destination are NULL, umocktypes_copy_char shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_011: [ On success umocktypes_copy_char shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_013: [ umocktypes_free_char shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(char, char, "%d") + +/* Codes_SRS_UMOCKTYPES_C_01_014: [ umocktypes_stringify_unsignedchar shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_015: [ If value is NULL, umocktypes_stringify_unsignedchar shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_017: [ If any other error occurs when creating the string representation, umocktypes_stringify_unsignedchar shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_016: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_unsignedchar shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_018: [ umocktypes_are_equal_unsignedchar shall compare the 2 unsigned chars pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_019: [ If any of the arguments is NULL, umocktypes_are_equal_unsignedchar shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_020: [ If the values pointed to by left and right are equal, umocktypes_are_equal_unsignedchar shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_021: [ If the values pointed to by left and right are different, umocktypes_are_equal_unsignedchar shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_022: [ umocktypes_copy_unsignedchar shall copy the unsigned char value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_024: [ If source or destination are NULL, umocktypes_copy_unsignedchar shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_023: [ On success umocktypes_copy_unsignedchar shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_025: [ umocktypes_free_unsignedchar shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(unsigned char, unsignedchar, "%u") + +/* Codes_SRS_UMOCKTYPES_C_01_026: [ umocktypes_stringify_short shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_027: [ If value is NULL, umocktypes_stringify_short shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_029: [ If any other error occurs when creating the string representation, umocktypes_stringify_short shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_028: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_short shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_030: [ umocktypes_are_equal_short shall compare the 2 shorts pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_031: [ If any of the arguments is NULL, umocktypes_are_equal_short shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_032: [ If the values pointed to by left and right are equal, umocktypes_are_equal_short shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_033: [ If the values pointed to by left and right are different, umocktypes_are_equal_short shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_034: [ umocktypes_copy_short shall copy the short value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_036: [ If source or destination are NULL, umocktypes_copy_short shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_035: [ On success umocktypes_copy_short shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_037: [ umocktypes_free_short shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(short, short, "%hd") + +/* Codes_SRS_UMOCKTYPES_C_01_038: [ umocktypes_stringify_unsignedshort shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_039: [ If value is NULL, umocktypes_stringify_unsignedshort shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_041: [ If any other error occurs when creating the string representation, umocktypes_stringify_unsignedshort shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_040: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_unsignedshort shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_042: [ umocktypes_are_equal_unsignedshort shall compare the 2 unsigned shorts pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_043: [ If any of the arguments is NULL, umocktypes_are_equal_unsignedshort shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_044: [ If the values pointed to by left and right are equal, umocktypes_are_equal_unsignedshort shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_045: [ If the values pointed to by left and right are different, umocktypes_are_equal_unsignedshort shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_046: [ umocktypes_copy_unsignedshort shall copy the unsigned short value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_048: [ If source or destination are NULL, umocktypes_copy_unsignedshort shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_047: [ On success umocktypes_copy_unsignedshort shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_049: [ umocktypes_free_unsignedshort shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(unsigned short, unsignedshort, "%hu") + +/* Codes_SRS_UMOCKTYPES_C_01_050: [ umocktypes_stringify_int shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_051: [ If value is NULL, umocktypes_stringify_int shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_053: [ If any other error occurs when creating the string representation, umocktypes_stringify_int shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_052: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_int shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_054: [ umocktypes_are_equal_int shall compare the 2 ints pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_055: [ If any of the arguments is NULL, umocktypes_are_equal_int shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_056: [ If the values pointed to by left and right are equal, umocktypes_are_equal_int shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_057: [ If the values pointed to by left and right are different, umocktypes_are_equal_int shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_058: [ umocktypes_copy_int shall copy the int value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_060: [ If source or destination are NULL, umocktypes_copy_int shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_059: [ On success umocktypes_copy_int shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_061: [ umocktypes_free_int shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(int, int, "%d") + +/* Codes_SRS_UMOCKTYPES_C_01_062: [ umocktypes_stringify_unsignedint shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_063: [ If value is NULL, umocktypes_stringify_unsignedint shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_065: [ If any other error occurs when creating the string representation, umocktypes_stringify_unsignedint shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_064: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_unsignedint shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_066: [ umocktypes_are_equal_unsignedint shall compare the 2 unsigned ints pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_067: [ If any of the arguments is NULL, umocktypes_are_equal_unsignedint shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_068: [ If the values pointed to by left and right are equal, umocktypes_are_equal_unsignedint shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_069: [ If the values pointed to by left and right are different, umocktypes_are_equal_unsignedint shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_070: [ umocktypes_copy_unsignedint shall copy the unsigned int value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_072: [ If source or destination are NULL, umocktypes_copy_unsignedint shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_071: [ On success umocktypes_copy_unsignedint shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_073: [ umocktypes_free_unsignedint shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(unsigned int, unsignedint, "%u") + +/* Codes_SRS_UMOCKTYPES_C_01_074: [ umocktypes_stringify_long shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_075: [ If value is NULL, umocktypes_stringify_long shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_077: [ If any other error occurs when creating the string representation, umocktypes_stringify_long shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_076: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_long shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_078: [ umocktypes_are_equal_long shall compare the 2 longs pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_079: [ If any of the arguments is NULL, umocktypes_are_equal_long shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_080: [ If the values pointed to by left and right are equal, umocktypes_are_equal_long shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_081: [ If the values pointed to by left and right are different, umocktypes_are_equal_long shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_082: [ umocktypes_copy_long shall copy the long value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_084: [ If source or destination are NULL, umocktypes_copy_long shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_083: [ On success umocktypes_copy_long shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_085: [ umocktypes_free_long shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(long, long, "%ld") + +/* Codes_SRS_UMOCKTYPES_C_01_086: [ umocktypes_stringify_unsignedlong shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_087: [ If value is NULL, umocktypes_stringify_unsignedlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_089: [ If any other error occurs when creating the string representation, umocktypes_stringify_unsignedlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_088: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_unsignedlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_090: [ umocktypes_are_equal_unsignedlong shall compare the 2 unsigned longs pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_091: [ If any of the arguments is NULL, umocktypes_are_equal_unsignedlong shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_092: [ If the values pointed to by left and right are equal, umocktypes_are_equal_unsignedlong shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_093: [ If the values pointed to by left and right are different, umocktypes_are_equal_unsignedlong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_094: [ umocktypes_copy_unsignedlong shall copy the unsigned long value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_096: [ If source or destination are NULL, umocktypes_copy_unsignedlong shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_095: [ On success umocktypes_copy_unsignedlong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_097: [ umocktypes_free_unsignedlong shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(unsigned long, unsignedlong, "%lu") + +/* Codes_SRS_UMOCKTYPES_C_01_098: [ umocktypes_stringify_longlong shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_099: [ If value is NULL, umocktypes_stringify_longlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_101: [ If any other error occurs when creating the string representation, umocktypes_stringify_longlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_100: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_longlong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_102: [ umocktypes_are_equal_longlong shall compare the 2 long longs pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_103: [ If any of the arguments is NULL, umocktypes_are_equal_longlong shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_104: [ If the values pointed to by left and right are equal, umocktypes_are_equal_longlong shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_105: [ If the values pointed to by left and right are different, umocktypes_are_equal_longlong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_106: [ umocktypes_copy_longlong shall copy the long long value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_108: [ If source or destination are NULL, umocktypes_copy_longlong shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_107: [ On success umocktypes_copy_longlong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_109: [ umocktypes_free_longlong shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(long long, longlong, "%lld") + +/* Codes_SRS_UMOCKTYPES_C_01_110: [ umocktypes_stringify_unsignedlonglong shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_111: [ If value is NULL, umocktypes_stringify_unsignedlonglong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_113: [ If any other error occurs when creating the string representation, umocktypes_stringify_unsignedlonglong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_112: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_unsignedlonglong shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_114: [ umocktypes_are_equal_unsignedlonglong shall compare the 2 unsigned long longs pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_115: [ If any of the arguments is NULL, umocktypes_are_equal_unsignedlonglong shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_116: [ If the values pointed to by left and right are equal, umocktypes_are_equal_unsignedlonglong shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_117: [ If the values pointed to by left and right are different, umocktypes_are_equal_unsignedlonglong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_118: [ umocktypes_copy_unsignedlonglong shall copy the unsigned long long value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_120: [ If source or destination are NULL, umocktypes_copy_unsignedlonglong shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_119: [ On success umocktypes_copy_unsignedlonglong shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_121: [ umocktypes_free_unsignedlonglong shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(unsigned long long, unsignedlonglong, "%llu") + +/* Codes_SRS_UMOCKTYPES_C_01_122: [ umocktypes_stringify_float shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_123: [ If value is NULL, umocktypes_stringify_float shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_125: [ If any other error occurs when creating the string representation, umocktypes_stringify_float shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_124: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_float shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_126: [ umocktypes_are_equal_float shall compare the 2 floats pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_127: [ If any of the arguments is NULL, umocktypes_are_equal_float shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_128: [ If the values pointed to by left and right are equal, umocktypes_are_equal_float shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_129: [ If the values pointed to by left and right are different, umocktypes_are_equal_float shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_130: [ umocktypes_copy_float shall copy the float value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_132: [ If source or destination are NULL, umocktypes_copy_float shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_131: [ On success umocktypes_copy_float shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_133: [ umocktypes_free_float shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(float, float, "%f") + +/* Codes_SRS_UMOCKTYPES_C_01_134: [ umocktypes_stringify_double shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_135: [ If value is NULL, umocktypes_stringify_double shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_137: [ If any other error occurs when creating the string representation, umocktypes_stringify_double shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_136: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_double shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_138: [ umocktypes_are_equal_double shall compare the 2 doubles pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_139: [ If any of the arguments is NULL, umocktypes_are_equal_double shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_140: [ If the values pointed to by left and right are equal, umocktypes_are_equal_double shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_141: [ If the values pointed to by left and right are different, umocktypes_are_equal_double shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_142: [ umocktypes_copy_double shall copy the double value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_144: [ If source or destination are NULL, umocktypes_copy_double shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_143: [ On success umocktypes_copy_double shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_145: [ umocktypes_free_double shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(double, double, "%f") + +/* Codes_SRS_UMOCKTYPES_C_01_146: [ umocktypes_stringify_longdouble shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_147: [ If value is NULL, umocktypes_stringify_longdouble shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_149: [ If any other error occurs when creating the string representation, umocktypes_stringify_longdouble shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_148: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_longdouble shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_150: [ umocktypes_are_equal_longdouble shall compare the 2 long doubles pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_151: [ If any of the arguments is NULL, umocktypes_are_equal_longdouble shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_152: [ If the values pointed to by left and right are equal, umocktypes_are_equal_longdouble shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_153: [ If the values pointed to by left and right are different, umocktypes_are_equal_longdouble shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_154: [ umocktypes_copy_longdouble shall copy the long double value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_156: [ If source or destination are NULL, umocktypes_copy_longdouble shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_155: [ On success umocktypes_copy_longdouble shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_157: [ umocktypes_free_longdouble shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(long double, longdouble, "%Lf") + +/* Codes_SRS_UMOCKTYPES_C_01_158: [ umocktypes_stringify_size_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_159: [ If value is NULL, umocktypes_stringify_size_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_161: [ If any other error occurs when creating the string representation, umocktypes_stringify_size_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_160: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_size_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_162: [ umocktypes_are_equal_size_t shall compare the 2 size_ts pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_163: [ If any of the arguments is NULL, umocktypes_are_equal_size_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_164: [ If the values pointed to by left and right are equal, umocktypes_are_equal_size_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_165: [ If the values pointed to by left and right are different, umocktypes_are_equal_size_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_166: [ umocktypes_copy_size_t shall copy the size_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_168: [ If source or destination are NULL, umocktypes_copy_size_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_167: [ On success umocktypes_copy_size_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_C_01_169: [ umocktypes_free_size_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(size_t, size_t, "%lu") + +IMPLEMENT_TYPE_HANDLERS(void*, void_ptr, "%p") + +int umocktypes_c_register_types(void) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_C_01_001: [ umocktypes_c_register_types shall register support for all the types in the module. ]*/ + if ((REGISTER_TYPE(char, char) != 0) || + (REGISTER_TYPE(unsigned char, unsignedchar) != 0) || + (REGISTER_TYPE(short, short) != 0) || + (REGISTER_TYPE(unsigned short, unsignedshort) != 0) || + (REGISTER_TYPE(int, int) != 0) || + (REGISTER_TYPE(unsigned int, unsignedint) != 0) || + (REGISTER_TYPE(long, long) != 0) || + (REGISTER_TYPE(unsigned long, unsignedlong) != 0) || + (REGISTER_TYPE(long long, longlong) != 0) || + (REGISTER_TYPE(unsigned long long, unsignedlonglong) != 0) || + (REGISTER_TYPE(float, float) != 0) || + (REGISTER_TYPE(double, double) != 0) || + (REGISTER_TYPE(long double, longdouble) != 0) || + (REGISTER_TYPE(size_t, size_t) != 0) || + (REGISTER_TYPE(void*, void_ptr) != 0) || + (REGISTER_TYPE(const void*, void_ptr) != 0)) + { + /* Codes_SRS_UMOCKTYPES_C_01_171: [ If registering any of the types fails, umocktypes_c_register_types shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_c_register_types: Failed registering types."); \ + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_C_01_170: [ On success, umocktypes_c_register_types shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypes_charptr.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,296 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <string.h> +#include "umocktypes.h" +#include "macro_utils.h" +#include "umocktypes_charptr.h" +#include "umockalloc.h" +#include "umock_log.h" + +char* umocktypes_stringify_charptr(const char** value) +{ + char* result; + + if (value == NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_004: [ If value is NULL, umocktypes_stringify_charptr shall return NULL. ]*/ + UMOCK_LOG("umocktypes_stringify_charptr: NULL value."); + result = NULL; + } + else + { + if (*value == NULL) + { + result = (char*)umockalloc_malloc(sizeof("NULL") + 1); + if (result != NULL) + { + (void)memcpy(result, "NULL", sizeof("NULL") + 1); + } + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_002: [ umocktypes_stringify_charptr shall return a string containing the string representation of value, enclosed by quotes ("value"). ] */ + size_t length = strlen(*value); + result = (char*)umockalloc_malloc(length + 3); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_003: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_charptr shall return NULL. ]*/ + if (result == NULL) + { + UMOCK_LOG("umocktypes_stringify_charptr: Cannot allocate memory for result."); + } + else + { + result[0] = '\"'; + (void)memcpy(result + 1, *value, length); + result[length + 1] = '\"'; + result[length + 2] = '\0'; + } + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_CHARPTR_01_005: [ umocktypes_are_equal_charptr shall compare the 2 strings pointed to by left and right. ] */ +int umocktypes_are_equal_charptr(const char** left, const char** right) +{ + int result; + + if ((left == NULL) || (right == NULL)) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_20_001: [ If any of the arguments is NULL, umocktypes_are_equal_charptr shall return -1. ]*/ + UMOCK_LOG("umocktypes_are_equal_charptr: Bad arguments:left = %p, right = %p.", left, right); + result = -1; + } + else if (*left == *right) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_007: [ If left and right are equal, umocktypes_are_equal_charptr shall return 1. ]*/ + result = 1; + } + else if ((*left == NULL) || (*right == NULL)) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_008: [ If only one of the left and right argument is NULL, umocktypes_are_equal_charptr shall return 0. ] */ + result = 0; + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_009: [ If the string pointed to by left is equal to the string pointed to by right, umocktypes_are_equal_charptr shall return 1. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_010: [ If the string pointed to by left is different than the string pointed to by right, umocktypes_are_equal_charptr shall return 0. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_006: [ The comparison shall be case sensitive. ]*/ + result = (strcmp(*left, *right) == 0) ? 1 : 0; + } + + return result; +} + +int umocktypes_copy_charptr(char** destination, const char** source) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_013: [ If source or destination are NULL, umocktypes_copy_charptr shall return a non-zero value. ]*/ + if ((destination == NULL) || (source == NULL)) + { + UMOCK_LOG("umocktypes_copy_charptr: Bad arguments: destination = %p, source = %p.", + destination, source); + result = __LINE__; + } + else + { + if (*source == NULL) + { + *destination = NULL; + result = 0; + } + else + { + size_t source_length = strlen(*source); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_012: [ The number of bytes allocated shall accomodate the string pointed to by source. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_011: [ umocktypes_copy_charptr shall allocate a new sequence of chars by using umockalloc_malloc. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_015: [ The newly allocated string shall be returned in the destination argument. ]*/ + *destination = (char*)umockalloc_malloc(source_length + 1); + if (*destination == NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_036: [ If allocating the memory for the new string fails, umocktypes_copy_charptr shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_copy_charptr: Failed allocating memory for the destination string."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_014: [ umocktypes_copy_charptr shall copy the string pointed to by source to the newly allocated memory. ]*/ + (void)memcpy(*destination, *source, source_length + 1); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_016: [ On success umocktypes_copy_charptr shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +void umocktypes_free_charptr(char** value) +{ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_018: [ If value is NULL, umocktypes_free_charptr shall do nothing. ] */ + if (value != NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_017: [ umocktypes_free_charptr shall free the string pointed to by value. ]*/ + umockalloc_free(*value); + } +} + +char* umocktypes_stringify_const_charptr(const char** value) +{ + char* result; + + if (value == NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_020: [ If value is NULL, umocktypes_stringify_const_charptr shall return NULL. ]*/ + UMOCK_LOG("umocktypes_stringify_const_charptr: NULL value."); + result = NULL; + } + else + { + if (*value == NULL) + { + result = (char*)umockalloc_malloc(sizeof("NULL") + 1); + if (result == NULL) + { + UMOCK_LOG("umocktypes_stringify_const_charptr: Cannot allocate memoryfor result string."); + } + else + { + (void)memcpy(result, "NULL", sizeof("NULL") + 1); + } + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_019: [ umocktypes_stringify_const_charptr shall return a string containing the string representation of value, enclosed by quotes ("value"). ] */ + size_t length = strlen(*value); + result = (char*)umockalloc_malloc(length + 3); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_021: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_const_charptr shall return NULL. ]*/ + if (result != NULL) + { + result[0] = '\"'; + (void)memcpy(result + 1, *value, length); + result[length + 1] = '\"'; + result[length + 2] = '\0'; + } + } + } + + return result; +} + +/* Codes_SRS_UMOCKTYPES_CHARPTR_01_022: [ umocktypes_are_equal_const_charptr shall compare the 2 strings pointed to by left and right. ] */ +int umocktypes_are_equal_const_charptr(const char** left, const char** right) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_024: [ If left and right are equal, umocktypes_are_equal_const_charptr shall return 1. ]*/ + if (left == right) + { + result = 1; + } + else if ((left == NULL) || (right == NULL)) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_025: [ If only one of the left and right argument is NULL, umocktypes_are_equal_const_charptr shall return 0. ] */ + result = 0; + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_026: [ If the string pointed to by left is equal to the string pointed to by right, umocktypes_are_equal_const_charptr shall return 1. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_027: [ If the string pointed to by left is different than the string pointed to by right, umocktypes_are_equal_const_charptr shall return 0. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_023: [ The comparison shall be case sensitive. ]*/ + if (*left == *right) + { + result = 1; + } + else + { + if ((*left == NULL) || (*right == NULL)) + { + result = 0; + } + else + { + result = (strcmp(*left, *right) == 0) ? 1 : 0; + } + } + } + + return result; +} + +int umocktypes_copy_const_charptr(const char** destination, const char** source) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_033: [ If source or destination are NULL, umocktypes_copy_const_charptr shall return a non-zero value. ]*/ + if ((destination == NULL) || (source == NULL)) + { + UMOCK_LOG("umocktypes_copy_const_charptr: Bad arguments: destination = %p, source = %p.", + destination, source); + result = __LINE__; + } + else + { + if (*source == NULL) + { + *destination = NULL; + result = 0; + } + else + { + size_t source_length = strlen(*source); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_029: [ The number of bytes allocated shall accomodate the string pointed to by source. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_028: [ umocktypes_copy_const_charptr shall allocate a new sequence of chars by using umockalloc_malloc. ]*/ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_031: [ The newly allocated string shall be returned in the destination argument. ]*/ + *destination = (char*)umockalloc_malloc(source_length + 1); + if (*destination == NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_037: [ If allocating the memory for the new string fails, umocktypes_copy_const_charptr shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_copy_const_charptr: Cannot allocate memory for destination string."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_030: [ umocktypes_copy_const_charptr shall copy the string pointed to by source to the newly allocated memory. ]*/ + (void)memcpy((void*)*destination, *source, source_length + 1); + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_032: [ On success umocktypes_copy_const_charptr shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +void umocktypes_free_const_charptr(const char** value) +{ + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_035: [ If value is NULL, umocktypes_free_const_charptr shall do nothing. ] */ + if (value != NULL) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_034: [ umocktypes_free_const_charptr shall free the string pointed to by value. ]*/ + umockalloc_free((void*)*value); + } +} + +int umocktypes_charptr_register_types(void) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_001: [ umocktypes_charptr_register_types shall register support for the types char\* and const char\* by using the REGISTER_UMOCK_VALUE_TYPE macro provided by umockc. ]*/ + if ((REGISTER_TYPE(char*, charptr) != 0) || + (REGISTER_TYPE(const char*, const_charptr) != 0)) + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_039: [ If registering any of the types fails, umocktypes_charptr_register_types shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_charptr_register_types: Cannot register types."); + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_CHARPTR_01_038: [ On success, umocktypes_charptr_register_types shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/deps/umock-c/src/umocktypes_stdint.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <inttypes.h> +#include "macro_utils.h" +#include "umocktypes.h" +#include "umocktypes_stdint.h" +#include "umockalloc.h" +#include "umock_log.h" + +#define IMPLEMENT_STRINGIFY(type, function_postfix, printf_specifier) \ + char* C2(umocktypes_stringify_,function_postfix)(type* value) \ + { \ + char* result; \ + if (value == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": NULL value."); \ + result = NULL; \ + } \ + else \ + { \ + char temp_buffer[32]; \ + size_t length = sprintf(temp_buffer, printf_specifier, *value); \ + result = (char*)umockalloc_malloc(length + 1); \ + if (result == NULL) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_stringify_,function_postfix)) ": Cannot allocate memory for result string."); \ + } \ + else \ + { \ + (void)memcpy(result, temp_buffer, length + 1); \ + } \ + } \ + return result; \ + } + +#define IMPLEMENT_ARE_EQUAL(type, function_postfix) \ + int C2(umocktypes_are_equal_,function_postfix)(type* left, type* right) \ + { \ + int result; \ + if ((left == NULL) || (right == NULL)) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_are_equal_,function_postfix)) ": Bad arguments: left = %p, right = %p", left, right); \ + result = -1; \ + } \ + else \ + { \ + result = ((*left) == (*right)) ? 1 : 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_COPY(type, function_postfix) \ + int C2(umocktypes_copy_,function_postfix)(type* destination, type* source) \ + { \ + int result; \ + if ((destination == NULL) || \ + (source == NULL)) \ + { \ + UMOCK_LOG(TOSTRING(C2(umocktypes_are_equal_,function_postfix)) ": Bad arguments: destination = %p, source = %p", destination, source); \ + result = __LINE__; \ + } \ + else \ + { \ + *destination = *source; \ + result = 0; \ + } \ + return result; \ + } + +#define IMPLEMENT_FREE(type, function_postfix) \ + void C2(umocktypes_free_,function_postfix)(type* value) \ + { \ + (void)value; \ + } + +#define IMPLEMENT_TYPE_HANDLERS(type, function_postfix, printf_specifier) \ + IMPLEMENT_STRINGIFY(type, function_postfix, printf_specifier) \ + IMPLEMENT_ARE_EQUAL(type, function_postfix) \ + IMPLEMENT_COPY(type, function_postfix) \ + IMPLEMENT_FREE(type, function_postfix) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_004: [ umocktypes_stringify_uint8_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_005: [ If value is NULL, umocktypes_stringify_uint8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_006: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_uint8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_007: [ If any other error occurs when creating the string representation, umocktypes_stringify_uint8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_008: [ umocktypes_are_equal_uint8_t shall compare the 2 uint8_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_009: [ If any of the arguments is NULL, umocktypes_are_equal_uint8_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_010: [ If the values pointed to by left and right are equal, umocktypes_are_equal_uint8_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_011: [ If the values pointed to by left and right are different, umocktypes_are_equal_uint8_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_012: [ umocktypes_copy_uint8_t shall copy the uint8_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_013: [ On success umocktypes_copy_uint8_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_014: [ If source or destination are NULL, umocktypes_copy_uint8_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_015: [ umocktypes_free_uint8_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(uint8_t, uint8_t, "%"PRIu8) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_016: [ umocktypes_stringify_int8_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_017: [ If value is NULL, umocktypes_stringify_int8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_018: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_int8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_019: [ If any other error occurs when creating the string representation, umocktypes_stringify_int8_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_020: [ umocktypes_are_equal_int8_t shall compare the 2 int8_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_021: [ If any of the arguments is NULL, umocktypes_are_equal_int8_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_022: [ If the values pointed to by left and right are equal, umocktypes_are_equal_int8_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_023: [ If the values pointed to by left and right are different, umocktypes_are_equal_int8_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_024: [ umocktypes_copy_int8_t shall copy the int8_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_025: [ On success umocktypes_copy_int8_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_026: [ If source or destination are NULL, umocktypes_copy_int8_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_027: [ umocktypes_free_int8_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(int8_t, int8_t, "%"PRId8) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_028: [ umocktypes_stringify_uint16_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_029: [ If value is NULL, umocktypes_stringify_uint16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_030: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_uint16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_031: [ If any other error occurs when creating the string representation, umocktypes_stringify_uint16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_032: [ umocktypes_are_equal_uint16_t shall compare the 2 uint16_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_033: [ If any of the arguments is NULL, umocktypes_are_equal_uint16_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_034: [ If the values pointed to by left and right are equal, umocktypes_are_equal_uint16_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_035: [ If the values pointed to by left and right are different, umocktypes_are_equal_uint16_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_036: [ umocktypes_copy_uint16_t shall copy the uint16_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_037: [ On success umocktypes_copy_uint16_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_038: [ If source or destination are NULL, umocktypes_copy_uint16_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_039: [ umocktypes_free_uint16_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(uint16_t, uint16_t, "%"PRIu16) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_040: [ umocktypes_stringify_int16_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_041: [ If value is NULL, umocktypes_stringify_int16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_042: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_int16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_043: [ If any other error occurs when creating the string representation, umocktypes_stringify_int16_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_044: [ umocktypes_are_equal_int16_t shall compare the 2 int16_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_045: [ If any of the arguments is NULL, umocktypes_are_equal_int16_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_046: [ If the values pointed to by left and right are equal, umocktypes_are_equal_int16_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_047: [ If the values pointed to by left and right are different, umocktypes_are_equal_int16_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_048: [ umocktypes_copy_int16_t shall copy the int16_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_049: [ On success umocktypes_copy_int16_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_050: [ If source or destination are NULL, umocktypes_copy_int16_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_051: [ umocktypes_free_int16_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(int16_t, int16_t, "%"PRId16) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_052: [ umocktypes_stringify_uint32_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_053: [ If value is NULL, umocktypes_stringify_uint32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_054: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_uint32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_055: [ If any other error occurs when creating the string representation, umocktypes_stringify_uint32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_056: [ umocktypes_are_equal_uint32_t shall compare the 2 uint32_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_057: [ If any of the arguments is NULL, umocktypes_are_equal_uint32_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_058: [ If the values pointed to by left and right are equal, umocktypes_are_equal_uint32_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_059: [ If the values pointed to by left and right are different, umocktypes_are_equal_uint32_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_060: [ umocktypes_copy_uint32_t shall copy the uint32_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_061: [ On success umocktypes_copy_uint32_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_062: [ If source or destination are NULL, umocktypes_copy_uint32_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_063: [ umocktypes_free_uint32_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(uint32_t, uint32_t, "%"PRIu32) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_064: [ umocktypes_stringify_int32_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_065: [ If value is NULL, umocktypes_stringify_int32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_066: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_int32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_067: [ If any other error occurs when creating the string representation, umocktypes_stringify_int32_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_068: [ umocktypes_are_equal_int32_t shall compare the 2 int32_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_069: [ If any of the arguments is NULL, umocktypes_are_equal_int32_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_070: [ If the values pointed to by left and right are equal, umocktypes_are_equal_int32_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_071: [ If the values pointed to by left and right are different, umocktypes_are_equal_int32_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_072: [ umocktypes_copy_int32_t shall copy the int32_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_073: [ On success umocktypes_copy_int32_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_074: [ If source or destination are NULL, umocktypes_copy_int32_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_075: [ umocktypes_free_int32_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(int32_t, int32_t, "%"PRId32) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_076: [ umocktypes_stringify_uint64_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_077: [ If value is NULL, umocktypes_stringify_uint64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_078: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_uint64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_079: [ If any other error occurs when creating the string representation, umocktypes_stringify_uint64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_080: [ umocktypes_are_equal_uint64_t shall compare the 2 uint64_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_081: [ If any of the arguments is NULL, umocktypes_are_equal_uint64_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_082: [ If the values pointed to by left and right are equal, umocktypes_are_equal_uint64_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_083: [ If the values pointed to by left and right are different, umocktypes_are_equal_uint64_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_084: [ umocktypes_copy_uint64_t shall copy the uint64_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_085: [ On success umocktypes_copy_uint64_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_086: [ If source or destination are NULL, umocktypes_copy_uint64_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_087: [ umocktypes_free_uint64_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(uint64_t, uint64_t, "%"PRIu64) + +/* Codes_SRS_UMOCKTYPES_STDINT_01_088: [ umocktypes_stringify_int64_t shall return the string representation of value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_089: [ If value is NULL, umocktypes_stringify_int64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_090: [ If allocating a new string to hold the string representation fails, umocktypes_stringify_int64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_091: [ If any other error occurs when creating the string representation, umocktypes_stringify_int64_t shall return NULL. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_092: [ umocktypes_are_equal_int64_t shall compare the 2 int64_t values pointed to by left and right. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_093: [ If any of the arguments is NULL, umocktypes_are_equal_int64_t shall return -1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_094: [ If the values pointed to by left and right are equal, umocktypes_are_equal_int64_t shall return 1. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_095: [ If the values pointed to by left and right are different, umocktypes_are_equal_int64_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_096: [ umocktypes_copy_int64_t shall copy the int64_t value from source to destination. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_097: [ On success umocktypes_copy_int64_t shall return 0. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_098: [ If source or destination are NULL, umocktypes_copy_int64_t shall return a non-zero value. ]*/ +/* Codes_SRS_UMOCKTYPES_STDINT_01_099: [ umocktypes_free_int64_t shall do nothing. ]*/ +IMPLEMENT_TYPE_HANDLERS(int64_t, int64_t, "%"PRId64) + +int umocktypes_stdint_register_types(void) +{ + int result; + + /* Codes_SRS_UMOCKTYPES_STDINT_01_001: [ umocktypes_stdint_register_types shall register support for all the types in the module. ]*/ + if ((REGISTER_TYPE(uint8_t, uint8_t) != 0) || + (REGISTER_TYPE(int8_t, int8_t) != 0) || + (REGISTER_TYPE(uint16_t, uint16_t) != 0) || + (REGISTER_TYPE(int16_t, int16_t) != 0) || + (REGISTER_TYPE(uint32_t, uint32_t) != 0) || + (REGISTER_TYPE(int32_t, int32_t) != 0) || + (REGISTER_TYPE(uint64_t, uint64_t) != 0) || + (REGISTER_TYPE(int64_t, int64_t) != 0)) + { + /* Codes_SRS_UMOCKTYPES_STDINT_01_171: [ If registering any of the types fails, umocktypes_stdint_register_types shall fail and return a non-zero value. ]*/ + UMOCK_LOG("umocktypes_stdint_register_types: Failed registering types."); \ + result = __LINE__; + } + else + { + /* Codes_SRS_UMOCKTYPES_STDINT_01_170: [ On success, umocktypes_stdint_register_types shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,87 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_H +#define AMQP_DEFINITIONS_H + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#include "azure_uamqp_c/amqp_definitions_role.h" +#include "azure_uamqp_c/amqp_definitions_sender_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_receiver_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_handle.h" +#include "azure_uamqp_c/amqp_definitions_seconds.h" +#include "azure_uamqp_c/amqp_definitions_milliseconds.h" +#include "azure_uamqp_c/amqp_definitions_delivery_tag.h" +#include "azure_uamqp_c/amqp_definitions_sequence_no.h" +#include "azure_uamqp_c/amqp_definitions_delivery_number.h" +#include "azure_uamqp_c/amqp_definitions_transfer_number.h" +#include "azure_uamqp_c/amqp_definitions_message_format.h" +#include "azure_uamqp_c/amqp_definitions_ietf_language_tag.h" +#include "azure_uamqp_c/amqp_definitions_fields.h" +#include "azure_uamqp_c/amqp_definitions_error.h" +#include "azure_uamqp_c/amqp_definitions_amqp_error.h" +#include "azure_uamqp_c/amqp_definitions_connection_error.h" +#include "azure_uamqp_c/amqp_definitions_session_error.h" +#include "azure_uamqp_c/amqp_definitions_link_error.h" +#include "azure_uamqp_c/amqp_definitions_open.h" +#include "azure_uamqp_c/amqp_definitions_begin.h" +#include "azure_uamqp_c/amqp_definitions_attach.h" +#include "azure_uamqp_c/amqp_definitions_flow.h" +#include "azure_uamqp_c/amqp_definitions_transfer.h" +#include "azure_uamqp_c/amqp_definitions_disposition.h" +#include "azure_uamqp_c/amqp_definitions_detach.h" +#include "azure_uamqp_c/amqp_definitions_end.h" +#include "azure_uamqp_c/amqp_definitions_close.h" +#include "azure_uamqp_c/amqp_definitions_sasl_code.h" +#include "azure_uamqp_c/amqp_definitions_sasl_mechanisms.h" +#include "azure_uamqp_c/amqp_definitions_sasl_init.h" +#include "azure_uamqp_c/amqp_definitions_sasl_challenge.h" +#include "azure_uamqp_c/amqp_definitions_sasl_response.h" +#include "azure_uamqp_c/amqp_definitions_sasl_outcome.h" +#include "azure_uamqp_c/amqp_definitions_terminus_durability.h" +#include "azure_uamqp_c/amqp_definitions_terminus_expiry_policy.h" +#include "azure_uamqp_c/amqp_definitions_node_properties.h" +#include "azure_uamqp_c/amqp_definitions_filter_set.h" +#include "azure_uamqp_c/amqp_definitions_source.h" +#include "azure_uamqp_c/amqp_definitions_target.h" +#include "azure_uamqp_c/amqp_definitions_annotations.h" +#include "azure_uamqp_c/amqp_definitions_message_id_ulong.h" +#include "azure_uamqp_c/amqp_definitions_message_id_uuid.h" +#include "azure_uamqp_c/amqp_definitions_message_id_binary.h" +#include "azure_uamqp_c/amqp_definitions_message_id_string.h" +#include "azure_uamqp_c/amqp_definitions_address_string.h" +#include "azure_uamqp_c/amqp_definitions_header.h" +#include "azure_uamqp_c/amqp_definitions_delivery_annotations.h" +#include "azure_uamqp_c/amqp_definitions_message_annotations.h" +#include "azure_uamqp_c/amqp_definitions_application_properties.h" +#include "azure_uamqp_c/amqp_definitions_data.h" +#include "azure_uamqp_c/amqp_definitions_amqp_sequence.h" +#include "azure_uamqp_c/amqp_definitions_amqp_value.h" +#include "azure_uamqp_c/amqp_definitions_footer.h" +#include "azure_uamqp_c/amqp_definitions_properties.h" +#include "azure_uamqp_c/amqp_definitions_received.h" +#include "azure_uamqp_c/amqp_definitions_accepted.h" +#include "azure_uamqp_c/amqp_definitions_rejected.h" +#include "azure_uamqp_c/amqp_definitions_released.h" +#include "azure_uamqp_c/amqp_definitions_modified.h" + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_accepted.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ACCEPTED_H +#define AMQP_DEFINITIONS_ACCEPTED_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct ACCEPTED_INSTANCE_TAG* ACCEPTED_HANDLE; + + MOCKABLE_FUNCTION(, ACCEPTED_HANDLE, accepted_create ); + MOCKABLE_FUNCTION(, ACCEPTED_HANDLE, accepted_clone, ACCEPTED_HANDLE, value); + MOCKABLE_FUNCTION(, void, accepted_destroy, ACCEPTED_HANDLE, accepted); + MOCKABLE_FUNCTION(, bool, is_accepted_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_accepted, AMQP_VALUE, value, ACCEPTED_HANDLE*, ACCEPTED_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_accepted, ACCEPTED_HANDLE, accepted); + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ACCEPTED_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_address_string.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ADDRESS_STRING_H +#define AMQP_DEFINITIONS_ADDRESS_STRING_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* address_string; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_address_string, address_string, value); + + + #define amqpvalue_get_address_string amqpvalue_get_string + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ADDRESS_STRING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_amqp_error.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,51 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_AMQP_ERROR_H +#define AMQP_DEFINITIONS_AMQP_ERROR_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* amqp_error; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_amqp_error, amqp_error, value); + + + #define amqpvalue_get_amqp_error amqpvalue_get_symbol + + #define amqp_error_internal_error "amqp:internal-error" + #define amqp_error_not_found "amqp:not-found" + #define amqp_error_unauthorized_access "amqp:unauthorized-access" + #define amqp_error_decode_error "amqp:decode-error" + #define amqp_error_resource_limit_exceeded "amqp:resource-limit-exceeded" + #define amqp_error_not_allowed "amqp:not-allowed" + #define amqp_error_invalid_field "amqp:invalid-field" + #define amqp_error_not_implemented "amqp:not-implemented" + #define amqp_error_resource_locked "amqp:resource-locked" + #define amqp_error_precondition_failed "amqp:precondition-failed" + #define amqp_error_resource_deleted "amqp:resource-deleted" + #define amqp_error_illegal_state "amqp:illegal-state" + #define amqp_error_frame_size_too_small "amqp:frame-size-too-small" + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_AMQP_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_amqp_sequence.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_AMQP_SEQUENCE_H +#define AMQP_DEFINITIONS_AMQP_SEQUENCE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE amqp_sequence; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_amqp_sequence, AMQP_VALUE, value); + #define amqp_sequence_clone amqpvalue_clone + #define amqp_sequence_destroy amqpvalue_destroy + + MOCKABLE_FUNCTION(, bool, is_amqp_sequence_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_amqp_sequence amqpvalue_get_list + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_AMQP_SEQUENCE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_amqp_value.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_AMQP_VALUE_H +#define AMQP_DEFINITIONS_AMQP_VALUE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE amqp_value; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_amqp_value, AMQP_VALUE, value); + #define amqp_value_clone amqpvalue_clone + #define amqp_value_destroy amqpvalue_destroy + + MOCKABLE_FUNCTION(, bool, is_amqp_value_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_amqp_value amqpvalue_get_* + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_AMQP_VALUE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_annotations.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ANNOTATIONS_H +#define AMQP_DEFINITIONS_ANNOTATIONS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE annotations; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_annotations, AMQP_VALUE, value); + #define annotations_clone amqpvalue_clone + #define annotations_destroy amqpvalue_destroy + + + #define amqpvalue_get_annotations amqpvalue_get_map + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ANNOTATIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_application_properties.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_APPLICATION_PROPERTIES_H +#define AMQP_DEFINITIONS_APPLICATION_PROPERTIES_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE application_properties; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_application_properties, AMQP_VALUE, value); + #define application_properties_clone amqpvalue_clone + #define application_properties_destroy amqpvalue_destroy + + MOCKABLE_FUNCTION(, bool, is_application_properties_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_application_properties amqpvalue_get_map + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_APPLICATION_PROPERTIES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_attach.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,67 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ATTACH_H +#define AMQP_DEFINITIONS_ATTACH_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct ATTACH_INSTANCE_TAG* ATTACH_HANDLE; + + MOCKABLE_FUNCTION(, ATTACH_HANDLE, attach_create , const char*, name_value, handle, handle_value, role, role_value); + MOCKABLE_FUNCTION(, ATTACH_HANDLE, attach_clone, ATTACH_HANDLE, value); + MOCKABLE_FUNCTION(, void, attach_destroy, ATTACH_HANDLE, attach); + MOCKABLE_FUNCTION(, bool, is_attach_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_attach, AMQP_VALUE, value, ATTACH_HANDLE*, ATTACH_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_attach, ATTACH_HANDLE, attach); + + MOCKABLE_FUNCTION(, int, attach_get_name, ATTACH_HANDLE, attach, const char**, name_value); + MOCKABLE_FUNCTION(, int, attach_set_name, ATTACH_HANDLE, attach, const char*, name_value); + MOCKABLE_FUNCTION(, int, attach_get_handle, ATTACH_HANDLE, attach, handle*, handle_value); + MOCKABLE_FUNCTION(, int, attach_set_handle, ATTACH_HANDLE, attach, handle, handle_value); + MOCKABLE_FUNCTION(, int, attach_get_role, ATTACH_HANDLE, attach, role*, role_value); + MOCKABLE_FUNCTION(, int, attach_set_role, ATTACH_HANDLE, attach, role, role_value); + MOCKABLE_FUNCTION(, int, attach_get_snd_settle_mode, ATTACH_HANDLE, attach, sender_settle_mode*, snd_settle_mode_value); + MOCKABLE_FUNCTION(, int, attach_set_snd_settle_mode, ATTACH_HANDLE, attach, sender_settle_mode, snd_settle_mode_value); + MOCKABLE_FUNCTION(, int, attach_get_rcv_settle_mode, ATTACH_HANDLE, attach, receiver_settle_mode*, rcv_settle_mode_value); + MOCKABLE_FUNCTION(, int, attach_set_rcv_settle_mode, ATTACH_HANDLE, attach, receiver_settle_mode, rcv_settle_mode_value); + MOCKABLE_FUNCTION(, int, attach_get_source, ATTACH_HANDLE, attach, AMQP_VALUE*, source_value); + MOCKABLE_FUNCTION(, int, attach_set_source, ATTACH_HANDLE, attach, AMQP_VALUE, source_value); + MOCKABLE_FUNCTION(, int, attach_get_target, ATTACH_HANDLE, attach, AMQP_VALUE*, target_value); + MOCKABLE_FUNCTION(, int, attach_set_target, ATTACH_HANDLE, attach, AMQP_VALUE, target_value); + MOCKABLE_FUNCTION(, int, attach_get_unsettled, ATTACH_HANDLE, attach, AMQP_VALUE*, unsettled_value); + MOCKABLE_FUNCTION(, int, attach_set_unsettled, ATTACH_HANDLE, attach, AMQP_VALUE, unsettled_value); + MOCKABLE_FUNCTION(, int, attach_get_incomplete_unsettled, ATTACH_HANDLE, attach, bool*, incomplete_unsettled_value); + MOCKABLE_FUNCTION(, int, attach_set_incomplete_unsettled, ATTACH_HANDLE, attach, bool, incomplete_unsettled_value); + MOCKABLE_FUNCTION(, int, attach_get_initial_delivery_count, ATTACH_HANDLE, attach, sequence_no*, initial_delivery_count_value); + MOCKABLE_FUNCTION(, int, attach_set_initial_delivery_count, ATTACH_HANDLE, attach, sequence_no, initial_delivery_count_value); + MOCKABLE_FUNCTION(, int, attach_get_max_message_size, ATTACH_HANDLE, attach, uint64_t*, max_message_size_value); + MOCKABLE_FUNCTION(, int, attach_set_max_message_size, ATTACH_HANDLE, attach, uint64_t, max_message_size_value); + MOCKABLE_FUNCTION(, int, attach_get_offered_capabilities, ATTACH_HANDLE, attach, AMQP_VALUE*, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, attach_set_offered_capabilities, ATTACH_HANDLE, attach, AMQP_VALUE, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, attach_get_desired_capabilities, ATTACH_HANDLE, attach, AMQP_VALUE*, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, attach_set_desired_capabilities, ATTACH_HANDLE, attach, AMQP_VALUE, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, attach_get_properties, ATTACH_HANDLE, attach, fields*, properties_value); + MOCKABLE_FUNCTION(, int, attach_set_properties, ATTACH_HANDLE, attach, fields, properties_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ATTACH_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_begin.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,55 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_BEGIN_H +#define AMQP_DEFINITIONS_BEGIN_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct BEGIN_INSTANCE_TAG* BEGIN_HANDLE; + + MOCKABLE_FUNCTION(, BEGIN_HANDLE, begin_create , transfer_number, next_outgoing_id_value, uint32_t, incoming_window_value, uint32_t, outgoing_window_value); + MOCKABLE_FUNCTION(, BEGIN_HANDLE, begin_clone, BEGIN_HANDLE, value); + MOCKABLE_FUNCTION(, void, begin_destroy, BEGIN_HANDLE, begin); + MOCKABLE_FUNCTION(, bool, is_begin_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_begin, AMQP_VALUE, value, BEGIN_HANDLE*, BEGIN_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_begin, BEGIN_HANDLE, begin); + + MOCKABLE_FUNCTION(, int, begin_get_remote_channel, BEGIN_HANDLE, begin, uint16_t*, remote_channel_value); + MOCKABLE_FUNCTION(, int, begin_set_remote_channel, BEGIN_HANDLE, begin, uint16_t, remote_channel_value); + MOCKABLE_FUNCTION(, int, begin_get_next_outgoing_id, BEGIN_HANDLE, begin, transfer_number*, next_outgoing_id_value); + MOCKABLE_FUNCTION(, int, begin_set_next_outgoing_id, BEGIN_HANDLE, begin, transfer_number, next_outgoing_id_value); + MOCKABLE_FUNCTION(, int, begin_get_incoming_window, BEGIN_HANDLE, begin, uint32_t*, incoming_window_value); + MOCKABLE_FUNCTION(, int, begin_set_incoming_window, BEGIN_HANDLE, begin, uint32_t, incoming_window_value); + MOCKABLE_FUNCTION(, int, begin_get_outgoing_window, BEGIN_HANDLE, begin, uint32_t*, outgoing_window_value); + MOCKABLE_FUNCTION(, int, begin_set_outgoing_window, BEGIN_HANDLE, begin, uint32_t, outgoing_window_value); + MOCKABLE_FUNCTION(, int, begin_get_handle_max, BEGIN_HANDLE, begin, handle*, handle_max_value); + MOCKABLE_FUNCTION(, int, begin_set_handle_max, BEGIN_HANDLE, begin, handle, handle_max_value); + MOCKABLE_FUNCTION(, int, begin_get_offered_capabilities, BEGIN_HANDLE, begin, AMQP_VALUE*, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, begin_set_offered_capabilities, BEGIN_HANDLE, begin, AMQP_VALUE, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, begin_get_desired_capabilities, BEGIN_HANDLE, begin, AMQP_VALUE*, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, begin_set_desired_capabilities, BEGIN_HANDLE, begin, AMQP_VALUE, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, begin_get_properties, BEGIN_HANDLE, begin, fields*, properties_value); + MOCKABLE_FUNCTION(, int, begin_set_properties, BEGIN_HANDLE, begin, fields, properties_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_BEGIN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_close.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_CLOSE_H +#define AMQP_DEFINITIONS_CLOSE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct CLOSE_INSTANCE_TAG* CLOSE_HANDLE; + + MOCKABLE_FUNCTION(, CLOSE_HANDLE, close_create ); + MOCKABLE_FUNCTION(, CLOSE_HANDLE, close_clone, CLOSE_HANDLE, value); + MOCKABLE_FUNCTION(, void, close_destroy, CLOSE_HANDLE, close); + MOCKABLE_FUNCTION(, bool, is_close_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_close, AMQP_VALUE, value, CLOSE_HANDLE*, CLOSE_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_close, CLOSE_HANDLE, close); + + MOCKABLE_FUNCTION(, int, close_get_error, CLOSE_HANDLE, close, ERROR_HANDLE*, error_value); + MOCKABLE_FUNCTION(, int, close_set_error, CLOSE_HANDLE, close, ERROR_HANDLE, error_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_CLOSE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_connection_error.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_CONNECTION_ERROR_H +#define AMQP_DEFINITIONS_CONNECTION_ERROR_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* connection_error; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_connection_error, connection_error, value); + + + #define amqpvalue_get_connection_error amqpvalue_get_symbol + + #define connection_error_connection_forced "amqp:connection:forced" + #define connection_error_framing_error "amqp:connection:framing-error" + #define connection_error_redirect "amqp:connection:redirect" + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_CONNECTION_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_data.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DATA_H +#define AMQP_DEFINITIONS_DATA_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef amqp_binary data; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_data, data, value); + + MOCKABLE_FUNCTION(, bool, is_data_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_data amqpvalue_get_binary + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DATA_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_delivery_annotations.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DELIVERY_ANNOTATIONS_H +#define AMQP_DEFINITIONS_DELIVERY_ANNOTATIONS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef annotations delivery_annotations; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_delivery_annotations, delivery_annotations, value); + + MOCKABLE_FUNCTION(, bool, is_delivery_annotations_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_delivery_annotations amqpvalue_get_annotations + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DELIVERY_ANNOTATIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_delivery_number.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DELIVERY_NUMBER_H +#define AMQP_DEFINITIONS_DELIVERY_NUMBER_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef sequence_no delivery_number; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_delivery_number, delivery_number, value); + + + #define amqpvalue_get_delivery_number amqpvalue_get_sequence_no + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DELIVERY_NUMBER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_delivery_tag.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DELIVERY_TAG_H +#define AMQP_DEFINITIONS_DELIVERY_TAG_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef amqp_binary delivery_tag; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_delivery_tag, delivery_tag, value); + + + #define amqpvalue_get_delivery_tag amqpvalue_get_binary + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DELIVERY_TAG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_detach.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DETACH_H +#define AMQP_DEFINITIONS_DETACH_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct DETACH_INSTANCE_TAG* DETACH_HANDLE; + + MOCKABLE_FUNCTION(, DETACH_HANDLE, detach_create , handle, handle_value); + MOCKABLE_FUNCTION(, DETACH_HANDLE, detach_clone, DETACH_HANDLE, value); + MOCKABLE_FUNCTION(, void, detach_destroy, DETACH_HANDLE, detach); + MOCKABLE_FUNCTION(, bool, is_detach_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_detach, AMQP_VALUE, value, DETACH_HANDLE*, DETACH_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_detach, DETACH_HANDLE, detach); + + MOCKABLE_FUNCTION(, int, detach_get_handle, DETACH_HANDLE, detach, handle*, handle_value); + MOCKABLE_FUNCTION(, int, detach_set_handle, DETACH_HANDLE, detach, handle, handle_value); + MOCKABLE_FUNCTION(, int, detach_get_closed, DETACH_HANDLE, detach, bool*, closed_value); + MOCKABLE_FUNCTION(, int, detach_set_closed, DETACH_HANDLE, detach, bool, closed_value); + MOCKABLE_FUNCTION(, int, detach_get_error, DETACH_HANDLE, detach, ERROR_HANDLE*, error_value); + MOCKABLE_FUNCTION(, int, detach_set_error, DETACH_HANDLE, detach, ERROR_HANDLE, error_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DETACH_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_disposition.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,51 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_DISPOSITION_H +#define AMQP_DEFINITIONS_DISPOSITION_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct DISPOSITION_INSTANCE_TAG* DISPOSITION_HANDLE; + + MOCKABLE_FUNCTION(, DISPOSITION_HANDLE, disposition_create , role, role_value, delivery_number, first_value); + MOCKABLE_FUNCTION(, DISPOSITION_HANDLE, disposition_clone, DISPOSITION_HANDLE, value); + MOCKABLE_FUNCTION(, void, disposition_destroy, DISPOSITION_HANDLE, disposition); + MOCKABLE_FUNCTION(, bool, is_disposition_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_disposition, AMQP_VALUE, value, DISPOSITION_HANDLE*, DISPOSITION_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_disposition, DISPOSITION_HANDLE, disposition); + + MOCKABLE_FUNCTION(, int, disposition_get_role, DISPOSITION_HANDLE, disposition, role*, role_value); + MOCKABLE_FUNCTION(, int, disposition_set_role, DISPOSITION_HANDLE, disposition, role, role_value); + MOCKABLE_FUNCTION(, int, disposition_get_first, DISPOSITION_HANDLE, disposition, delivery_number*, first_value); + MOCKABLE_FUNCTION(, int, disposition_set_first, DISPOSITION_HANDLE, disposition, delivery_number, first_value); + MOCKABLE_FUNCTION(, int, disposition_get_last, DISPOSITION_HANDLE, disposition, delivery_number*, last_value); + MOCKABLE_FUNCTION(, int, disposition_set_last, DISPOSITION_HANDLE, disposition, delivery_number, last_value); + MOCKABLE_FUNCTION(, int, disposition_get_settled, DISPOSITION_HANDLE, disposition, bool*, settled_value); + MOCKABLE_FUNCTION(, int, disposition_set_settled, DISPOSITION_HANDLE, disposition, bool, settled_value); + MOCKABLE_FUNCTION(, int, disposition_get_state, DISPOSITION_HANDLE, disposition, AMQP_VALUE*, state_value); + MOCKABLE_FUNCTION(, int, disposition_set_state, DISPOSITION_HANDLE, disposition, AMQP_VALUE, state_value); + MOCKABLE_FUNCTION(, int, disposition_get_batchable, DISPOSITION_HANDLE, disposition, bool*, batchable_value); + MOCKABLE_FUNCTION(, int, disposition_set_batchable, DISPOSITION_HANDLE, disposition, bool, batchable_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_DISPOSITION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_end.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_END_H +#define AMQP_DEFINITIONS_END_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct END_INSTANCE_TAG* END_HANDLE; + + MOCKABLE_FUNCTION(, END_HANDLE, end_create ); + MOCKABLE_FUNCTION(, END_HANDLE, end_clone, END_HANDLE, value); + MOCKABLE_FUNCTION(, void, end_destroy, END_HANDLE, end); + MOCKABLE_FUNCTION(, bool, is_end_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_end, AMQP_VALUE, value, END_HANDLE*, END_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_end, END_HANDLE, end); + + MOCKABLE_FUNCTION(, int, end_get_error, END_HANDLE, end, ERROR_HANDLE*, error_value); + MOCKABLE_FUNCTION(, int, end_set_error, END_HANDLE, end, ERROR_HANDLE, error_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_END_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_error.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ERROR_H +#define AMQP_DEFINITIONS_ERROR_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct ERROR_INSTANCE_TAG* ERROR_HANDLE; + + MOCKABLE_FUNCTION(, ERROR_HANDLE, error_create , const char*, condition_value); + MOCKABLE_FUNCTION(, ERROR_HANDLE, error_clone, ERROR_HANDLE, value); + MOCKABLE_FUNCTION(, void, error_destroy, ERROR_HANDLE, error); + MOCKABLE_FUNCTION(, bool, is_error_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_error, AMQP_VALUE, value, ERROR_HANDLE*, ERROR_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_error, ERROR_HANDLE, error); + + MOCKABLE_FUNCTION(, int, error_get_condition, ERROR_HANDLE, error, const char**, condition_value); + MOCKABLE_FUNCTION(, int, error_set_condition, ERROR_HANDLE, error, const char*, condition_value); + MOCKABLE_FUNCTION(, int, error_get_description, ERROR_HANDLE, error, const char**, description_value); + MOCKABLE_FUNCTION(, int, error_set_description, ERROR_HANDLE, error, const char*, description_value); + MOCKABLE_FUNCTION(, int, error_get_info, ERROR_HANDLE, error, fields*, info_value); + MOCKABLE_FUNCTION(, int, error_set_info, ERROR_HANDLE, error, fields, info_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_fields.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_FIELDS_H +#define AMQP_DEFINITIONS_FIELDS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE fields; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_fields, AMQP_VALUE, value); + #define fields_clone amqpvalue_clone + #define fields_destroy amqpvalue_destroy + + + #define amqpvalue_get_fields amqpvalue_get_map + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_FIELDS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_filter_set.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_FILTER_SET_H +#define AMQP_DEFINITIONS_FILTER_SET_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef AMQP_VALUE filter_set; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_filter_set, AMQP_VALUE, value); + #define filter_set_clone amqpvalue_clone + #define filter_set_destroy amqpvalue_destroy + + + #define amqpvalue_get_filter_set amqpvalue_get_map + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_FILTER_SET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_flow.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,61 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_FLOW_H +#define AMQP_DEFINITIONS_FLOW_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct FLOW_INSTANCE_TAG* FLOW_HANDLE; + + MOCKABLE_FUNCTION(, FLOW_HANDLE, flow_create , uint32_t, incoming_window_value, transfer_number, next_outgoing_id_value, uint32_t, outgoing_window_value); + MOCKABLE_FUNCTION(, FLOW_HANDLE, flow_clone, FLOW_HANDLE, value); + MOCKABLE_FUNCTION(, void, flow_destroy, FLOW_HANDLE, flow); + MOCKABLE_FUNCTION(, bool, is_flow_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_flow, AMQP_VALUE, value, FLOW_HANDLE*, FLOW_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_flow, FLOW_HANDLE, flow); + + MOCKABLE_FUNCTION(, int, flow_get_next_incoming_id, FLOW_HANDLE, flow, transfer_number*, next_incoming_id_value); + MOCKABLE_FUNCTION(, int, flow_set_next_incoming_id, FLOW_HANDLE, flow, transfer_number, next_incoming_id_value); + MOCKABLE_FUNCTION(, int, flow_get_incoming_window, FLOW_HANDLE, flow, uint32_t*, incoming_window_value); + MOCKABLE_FUNCTION(, int, flow_set_incoming_window, FLOW_HANDLE, flow, uint32_t, incoming_window_value); + MOCKABLE_FUNCTION(, int, flow_get_next_outgoing_id, FLOW_HANDLE, flow, transfer_number*, next_outgoing_id_value); + MOCKABLE_FUNCTION(, int, flow_set_next_outgoing_id, FLOW_HANDLE, flow, transfer_number, next_outgoing_id_value); + MOCKABLE_FUNCTION(, int, flow_get_outgoing_window, FLOW_HANDLE, flow, uint32_t*, outgoing_window_value); + MOCKABLE_FUNCTION(, int, flow_set_outgoing_window, FLOW_HANDLE, flow, uint32_t, outgoing_window_value); + MOCKABLE_FUNCTION(, int, flow_get_handle, FLOW_HANDLE, flow, handle*, handle_value); + MOCKABLE_FUNCTION(, int, flow_set_handle, FLOW_HANDLE, flow, handle, handle_value); + MOCKABLE_FUNCTION(, int, flow_get_delivery_count, FLOW_HANDLE, flow, sequence_no*, delivery_count_value); + MOCKABLE_FUNCTION(, int, flow_set_delivery_count, FLOW_HANDLE, flow, sequence_no, delivery_count_value); + MOCKABLE_FUNCTION(, int, flow_get_link_credit, FLOW_HANDLE, flow, uint32_t*, link_credit_value); + MOCKABLE_FUNCTION(, int, flow_set_link_credit, FLOW_HANDLE, flow, uint32_t, link_credit_value); + MOCKABLE_FUNCTION(, int, flow_get_available, FLOW_HANDLE, flow, uint32_t*, available_value); + MOCKABLE_FUNCTION(, int, flow_set_available, FLOW_HANDLE, flow, uint32_t, available_value); + MOCKABLE_FUNCTION(, int, flow_get_drain, FLOW_HANDLE, flow, bool*, drain_value); + MOCKABLE_FUNCTION(, int, flow_set_drain, FLOW_HANDLE, flow, bool, drain_value); + MOCKABLE_FUNCTION(, int, flow_get_echo, FLOW_HANDLE, flow, bool*, echo_value); + MOCKABLE_FUNCTION(, int, flow_set_echo, FLOW_HANDLE, flow, bool, echo_value); + MOCKABLE_FUNCTION(, int, flow_get_properties, FLOW_HANDLE, flow, fields*, properties_value); + MOCKABLE_FUNCTION(, int, flow_set_properties, FLOW_HANDLE, flow, fields, properties_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_FLOW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_footer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_FOOTER_H +#define AMQP_DEFINITIONS_FOOTER_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef annotations footer; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_footer, footer, value); + + MOCKABLE_FUNCTION(, bool, is_footer_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_footer amqpvalue_get_annotations + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_FOOTER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_handle.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_HANDLE_H +#define AMQP_DEFINITIONS_HANDLE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t handle; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_handle, handle, value); + + + #define amqpvalue_get_handle amqpvalue_get_uint + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_HANDLE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_header.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,49 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_HEADER_H +#define AMQP_DEFINITIONS_HEADER_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct HEADER_INSTANCE_TAG* HEADER_HANDLE; + + MOCKABLE_FUNCTION(, HEADER_HANDLE, header_create ); + MOCKABLE_FUNCTION(, HEADER_HANDLE, header_clone, HEADER_HANDLE, value); + MOCKABLE_FUNCTION(, void, header_destroy, HEADER_HANDLE, header); + MOCKABLE_FUNCTION(, bool, is_header_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_header, AMQP_VALUE, value, HEADER_HANDLE*, HEADER_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_header, HEADER_HANDLE, header); + + MOCKABLE_FUNCTION(, int, header_get_durable, HEADER_HANDLE, header, bool*, durable_value); + MOCKABLE_FUNCTION(, int, header_set_durable, HEADER_HANDLE, header, bool, durable_value); + MOCKABLE_FUNCTION(, int, header_get_priority, HEADER_HANDLE, header, uint8_t*, priority_value); + MOCKABLE_FUNCTION(, int, header_set_priority, HEADER_HANDLE, header, uint8_t, priority_value); + MOCKABLE_FUNCTION(, int, header_get_ttl, HEADER_HANDLE, header, milliseconds*, ttl_value); + MOCKABLE_FUNCTION(, int, header_set_ttl, HEADER_HANDLE, header, milliseconds, ttl_value); + MOCKABLE_FUNCTION(, int, header_get_first_acquirer, HEADER_HANDLE, header, bool*, first_acquirer_value); + MOCKABLE_FUNCTION(, int, header_set_first_acquirer, HEADER_HANDLE, header, bool, first_acquirer_value); + MOCKABLE_FUNCTION(, int, header_get_delivery_count, HEADER_HANDLE, header, uint32_t*, delivery_count_value); + MOCKABLE_FUNCTION(, int, header_set_delivery_count, HEADER_HANDLE, header, uint32_t, delivery_count_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_HEADER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_ietf_language_tag.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_IETF_LANGUAGE_TAG_H +#define AMQP_DEFINITIONS_IETF_LANGUAGE_TAG_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* ietf_language_tag; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_ietf_language_tag, ietf_language_tag, value); + + + #define amqpvalue_get_ietf_language_tag amqpvalue_get_symbol + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_IETF_LANGUAGE_TAG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_link_error.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_LINK_ERROR_H +#define AMQP_DEFINITIONS_LINK_ERROR_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* link_error; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_link_error, link_error, value); + + + #define amqpvalue_get_link_error amqpvalue_get_symbol + + #define link_error_detach_forced "amqp:link:detach-forced" + #define link_error_transfer_limit_exceeded "amqp:link:transfer-limit-exceeded" + #define link_error_message_size_exceeded "amqp:link:message-size-exceeded" + #define link_error_redirect "amqp:link:redirect" + #define link_error_stolen "amqp:link:stolen" + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_LINK_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_annotations.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_ANNOTATIONS_H +#define AMQP_DEFINITIONS_MESSAGE_ANNOTATIONS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef annotations message_annotations; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_annotations, message_annotations, value); + + MOCKABLE_FUNCTION(, bool, is_message_annotations_type_by_descriptor, AMQP_VALUE, descriptor); + + #define amqpvalue_get_message_annotations amqpvalue_get_annotations + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_ANNOTATIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_format.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_FORMAT_H +#define AMQP_DEFINITIONS_MESSAGE_FORMAT_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t message_format; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_format, message_format, value); + + + #define amqpvalue_get_message_format amqpvalue_get_uint + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_FORMAT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_id_binary.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_ID_BINARY_H +#define AMQP_DEFINITIONS_MESSAGE_ID_BINARY_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef amqp_binary message_id_binary; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_id_binary, message_id_binary, value); + + + #define amqpvalue_get_message_id_binary amqpvalue_get_binary + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_ID_BINARY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_id_string.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_ID_STRING_H +#define AMQP_DEFINITIONS_MESSAGE_ID_STRING_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* message_id_string; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_id_string, message_id_string, value); + + + #define amqpvalue_get_message_id_string amqpvalue_get_string + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_ID_STRING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_id_ulong.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_ID_ULONG_H +#define AMQP_DEFINITIONS_MESSAGE_ID_ULONG_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint64_t message_id_ulong; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_id_ulong, message_id_ulong, value); + + + #define amqpvalue_get_message_id_ulong amqpvalue_get_ulong + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_ID_ULONG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_message_id_uuid.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MESSAGE_ID_UUID_H +#define AMQP_DEFINITIONS_MESSAGE_ID_UUID_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uuid message_id_uuid; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_message_id_uuid, message_id_uuid, value); + + + #define amqpvalue_get_message_id_uuid amqpvalue_get_uuid + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MESSAGE_ID_UUID_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_milliseconds.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MILLISECONDS_H +#define AMQP_DEFINITIONS_MILLISECONDS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t milliseconds; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_milliseconds, milliseconds, value); + + + #define amqpvalue_get_milliseconds amqpvalue_get_uint + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MILLISECONDS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_modified.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_MODIFIED_H +#define AMQP_DEFINITIONS_MODIFIED_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct MODIFIED_INSTANCE_TAG* MODIFIED_HANDLE; + + MOCKABLE_FUNCTION(, MODIFIED_HANDLE, modified_create ); + MOCKABLE_FUNCTION(, MODIFIED_HANDLE, modified_clone, MODIFIED_HANDLE, value); + MOCKABLE_FUNCTION(, void, modified_destroy, MODIFIED_HANDLE, modified); + MOCKABLE_FUNCTION(, bool, is_modified_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_modified, AMQP_VALUE, value, MODIFIED_HANDLE*, MODIFIED_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_modified, MODIFIED_HANDLE, modified); + + MOCKABLE_FUNCTION(, int, modified_get_delivery_failed, MODIFIED_HANDLE, modified, bool*, delivery_failed_value); + MOCKABLE_FUNCTION(, int, modified_set_delivery_failed, MODIFIED_HANDLE, modified, bool, delivery_failed_value); + MOCKABLE_FUNCTION(, int, modified_get_undeliverable_here, MODIFIED_HANDLE, modified, bool*, undeliverable_here_value); + MOCKABLE_FUNCTION(, int, modified_set_undeliverable_here, MODIFIED_HANDLE, modified, bool, undeliverable_here_value); + MOCKABLE_FUNCTION(, int, modified_get_message_annotations, MODIFIED_HANDLE, modified, fields*, message_annotations_value); + MOCKABLE_FUNCTION(, int, modified_set_message_annotations, MODIFIED_HANDLE, modified, fields, message_annotations_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_MODIFIED_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_node_properties.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_NODE_PROPERTIES_H +#define AMQP_DEFINITIONS_NODE_PROPERTIES_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef fields node_properties; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_node_properties, node_properties, value); + + + #define amqpvalue_get_node_properties amqpvalue_get_fields + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_NODE_PROPERTIES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_open.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,59 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_OPEN_H +#define AMQP_DEFINITIONS_OPEN_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct OPEN_INSTANCE_TAG* OPEN_HANDLE; + + MOCKABLE_FUNCTION(, OPEN_HANDLE, open_create , const char*, container_id_value); + MOCKABLE_FUNCTION(, OPEN_HANDLE, open_clone, OPEN_HANDLE, value); + MOCKABLE_FUNCTION(, void, open_destroy, OPEN_HANDLE, open); + MOCKABLE_FUNCTION(, bool, is_open_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_open, AMQP_VALUE, value, OPEN_HANDLE*, OPEN_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_open, OPEN_HANDLE, open); + + MOCKABLE_FUNCTION(, int, open_get_container_id, OPEN_HANDLE, open, const char**, container_id_value); + MOCKABLE_FUNCTION(, int, open_set_container_id, OPEN_HANDLE, open, const char*, container_id_value); + MOCKABLE_FUNCTION(, int, open_get_hostname, OPEN_HANDLE, open, const char**, hostname_value); + MOCKABLE_FUNCTION(, int, open_set_hostname, OPEN_HANDLE, open, const char*, hostname_value); + MOCKABLE_FUNCTION(, int, open_get_max_frame_size, OPEN_HANDLE, open, uint32_t*, max_frame_size_value); + MOCKABLE_FUNCTION(, int, open_set_max_frame_size, OPEN_HANDLE, open, uint32_t, max_frame_size_value); + MOCKABLE_FUNCTION(, int, open_get_channel_max, OPEN_HANDLE, open, uint16_t*, channel_max_value); + MOCKABLE_FUNCTION(, int, open_set_channel_max, OPEN_HANDLE, open, uint16_t, channel_max_value); + MOCKABLE_FUNCTION(, int, open_get_idle_time_out, OPEN_HANDLE, open, milliseconds*, idle_time_out_value); + MOCKABLE_FUNCTION(, int, open_set_idle_time_out, OPEN_HANDLE, open, milliseconds, idle_time_out_value); + MOCKABLE_FUNCTION(, int, open_get_outgoing_locales, OPEN_HANDLE, open, AMQP_VALUE*, outgoing_locales_value); + MOCKABLE_FUNCTION(, int, open_set_outgoing_locales, OPEN_HANDLE, open, AMQP_VALUE, outgoing_locales_value); + MOCKABLE_FUNCTION(, int, open_get_incoming_locales, OPEN_HANDLE, open, AMQP_VALUE*, incoming_locales_value); + MOCKABLE_FUNCTION(, int, open_set_incoming_locales, OPEN_HANDLE, open, AMQP_VALUE, incoming_locales_value); + MOCKABLE_FUNCTION(, int, open_get_offered_capabilities, OPEN_HANDLE, open, AMQP_VALUE*, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, open_set_offered_capabilities, OPEN_HANDLE, open, AMQP_VALUE, offered_capabilities_value); + MOCKABLE_FUNCTION(, int, open_get_desired_capabilities, OPEN_HANDLE, open, AMQP_VALUE*, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, open_set_desired_capabilities, OPEN_HANDLE, open, AMQP_VALUE, desired_capabilities_value); + MOCKABLE_FUNCTION(, int, open_get_properties, OPEN_HANDLE, open, fields*, properties_value); + MOCKABLE_FUNCTION(, int, open_set_properties, OPEN_HANDLE, open, fields, properties_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_OPEN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_properties.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,65 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_PROPERTIES_H +#define AMQP_DEFINITIONS_PROPERTIES_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct PROPERTIES_INSTANCE_TAG* PROPERTIES_HANDLE; + + MOCKABLE_FUNCTION(, PROPERTIES_HANDLE, properties_create ); + MOCKABLE_FUNCTION(, PROPERTIES_HANDLE, properties_clone, PROPERTIES_HANDLE, value); + MOCKABLE_FUNCTION(, void, properties_destroy, PROPERTIES_HANDLE, properties); + MOCKABLE_FUNCTION(, bool, is_properties_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_properties, AMQP_VALUE, value, PROPERTIES_HANDLE*, PROPERTIES_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_properties, PROPERTIES_HANDLE, properties); + + MOCKABLE_FUNCTION(, int, properties_get_message_id, PROPERTIES_HANDLE, properties, AMQP_VALUE*, message_id_value); + MOCKABLE_FUNCTION(, int, properties_set_message_id, PROPERTIES_HANDLE, properties, AMQP_VALUE, message_id_value); + MOCKABLE_FUNCTION(, int, properties_get_user_id, PROPERTIES_HANDLE, properties, amqp_binary*, user_id_value); + MOCKABLE_FUNCTION(, int, properties_set_user_id, PROPERTIES_HANDLE, properties, amqp_binary, user_id_value); + MOCKABLE_FUNCTION(, int, properties_get_to, PROPERTIES_HANDLE, properties, AMQP_VALUE*, to_value); + MOCKABLE_FUNCTION(, int, properties_set_to, PROPERTIES_HANDLE, properties, AMQP_VALUE, to_value); + MOCKABLE_FUNCTION(, int, properties_get_subject, PROPERTIES_HANDLE, properties, const char**, subject_value); + MOCKABLE_FUNCTION(, int, properties_set_subject, PROPERTIES_HANDLE, properties, const char*, subject_value); + MOCKABLE_FUNCTION(, int, properties_get_reply_to, PROPERTIES_HANDLE, properties, AMQP_VALUE*, reply_to_value); + MOCKABLE_FUNCTION(, int, properties_set_reply_to, PROPERTIES_HANDLE, properties, AMQP_VALUE, reply_to_value); + MOCKABLE_FUNCTION(, int, properties_get_correlation_id, PROPERTIES_HANDLE, properties, AMQP_VALUE*, correlation_id_value); + MOCKABLE_FUNCTION(, int, properties_set_correlation_id, PROPERTIES_HANDLE, properties, AMQP_VALUE, correlation_id_value); + MOCKABLE_FUNCTION(, int, properties_get_content_type, PROPERTIES_HANDLE, properties, const char**, content_type_value); + MOCKABLE_FUNCTION(, int, properties_set_content_type, PROPERTIES_HANDLE, properties, const char*, content_type_value); + MOCKABLE_FUNCTION(, int, properties_get_content_encoding, PROPERTIES_HANDLE, properties, const char**, content_encoding_value); + MOCKABLE_FUNCTION(, int, properties_set_content_encoding, PROPERTIES_HANDLE, properties, const char*, content_encoding_value); + MOCKABLE_FUNCTION(, int, properties_get_absolute_expiry_time, PROPERTIES_HANDLE, properties, timestamp*, absolute_expiry_time_value); + MOCKABLE_FUNCTION(, int, properties_set_absolute_expiry_time, PROPERTIES_HANDLE, properties, timestamp, absolute_expiry_time_value); + MOCKABLE_FUNCTION(, int, properties_get_creation_time, PROPERTIES_HANDLE, properties, timestamp*, creation_time_value); + MOCKABLE_FUNCTION(, int, properties_set_creation_time, PROPERTIES_HANDLE, properties, timestamp, creation_time_value); + MOCKABLE_FUNCTION(, int, properties_get_group_id, PROPERTIES_HANDLE, properties, const char**, group_id_value); + MOCKABLE_FUNCTION(, int, properties_set_group_id, PROPERTIES_HANDLE, properties, const char*, group_id_value); + MOCKABLE_FUNCTION(, int, properties_get_group_sequence, PROPERTIES_HANDLE, properties, sequence_no*, group_sequence_value); + MOCKABLE_FUNCTION(, int, properties_set_group_sequence, PROPERTIES_HANDLE, properties, sequence_no, group_sequence_value); + MOCKABLE_FUNCTION(, int, properties_get_reply_to_group_id, PROPERTIES_HANDLE, properties, const char**, reply_to_group_id_value); + MOCKABLE_FUNCTION(, int, properties_set_reply_to_group_id, PROPERTIES_HANDLE, properties, const char*, reply_to_group_id_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_PROPERTIES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_received.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_RECEIVED_H +#define AMQP_DEFINITIONS_RECEIVED_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct RECEIVED_INSTANCE_TAG* RECEIVED_HANDLE; + + MOCKABLE_FUNCTION(, RECEIVED_HANDLE, received_create , uint32_t, section_number_value, uint64_t, section_offset_value); + MOCKABLE_FUNCTION(, RECEIVED_HANDLE, received_clone, RECEIVED_HANDLE, value); + MOCKABLE_FUNCTION(, void, received_destroy, RECEIVED_HANDLE, received); + MOCKABLE_FUNCTION(, bool, is_received_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_received, AMQP_VALUE, value, RECEIVED_HANDLE*, RECEIVED_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_received, RECEIVED_HANDLE, received); + + MOCKABLE_FUNCTION(, int, received_get_section_number, RECEIVED_HANDLE, received, uint32_t*, section_number_value); + MOCKABLE_FUNCTION(, int, received_set_section_number, RECEIVED_HANDLE, received, uint32_t, section_number_value); + MOCKABLE_FUNCTION(, int, received_get_section_offset, RECEIVED_HANDLE, received, uint64_t*, section_offset_value); + MOCKABLE_FUNCTION(, int, received_set_section_offset, RECEIVED_HANDLE, received, uint64_t, section_offset_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_RECEIVED_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_receiver_settle_mode.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_RECEIVER_SETTLE_MODE_H +#define AMQP_DEFINITIONS_RECEIVER_SETTLE_MODE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint8_t receiver_settle_mode; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_receiver_settle_mode, receiver_settle_mode, value); + + + #define amqpvalue_get_receiver_settle_mode amqpvalue_get_ubyte + + #define receiver_settle_mode_first 0 + #define receiver_settle_mode_second 1 + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_RECEIVER_SETTLE_MODE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_rejected.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_REJECTED_H +#define AMQP_DEFINITIONS_REJECTED_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct REJECTED_INSTANCE_TAG* REJECTED_HANDLE; + + MOCKABLE_FUNCTION(, REJECTED_HANDLE, rejected_create ); + MOCKABLE_FUNCTION(, REJECTED_HANDLE, rejected_clone, REJECTED_HANDLE, value); + MOCKABLE_FUNCTION(, void, rejected_destroy, REJECTED_HANDLE, rejected); + MOCKABLE_FUNCTION(, bool, is_rejected_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_rejected, AMQP_VALUE, value, REJECTED_HANDLE*, REJECTED_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_rejected, REJECTED_HANDLE, rejected); + + MOCKABLE_FUNCTION(, int, rejected_get_error, REJECTED_HANDLE, rejected, ERROR_HANDLE*, error_value); + MOCKABLE_FUNCTION(, int, rejected_set_error, REJECTED_HANDLE, rejected, ERROR_HANDLE, error_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_REJECTED_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_released.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_RELEASED_H +#define AMQP_DEFINITIONS_RELEASED_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct RELEASED_INSTANCE_TAG* RELEASED_HANDLE; + + MOCKABLE_FUNCTION(, RELEASED_HANDLE, released_create ); + MOCKABLE_FUNCTION(, RELEASED_HANDLE, released_clone, RELEASED_HANDLE, value); + MOCKABLE_FUNCTION(, void, released_destroy, RELEASED_HANDLE, released); + MOCKABLE_FUNCTION(, bool, is_released_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_released, AMQP_VALUE, value, RELEASED_HANDLE*, RELEASED_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_released, RELEASED_HANDLE, released); + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_RELEASED_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_role.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_ROLE_H +#define AMQP_DEFINITIONS_ROLE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef bool role; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_role, role, value); + + + #define amqpvalue_get_role amqpvalue_get_boolean + + #define role_sender false + #define role_receiver true + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_ROLE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_challenge.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_CHALLENGE_H +#define AMQP_DEFINITIONS_SASL_CHALLENGE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_CHALLENGE_INSTANCE_TAG* SASL_CHALLENGE_HANDLE; + + MOCKABLE_FUNCTION(, SASL_CHALLENGE_HANDLE, sasl_challenge_create , amqp_binary, challenge_value); + MOCKABLE_FUNCTION(, SASL_CHALLENGE_HANDLE, sasl_challenge_clone, SASL_CHALLENGE_HANDLE, value); + MOCKABLE_FUNCTION(, void, sasl_challenge_destroy, SASL_CHALLENGE_HANDLE, sasl_challenge); + MOCKABLE_FUNCTION(, bool, is_sasl_challenge_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_sasl_challenge, AMQP_VALUE, value, SASL_CHALLENGE_HANDLE*, SASL_CHALLENGE_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_challenge, SASL_CHALLENGE_HANDLE, sasl_challenge); + + MOCKABLE_FUNCTION(, int, sasl_challenge_get_challenge, SASL_CHALLENGE_HANDLE, sasl_challenge, amqp_binary*, challenge_value); + MOCKABLE_FUNCTION(, int, sasl_challenge_set_challenge, SASL_CHALLENGE_HANDLE, sasl_challenge, amqp_binary, challenge_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_CHALLENGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_code.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_CODE_H +#define AMQP_DEFINITIONS_SASL_CODE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint8_t sasl_code; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_code, sasl_code, value); + + + #define amqpvalue_get_sasl_code amqpvalue_get_ubyte + + #define sasl_code_ok 0 + #define sasl_code_auth 1 + #define sasl_code_sys 2 + #define sasl_code_sys_perm 3 + #define sasl_code_sys_temp 4 + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_CODE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_init.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_INIT_H +#define AMQP_DEFINITIONS_SASL_INIT_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_INIT_INSTANCE_TAG* SASL_INIT_HANDLE; + + MOCKABLE_FUNCTION(, SASL_INIT_HANDLE, sasl_init_create , const char*, mechanism_value); + MOCKABLE_FUNCTION(, SASL_INIT_HANDLE, sasl_init_clone, SASL_INIT_HANDLE, value); + MOCKABLE_FUNCTION(, void, sasl_init_destroy, SASL_INIT_HANDLE, sasl_init); + MOCKABLE_FUNCTION(, bool, is_sasl_init_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_sasl_init, AMQP_VALUE, value, SASL_INIT_HANDLE*, SASL_INIT_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_init, SASL_INIT_HANDLE, sasl_init); + + MOCKABLE_FUNCTION(, int, sasl_init_get_mechanism, SASL_INIT_HANDLE, sasl_init, const char**, mechanism_value); + MOCKABLE_FUNCTION(, int, sasl_init_set_mechanism, SASL_INIT_HANDLE, sasl_init, const char*, mechanism_value); + MOCKABLE_FUNCTION(, int, sasl_init_get_initial_response, SASL_INIT_HANDLE, sasl_init, amqp_binary*, initial_response_value); + MOCKABLE_FUNCTION(, int, sasl_init_set_initial_response, SASL_INIT_HANDLE, sasl_init, amqp_binary, initial_response_value); + MOCKABLE_FUNCTION(, int, sasl_init_get_hostname, SASL_INIT_HANDLE, sasl_init, const char**, hostname_value); + MOCKABLE_FUNCTION(, int, sasl_init_set_hostname, SASL_INIT_HANDLE, sasl_init, const char*, hostname_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_INIT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_mechanisms.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_MECHANISMS_H +#define AMQP_DEFINITIONS_SASL_MECHANISMS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_MECHANISMS_INSTANCE_TAG* SASL_MECHANISMS_HANDLE; + + MOCKABLE_FUNCTION(, SASL_MECHANISMS_HANDLE, sasl_mechanisms_create , AMQP_VALUE, sasl_server_mechanisms_value); + MOCKABLE_FUNCTION(, SASL_MECHANISMS_HANDLE, sasl_mechanisms_clone, SASL_MECHANISMS_HANDLE, value); + MOCKABLE_FUNCTION(, void, sasl_mechanisms_destroy, SASL_MECHANISMS_HANDLE, sasl_mechanisms); + MOCKABLE_FUNCTION(, bool, is_sasl_mechanisms_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_sasl_mechanisms, AMQP_VALUE, value, SASL_MECHANISMS_HANDLE*, SASL_MECHANISMS_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_mechanisms, SASL_MECHANISMS_HANDLE, sasl_mechanisms); + + MOCKABLE_FUNCTION(, int, sasl_mechanisms_get_sasl_server_mechanisms, SASL_MECHANISMS_HANDLE, sasl_mechanisms, AMQP_VALUE*, sasl_server_mechanisms_value); + MOCKABLE_FUNCTION(, int, sasl_mechanisms_set_sasl_server_mechanisms, SASL_MECHANISMS_HANDLE, sasl_mechanisms, AMQP_VALUE, sasl_server_mechanisms_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_MECHANISMS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_outcome.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_OUTCOME_H +#define AMQP_DEFINITIONS_SASL_OUTCOME_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_OUTCOME_INSTANCE_TAG* SASL_OUTCOME_HANDLE; + + MOCKABLE_FUNCTION(, SASL_OUTCOME_HANDLE, sasl_outcome_create , sasl_code, code_value); + MOCKABLE_FUNCTION(, SASL_OUTCOME_HANDLE, sasl_outcome_clone, SASL_OUTCOME_HANDLE, value); + MOCKABLE_FUNCTION(, void, sasl_outcome_destroy, SASL_OUTCOME_HANDLE, sasl_outcome); + MOCKABLE_FUNCTION(, bool, is_sasl_outcome_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_sasl_outcome, AMQP_VALUE, value, SASL_OUTCOME_HANDLE*, SASL_OUTCOME_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_outcome, SASL_OUTCOME_HANDLE, sasl_outcome); + + MOCKABLE_FUNCTION(, int, sasl_outcome_get_code, SASL_OUTCOME_HANDLE, sasl_outcome, sasl_code*, code_value); + MOCKABLE_FUNCTION(, int, sasl_outcome_set_code, SASL_OUTCOME_HANDLE, sasl_outcome, sasl_code, code_value); + MOCKABLE_FUNCTION(, int, sasl_outcome_get_additional_data, SASL_OUTCOME_HANDLE, sasl_outcome, amqp_binary*, additional_data_value); + MOCKABLE_FUNCTION(, int, sasl_outcome_set_additional_data, SASL_OUTCOME_HANDLE, sasl_outcome, amqp_binary, additional_data_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_OUTCOME_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sasl_response.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SASL_RESPONSE_H +#define AMQP_DEFINITIONS_SASL_RESPONSE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_RESPONSE_INSTANCE_TAG* SASL_RESPONSE_HANDLE; + + MOCKABLE_FUNCTION(, SASL_RESPONSE_HANDLE, sasl_response_create , amqp_binary, response_value); + MOCKABLE_FUNCTION(, SASL_RESPONSE_HANDLE, sasl_response_clone, SASL_RESPONSE_HANDLE, value); + MOCKABLE_FUNCTION(, void, sasl_response_destroy, SASL_RESPONSE_HANDLE, sasl_response); + MOCKABLE_FUNCTION(, bool, is_sasl_response_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_sasl_response, AMQP_VALUE, value, SASL_RESPONSE_HANDLE*, SASL_RESPONSE_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sasl_response, SASL_RESPONSE_HANDLE, sasl_response); + + MOCKABLE_FUNCTION(, int, sasl_response_get_response, SASL_RESPONSE_HANDLE, sasl_response, amqp_binary*, response_value); + MOCKABLE_FUNCTION(, int, sasl_response_set_response, SASL_RESPONSE_HANDLE, sasl_response, amqp_binary, response_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SASL_RESPONSE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_seconds.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SECONDS_H +#define AMQP_DEFINITIONS_SECONDS_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t seconds; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_seconds, seconds, value); + + + #define amqpvalue_get_seconds amqpvalue_get_uint + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SECONDS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sender_settle_mode.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SENDER_SETTLE_MODE_H +#define AMQP_DEFINITIONS_SENDER_SETTLE_MODE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint8_t sender_settle_mode; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sender_settle_mode, sender_settle_mode, value); + + + #define amqpvalue_get_sender_settle_mode amqpvalue_get_ubyte + + #define sender_settle_mode_unsettled 0 + #define sender_settle_mode_settled 1 + #define sender_settle_mode_mixed 2 + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SENDER_SETTLE_MODE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_sequence_no.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SEQUENCE_NO_H +#define AMQP_DEFINITIONS_SEQUENCE_NO_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t sequence_no; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_sequence_no, sequence_no, value); + + + #define amqpvalue_get_sequence_no amqpvalue_get_uint + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SEQUENCE_NO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_session_error.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,42 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SESSION_ERROR_H +#define AMQP_DEFINITIONS_SESSION_ERROR_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* session_error; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_session_error, session_error, value); + + + #define amqpvalue_get_session_error amqpvalue_get_symbol + + #define session_error_window_violation "amqp:session:window-violation" + #define session_error_errant_link "amqp:session:errant-link" + #define session_error_handle_in_use "amqp:session:handle-in-use" + #define session_error_unattached_handle "amqp:session:unattached-handle" + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SESSION_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_source.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,61 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_SOURCE_H +#define AMQP_DEFINITIONS_SOURCE_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SOURCE_INSTANCE_TAG* SOURCE_HANDLE; + + MOCKABLE_FUNCTION(, SOURCE_HANDLE, source_create ); + MOCKABLE_FUNCTION(, SOURCE_HANDLE, source_clone, SOURCE_HANDLE, value); + MOCKABLE_FUNCTION(, void, source_destroy, SOURCE_HANDLE, source); + MOCKABLE_FUNCTION(, bool, is_source_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_source, AMQP_VALUE, value, SOURCE_HANDLE*, SOURCE_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_source, SOURCE_HANDLE, source); + + MOCKABLE_FUNCTION(, int, source_get_address, SOURCE_HANDLE, source, AMQP_VALUE*, address_value); + MOCKABLE_FUNCTION(, int, source_set_address, SOURCE_HANDLE, source, AMQP_VALUE, address_value); + MOCKABLE_FUNCTION(, int, source_get_durable, SOURCE_HANDLE, source, terminus_durability*, durable_value); + MOCKABLE_FUNCTION(, int, source_set_durable, SOURCE_HANDLE, source, terminus_durability, durable_value); + MOCKABLE_FUNCTION(, int, source_get_expiry_policy, SOURCE_HANDLE, source, terminus_expiry_policy*, expiry_policy_value); + MOCKABLE_FUNCTION(, int, source_set_expiry_policy, SOURCE_HANDLE, source, terminus_expiry_policy, expiry_policy_value); + MOCKABLE_FUNCTION(, int, source_get_timeout, SOURCE_HANDLE, source, seconds*, timeout_value); + MOCKABLE_FUNCTION(, int, source_set_timeout, SOURCE_HANDLE, source, seconds, timeout_value); + MOCKABLE_FUNCTION(, int, source_get_dynamic, SOURCE_HANDLE, source, bool*, dynamic_value); + MOCKABLE_FUNCTION(, int, source_set_dynamic, SOURCE_HANDLE, source, bool, dynamic_value); + MOCKABLE_FUNCTION(, int, source_get_dynamic_node_properties, SOURCE_HANDLE, source, node_properties*, dynamic_node_properties_value); + MOCKABLE_FUNCTION(, int, source_set_dynamic_node_properties, SOURCE_HANDLE, source, node_properties, dynamic_node_properties_value); + MOCKABLE_FUNCTION(, int, source_get_distribution_mode, SOURCE_HANDLE, source, const char**, distribution_mode_value); + MOCKABLE_FUNCTION(, int, source_set_distribution_mode, SOURCE_HANDLE, source, const char*, distribution_mode_value); + MOCKABLE_FUNCTION(, int, source_get_filter, SOURCE_HANDLE, source, filter_set*, filter_value); + MOCKABLE_FUNCTION(, int, source_set_filter, SOURCE_HANDLE, source, filter_set, filter_value); + MOCKABLE_FUNCTION(, int, source_get_default_outcome, SOURCE_HANDLE, source, AMQP_VALUE*, default_outcome_value); + MOCKABLE_FUNCTION(, int, source_set_default_outcome, SOURCE_HANDLE, source, AMQP_VALUE, default_outcome_value); + MOCKABLE_FUNCTION(, int, source_get_outcomes, SOURCE_HANDLE, source, AMQP_VALUE*, outcomes_value); + MOCKABLE_FUNCTION(, int, source_set_outcomes, SOURCE_HANDLE, source, AMQP_VALUE, outcomes_value); + MOCKABLE_FUNCTION(, int, source_get_capabilities, SOURCE_HANDLE, source, AMQP_VALUE*, capabilities_value); + MOCKABLE_FUNCTION(, int, source_set_capabilities, SOURCE_HANDLE, source, AMQP_VALUE, capabilities_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_SOURCE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_target.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,53 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_TARGET_H +#define AMQP_DEFINITIONS_TARGET_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct TARGET_INSTANCE_TAG* TARGET_HANDLE; + + MOCKABLE_FUNCTION(, TARGET_HANDLE, target_create ); + MOCKABLE_FUNCTION(, TARGET_HANDLE, target_clone, TARGET_HANDLE, value); + MOCKABLE_FUNCTION(, void, target_destroy, TARGET_HANDLE, target); + MOCKABLE_FUNCTION(, bool, is_target_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_target, AMQP_VALUE, value, TARGET_HANDLE*, TARGET_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_target, TARGET_HANDLE, target); + + MOCKABLE_FUNCTION(, int, target_get_address, TARGET_HANDLE, target, AMQP_VALUE*, address_value); + MOCKABLE_FUNCTION(, int, target_set_address, TARGET_HANDLE, target, AMQP_VALUE, address_value); + MOCKABLE_FUNCTION(, int, target_get_durable, TARGET_HANDLE, target, terminus_durability*, durable_value); + MOCKABLE_FUNCTION(, int, target_set_durable, TARGET_HANDLE, target, terminus_durability, durable_value); + MOCKABLE_FUNCTION(, int, target_get_expiry_policy, TARGET_HANDLE, target, terminus_expiry_policy*, expiry_policy_value); + MOCKABLE_FUNCTION(, int, target_set_expiry_policy, TARGET_HANDLE, target, terminus_expiry_policy, expiry_policy_value); + MOCKABLE_FUNCTION(, int, target_get_timeout, TARGET_HANDLE, target, seconds*, timeout_value); + MOCKABLE_FUNCTION(, int, target_set_timeout, TARGET_HANDLE, target, seconds, timeout_value); + MOCKABLE_FUNCTION(, int, target_get_dynamic, TARGET_HANDLE, target, bool*, dynamic_value); + MOCKABLE_FUNCTION(, int, target_set_dynamic, TARGET_HANDLE, target, bool, dynamic_value); + MOCKABLE_FUNCTION(, int, target_get_dynamic_node_properties, TARGET_HANDLE, target, node_properties*, dynamic_node_properties_value); + MOCKABLE_FUNCTION(, int, target_set_dynamic_node_properties, TARGET_HANDLE, target, node_properties, dynamic_node_properties_value); + MOCKABLE_FUNCTION(, int, target_get_capabilities, TARGET_HANDLE, target, AMQP_VALUE*, capabilities_value); + MOCKABLE_FUNCTION(, int, target_set_capabilities, TARGET_HANDLE, target, AMQP_VALUE, capabilities_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_TARGET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_terminus_durability.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,41 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_TERMINUS_DURABILITY_H +#define AMQP_DEFINITIONS_TERMINUS_DURABILITY_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef uint32_t terminus_durability; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_terminus_durability, terminus_durability, value); + + + #define amqpvalue_get_terminus_durability amqpvalue_get_uint + + #define terminus_durability_none 0 + #define terminus_durability_configuration 1 + #define terminus_durability_unsettled_state 2 + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_TERMINUS_DURABILITY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_terminus_expiry_policy.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,42 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_TERMINUS_EXPIRY_POLICY_H +#define AMQP_DEFINITIONS_TERMINUS_EXPIRY_POLICY_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef const char* terminus_expiry_policy; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_terminus_expiry_policy, terminus_expiry_policy, value); + + + #define amqpvalue_get_terminus_expiry_policy amqpvalue_get_symbol + + #define terminus_expiry_policy_link_detach "link-detach" + #define terminus_expiry_policy_session_end "session-end" + #define terminus_expiry_policy_connection_close "connection-close" + #define terminus_expiry_policy_never "never" + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_TERMINUS_EXPIRY_POLICY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_transfer.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,61 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_TRANSFER_H +#define AMQP_DEFINITIONS_TRANSFER_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct TRANSFER_INSTANCE_TAG* TRANSFER_HANDLE; + + MOCKABLE_FUNCTION(, TRANSFER_HANDLE, transfer_create , handle, handle_value); + MOCKABLE_FUNCTION(, TRANSFER_HANDLE, transfer_clone, TRANSFER_HANDLE, value); + MOCKABLE_FUNCTION(, void, transfer_destroy, TRANSFER_HANDLE, transfer); + MOCKABLE_FUNCTION(, bool, is_transfer_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_transfer, AMQP_VALUE, value, TRANSFER_HANDLE*, TRANSFER_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_transfer, TRANSFER_HANDLE, transfer); + + MOCKABLE_FUNCTION(, int, transfer_get_handle, TRANSFER_HANDLE, transfer, handle*, handle_value); + MOCKABLE_FUNCTION(, int, transfer_set_handle, TRANSFER_HANDLE, transfer, handle, handle_value); + MOCKABLE_FUNCTION(, int, transfer_get_delivery_id, TRANSFER_HANDLE, transfer, delivery_number*, delivery_id_value); + MOCKABLE_FUNCTION(, int, transfer_set_delivery_id, TRANSFER_HANDLE, transfer, delivery_number, delivery_id_value); + MOCKABLE_FUNCTION(, int, transfer_get_delivery_tag, TRANSFER_HANDLE, transfer, delivery_tag*, delivery_tag_value); + MOCKABLE_FUNCTION(, int, transfer_set_delivery_tag, TRANSFER_HANDLE, transfer, delivery_tag, delivery_tag_value); + MOCKABLE_FUNCTION(, int, transfer_get_message_format, TRANSFER_HANDLE, transfer, message_format*, message_format_value); + MOCKABLE_FUNCTION(, int, transfer_set_message_format, TRANSFER_HANDLE, transfer, message_format, message_format_value); + MOCKABLE_FUNCTION(, int, transfer_get_settled, TRANSFER_HANDLE, transfer, bool*, settled_value); + MOCKABLE_FUNCTION(, int, transfer_set_settled, TRANSFER_HANDLE, transfer, bool, settled_value); + MOCKABLE_FUNCTION(, int, transfer_get_more, TRANSFER_HANDLE, transfer, bool*, more_value); + MOCKABLE_FUNCTION(, int, transfer_set_more, TRANSFER_HANDLE, transfer, bool, more_value); + MOCKABLE_FUNCTION(, int, transfer_get_rcv_settle_mode, TRANSFER_HANDLE, transfer, receiver_settle_mode*, rcv_settle_mode_value); + MOCKABLE_FUNCTION(, int, transfer_set_rcv_settle_mode, TRANSFER_HANDLE, transfer, receiver_settle_mode, rcv_settle_mode_value); + MOCKABLE_FUNCTION(, int, transfer_get_state, TRANSFER_HANDLE, transfer, AMQP_VALUE*, state_value); + MOCKABLE_FUNCTION(, int, transfer_set_state, TRANSFER_HANDLE, transfer, AMQP_VALUE, state_value); + MOCKABLE_FUNCTION(, int, transfer_get_resume, TRANSFER_HANDLE, transfer, bool*, resume_value); + MOCKABLE_FUNCTION(, int, transfer_set_resume, TRANSFER_HANDLE, transfer, bool, resume_value); + MOCKABLE_FUNCTION(, int, transfer_get_aborted, TRANSFER_HANDLE, transfer, bool*, aborted_value); + MOCKABLE_FUNCTION(, int, transfer_set_aborted, TRANSFER_HANDLE, transfer, bool, aborted_value); + MOCKABLE_FUNCTION(, int, transfer_get_batchable, TRANSFER_HANDLE, transfer, bool*, batchable_value); + MOCKABLE_FUNCTION(, int, transfer_set_batchable, TRANSFER_HANDLE, transfer, bool, batchable_value); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_TRANSFER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_definitions_transfer_number.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,38 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_TRANSFER_NUMBER_H +#define AMQP_DEFINITIONS_TRANSFER_NUMBER_H + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + + + typedef sequence_no transfer_number; + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_transfer_number, transfer_number, value); + + + #define amqpvalue_get_transfer_number amqpvalue_get_sequence_no + + + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_TRANSFER_NUMBER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_frame_codec.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef AMQP_FRAME_CODEC_H +#define AMQP_FRAME_CODEC_H + +#ifdef __cplusplus +extern "C" { +#include <cstdint> +#include <cstddef> +#else +#include <stdint.h> +#include <stddef.h> +#endif /* __cplusplus */ +#include "azure_uamqp_c/frame_codec.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#define AMQP_OPEN (uint64_t)0x10 +#define AMQP_BEGIN (uint64_t)0x11 +#define AMQP_ATTACH (uint64_t)0x12 +#define AMQP_FLOW (uint64_t)0x13 +#define AMQP_TRANSFER (uint64_t)0x14 +#define AMQP_DISPOSITION (uint64_t)0x15 +#define AMQP_DETACH (uint64_t)0x16 +#define AMQP_END (uint64_t)0x17 +#define AMQP_CLOSE (uint64_t)0x18 + +typedef struct AMQP_FRAME_CODEC_TAG* AMQP_FRAME_CODEC_HANDLE; +typedef void(*AMQP_EMPTY_FRAME_RECEIVED_CALLBACK)(void* context, uint16_t channel); +typedef void(*AMQP_FRAME_RECEIVED_CALLBACK)(void* context, uint16_t channel, AMQP_VALUE performative, const unsigned char* payload_bytes, uint32_t frame_payload_size); +typedef void(*AMQP_FRAME_CODEC_ERROR_CALLBACK)(void* context); + +MOCKABLE_FUNCTION(, AMQP_FRAME_CODEC_HANDLE, amqp_frame_codec_create, FRAME_CODEC_HANDLE, frame_codec, AMQP_FRAME_RECEIVED_CALLBACK, frame_received_callback, AMQP_EMPTY_FRAME_RECEIVED_CALLBACK, empty_frame_received_callback, AMQP_FRAME_CODEC_ERROR_CALLBACK, amqp_frame_codec_error_callback, void*, callback_context); +MOCKABLE_FUNCTION(, void, amqp_frame_codec_destroy, AMQP_FRAME_CODEC_HANDLE, amqp_frame_codec); +MOCKABLE_FUNCTION(, int, amqp_frame_codec_encode_frame, AMQP_FRAME_CODEC_HANDLE, amqp_frame_codec, uint16_t, channel, AMQP_VALUE, performative, const PAYLOAD*, payloads, size_t, payload_count, ON_BYTES_ENCODED, on_bytes_encoded, void*, callback_context); +MOCKABLE_FUNCTION(, int, amqp_frame_codec_encode_empty_frame, AMQP_FRAME_CODEC_HANDLE, amqp_frame_codec, uint16_t, channel, ON_BYTES_ENCODED, on_bytes_encoded, void*, callback_context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AMQP_FRAME_CODEC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_management.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef AMQP_MANAGEMENT_H +#define AMQP_MANAGEMENT_H + +#include <stdbool.h> +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/message.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +#define AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT_VALUES \ + AMQP_MANAGEMENT_EXECUTE_OPERATION_OK, \ + AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR, \ + AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS, \ + AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED + +DEFINE_ENUM(AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT, AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT_VALUES) + +#define AMQP_MANAGEMENT_OPEN_RESULT_VALUES \ + AMQP_MANAGEMENT_OPEN_OK, \ + AMQP_MANAGEMENT_OPEN_ERROR, \ + AMQP_MANAGEMENT_OPEN_CANCELLED + +DEFINE_ENUM(AMQP_MANAGEMENT_OPEN_RESULT, AMQP_MANAGEMENT_OPEN_RESULT_VALUES) + + typedef struct AMQP_MANAGEMENT_INSTANCE_TAG* AMQP_MANAGEMENT_HANDLE; + typedef void(*ON_AMQP_MANAGEMENT_OPEN_COMPLETE)(void* context, AMQP_MANAGEMENT_OPEN_RESULT open_result); + typedef void(*ON_AMQP_MANAGEMENT_ERROR)(void* context); + typedef void(*ON_AMQP_MANAGEMENT_EXECUTE_OPERATION_COMPLETE)(void* context, AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT execute_operation_result, unsigned int status_code, const char* status_description, MESSAGE_HANDLE message_handle); + + MOCKABLE_FUNCTION(, AMQP_MANAGEMENT_HANDLE, amqp_management_create, SESSION_HANDLE, session, const char*, management_node); + MOCKABLE_FUNCTION(, void, amqp_management_destroy, AMQP_MANAGEMENT_HANDLE, amqp_management); + MOCKABLE_FUNCTION(, int, amqp_management_open_async, AMQP_MANAGEMENT_HANDLE, amqp_management, ON_AMQP_MANAGEMENT_OPEN_COMPLETE, on_amqp_management_open_complete, void*, on_amqp_management_open_complete_context, ON_AMQP_MANAGEMENT_ERROR, on_amqp_management_error, void*, on_amqp_management_error_context); + MOCKABLE_FUNCTION(, int, amqp_management_close, AMQP_MANAGEMENT_HANDLE, amqp_management); + MOCKABLE_FUNCTION(, int, amqp_management_execute_operation_async, AMQP_MANAGEMENT_HANDLE, amqp_management, const char*, operation, const char*, type, const char*, locales, MESSAGE_HANDLE, message, ON_AMQP_MANAGEMENT_EXECUTE_OPERATION_COMPLETE, on_execute_operation_complete, void*, context); + MOCKABLE_FUNCTION(, void, amqp_management_set_trace, AMQP_MANAGEMENT_HANDLE, amqp_management, bool, trace_on); + MOCKABLE_FUNCTION(, int, amqp_management_set_override_status_code_key_name, AMQP_MANAGEMENT_HANDLE, amqp_management, const char*, override_status_code_key_name); + MOCKABLE_FUNCTION(, int, amqp_management_set_override_status_description_key_name, AMQP_MANAGEMENT_HANDLE, amqp_management, const char*, override_status_description_key_name); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AMQP_MANAGEMENT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqp_types.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ANQP_TYPES_H +#define ANQP_TYPES_H + +#include <stddef.h> +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define AMQP_TYPE_VALUES \ + AMQP_TYPE_NULL, \ + AMQP_TYPE_BOOL, \ + AMQP_TYPE_UBYTE, \ + AMQP_TYPE_USHORT, \ + AMQP_TYPE_UINT, \ + AMQP_TYPE_ULONG, \ + AMQP_TYPE_BYTE, \ + AMQP_TYPE_SHORT, \ + AMQP_TYPE_INT, \ + AMQP_TYPE_LONG, \ + AMQP_TYPE_FLOAT, \ + AMQP_TYPE_DOUBLE, \ + AMQP_TYPE_CHAR, \ + AMQP_TYPE_TIMESTAMP, \ + AMQP_TYPE_UUID, \ + AMQP_TYPE_BINARY, \ + AMQP_TYPE_STRING, \ + AMQP_TYPE_SYMBOL, \ + AMQP_TYPE_LIST, \ + AMQP_TYPE_MAP, \ + AMQP_TYPE_ARRAY, \ + AMQP_TYPE_DESCRIBED, \ + AMQP_TYPE_COMPOSITE, \ + AMQP_TYPE_UNKNOWN + +DEFINE_ENUM(AMQP_TYPE, AMQP_TYPE_VALUES); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ANQP_TYPES_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqpvalue.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef AMQPVALUE_H +#define AMQPVALUE_H + +#include "azure_uamqp_c/amqp_types.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdint> +extern "C" { +#else +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct AMQP_VALUE_DATA_TAG* AMQP_VALUE; + typedef unsigned char uuid[16]; + typedef int64_t timestamp; + + typedef struct amqp_binary_TAG + { + const void* bytes; + uint32_t length; + } amqp_binary; + + /* type handling */ + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_null); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_boolean, bool, bool_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_boolean, AMQP_VALUE, value, bool*, bool_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_ubyte, unsigned char, ubyte_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_ubyte, AMQP_VALUE, value, unsigned char*, ubyte_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_ushort, uint16_t, ushort_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_ushort, AMQP_VALUE, value, uint16_t*, ushort_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_uint, uint32_t, uint_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_uint, AMQP_VALUE, value, uint32_t*, uint_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_ulong, uint64_t, ulong_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_ulong, AMQP_VALUE, value, uint64_t*, ulong_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_byte, char, byte_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_byte, AMQP_VALUE, value, char*, byte_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_short, int16_t, short_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_short, AMQP_VALUE, value, int16_t*, short_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_int, int32_t, int_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_int, AMQP_VALUE, value, int32_t*, int_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_long, int64_t, long_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_long, AMQP_VALUE, value, int64_t*, long_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_float, float, float_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_float, AMQP_VALUE, value, float*, float_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_double, double, double_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_double, AMQP_VALUE, value, double*, double_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_char, uint32_t, char_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_char, AMQP_VALUE, value, uint32_t*, char_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_timestamp, int64_t, timestamp_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_timestamp, AMQP_VALUE, value, int64_t*, timestamp_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_uuid, uuid, uuid_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_uuid, AMQP_VALUE, value, uuid*, uuid_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_binary, amqp_binary, binary_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_binary, AMQP_VALUE, value, amqp_binary*, binary_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_string, const char*, string_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_string, AMQP_VALUE, value, const char**, string_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_symbol, const char*, symbol_value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_symbol, AMQP_VALUE, value, const char**, symbol_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_list); + MOCKABLE_FUNCTION(, int, amqpvalue_set_list_item_count, AMQP_VALUE, list, uint32_t, count); + MOCKABLE_FUNCTION(, int, amqpvalue_get_list_item_count, AMQP_VALUE, list, uint32_t*, count); + MOCKABLE_FUNCTION(, int, amqpvalue_set_list_item, AMQP_VALUE, list, uint32_t, index, AMQP_VALUE, list_item_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_list_item, AMQP_VALUE, list, size_t, index); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_map); + MOCKABLE_FUNCTION(, int, amqpvalue_set_map_value, AMQP_VALUE, map, AMQP_VALUE, key, AMQP_VALUE, value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_map_value, AMQP_VALUE, map, AMQP_VALUE, key); + MOCKABLE_FUNCTION(, int, amqpvalue_get_map_pair_count, AMQP_VALUE, map, uint32_t*, pair_count); + MOCKABLE_FUNCTION(, int, amqpvalue_get_map_key_value_pair, AMQP_VALUE, map, uint32_t, index, AMQP_VALUE*, key, AMQP_VALUE*, value); + MOCKABLE_FUNCTION(, int, amqpvalue_get_map, AMQP_VALUE, from_value, AMQP_VALUE*, map); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_array); + MOCKABLE_FUNCTION(, int, amqpvalue_add_array_item, AMQP_VALUE, value, AMQP_VALUE, array_item_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_array_item, AMQP_VALUE, value, uint32_t, index); + MOCKABLE_FUNCTION(, int, amqpvalue_get_array_item_count, AMQP_VALUE, value, uint32_t*, count); + MOCKABLE_FUNCTION(, int, amqpvalue_get_array, AMQP_VALUE, value, AMQP_VALUE*, array_value); + MOCKABLE_FUNCTION(, AMQP_TYPE, amqpvalue_get_type, AMQP_VALUE, value); + + MOCKABLE_FUNCTION(, void, amqpvalue_destroy, AMQP_VALUE, value); + + MOCKABLE_FUNCTION(, bool, amqpvalue_are_equal, AMQP_VALUE, value1, AMQP_VALUE, value2); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_clone, AMQP_VALUE, value); + + /* encoding */ + typedef int (*AMQPVALUE_ENCODER_OUTPUT)(void* context, const unsigned char* bytes, size_t length); + + MOCKABLE_FUNCTION(, int, amqpvalue_encode, AMQP_VALUE, value, AMQPVALUE_ENCODER_OUTPUT, encoder_output, void*, context); + MOCKABLE_FUNCTION(, int, amqpvalue_get_encoded_size, AMQP_VALUE, value, size_t*, encoded_size); + + /* decoding */ + typedef struct AMQPVALUE_DECODER_HANDLE_DATA_TAG* AMQPVALUE_DECODER_HANDLE; + typedef void(*ON_VALUE_DECODED)(void* context, AMQP_VALUE decoded_value); + + MOCKABLE_FUNCTION(, AMQPVALUE_DECODER_HANDLE, amqpvalue_decoder_create, ON_VALUE_DECODED, on_value_decoded, void*, callback_context); + MOCKABLE_FUNCTION(, void, amqpvalue_decoder_destroy, AMQPVALUE_DECODER_HANDLE, handle); + MOCKABLE_FUNCTION(, int, amqpvalue_decode_bytes, AMQPVALUE_DECODER_HANDLE, handle, const unsigned char*, buffer, size_t, size); + + /* misc for now, not spec'd */ + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_inplace_descriptor, AMQP_VALUE, value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_inplace_described_value, AMQP_VALUE, value); + + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_composite, AMQP_VALUE, descriptor, uint32_t, list_size); + MOCKABLE_FUNCTION(, int, amqpvalue_set_composite_item, AMQP_VALUE, value, uint32_t, index, AMQP_VALUE, item_value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_composite_item, AMQP_VALUE, value, size_t, index); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_described, AMQP_VALUE, descriptor, AMQP_VALUE, value); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_composite_with_ulong_descriptor, uint64_t, descriptor); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_list_item_in_place, AMQP_VALUE, value, size_t, index); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_get_composite_item_in_place, AMQP_VALUE, value, size_t, index); + MOCKABLE_FUNCTION(, int, amqpvalue_get_composite_item_count, AMQP_VALUE, value, uint32_t*, item_count); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AMQPVALUE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/amqpvalue_to_string.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef AMQPVALUE_TO_STRING_H + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + MOCKABLE_FUNCTION(, char*, amqpvalue_to_string, AMQP_VALUE, amqp_value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AMQPVALUE_TO_STRING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/async_operation.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ASYNC_OPERATION_H +#define ASYNC_OPERATION_H + +#ifdef __cplusplus +extern "C" { +#include <cstdint> +#include <cstddef> +#else +#include <stdint.h> +#include <stddef.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct ASYNC_OPERATION_INSTANCE_TAG* ASYNC_OPERATION_HANDLE; + +typedef void(*ASYNC_OPERATION_CANCEL_HANDLER_FUNC)(ASYNC_OPERATION_HANDLE async_operation); + +#define DEFINE_ASYNC_OPERATION_CONTEXT(type) \ +typedef struct C3(ASYNC_OPERATION_CONTEXT_STRUCT_, type, _TAG) \ +{ \ + ASYNC_OPERATION_CANCEL_HANDLER_FUNC async_operation_cancel_handler; \ + type context; \ +} C2(ASYNC_OPERATION_CONTEXT_STRUCT_, type); + +#define GET_ASYNC_OPERATION_CONTEXT(type, async_operation) \ + (type*)((unsigned char*)async_operation + offsetof(C2(ASYNC_OPERATION_CONTEXT_STRUCT_, type), context)) + +#define CREATE_ASYNC_OPERATION(type, async_operation_cancel_handler) \ + async_operation_create(async_operation_cancel_handler, sizeof(C2(ASYNC_OPERATION_CONTEXT_STRUCT_, type))) + +MOCKABLE_FUNCTION(, ASYNC_OPERATION_HANDLE, async_operation_create, ASYNC_OPERATION_CANCEL_HANDLER_FUNC, async_operation_cancel_handler, size_t, context_size); +MOCKABLE_FUNCTION(, void, async_operation_destroy, ASYNC_OPERATION_HANDLE, async_operation); +MOCKABLE_FUNCTION(, int, async_operation_cancel, ASYNC_OPERATION_HANDLE, async_operation); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ASYNC_OPERATION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/cbs.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CBS_H +#define CBS_H + +#include "azure_uamqp_c/session.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif /* __cplusplus */ + +#define CBS_OPERATION_RESULT_VALUES \ + CBS_OPERATION_RESULT_OK, \ + CBS_OPERATION_RESULT_CBS_ERROR, \ + CBS_OPERATION_RESULT_OPERATION_FAILED, \ + CBS_OPERATION_RESULT_INSTANCE_CLOSED + +DEFINE_ENUM(CBS_OPERATION_RESULT, CBS_OPERATION_RESULT_VALUES) + +#define CBS_OPEN_COMPLETE_RESULT_VALUES \ + CBS_OPEN_OK, \ + CBS_OPEN_ERROR, \ + CBS_OPEN_CANCELLED + +DEFINE_ENUM(CBS_OPEN_COMPLETE_RESULT, CBS_OPEN_COMPLETE_RESULT_VALUES) + + typedef struct CBS_INSTANCE_TAG* CBS_HANDLE; + typedef void(*ON_CBS_OPEN_COMPLETE)(void* context, CBS_OPEN_COMPLETE_RESULT open_complete_result); + typedef void(*ON_CBS_ERROR)(void* context); + typedef void(*ON_CBS_OPERATION_COMPLETE)(void* context, CBS_OPERATION_RESULT complete_result, unsigned int status_code, const char* status_description); + + MOCKABLE_FUNCTION(, CBS_HANDLE, cbs_create, SESSION_HANDLE, session); + MOCKABLE_FUNCTION(, void, cbs_destroy, CBS_HANDLE, cbs); + MOCKABLE_FUNCTION(, int, cbs_open_async, CBS_HANDLE, cbs, ON_CBS_OPEN_COMPLETE, on_cbs_open_complete, void*, on_cbs_open_complete_context, ON_CBS_ERROR, on_cbs_error, void*, on_cbs_error_context); + MOCKABLE_FUNCTION(, int, cbs_close, CBS_HANDLE, cbs); + MOCKABLE_FUNCTION(, int, cbs_put_token_async, CBS_HANDLE, cbs, const char*, type, const char*, audience, const char*, token, ON_CBS_OPERATION_COMPLETE, on_cbs_put_token_complete, void*, on_cbs_put_token_complete_context); + MOCKABLE_FUNCTION(, int, cbs_delete_token_async, CBS_HANDLE, cbs, const char*, type, const char*, audience, ON_CBS_OPERATION_COMPLETE, on_cbs_delete_token_complete, void*, on_cbs_delete_token_complete_context); + MOCKABLE_FUNCTION(, int, cbs_set_trace, CBS_HANDLE, cbs, bool, trace_on); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CBS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/connection.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/amqp_definitions_fields.h" +#include "azure_uamqp_c/amqp_definitions_milliseconds.h" +#include "azure_uamqp_c/amqp_definitions_error.h" +#include "azure_uamqp_c/amqpvalue.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct CONNECTION_INSTANCE_TAG* CONNECTION_HANDLE; + typedef struct ENDPOINT_INSTANCE_TAG* ENDPOINT_HANDLE; + typedef struct ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_TAG* ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE; + + typedef enum CONNECTION_STATE_TAG + { + /* Codes_S_R_S_CONNECTION_01_039: [START In this state a connection exists, but nothing has been sent or received. This is the state an implementation would be in immediately after performing a socket connect or socket accept.] */ + CONNECTION_STATE_START, + + /* Codes_S_R_S_CONNECTION_01_040: [HDR RCVD In this state the connection header has been received from the peer but a connection header has not been sent.] */ + CONNECTION_STATE_HDR_RCVD, + + /* Codes_S_R_S_CONNECTION_01_041: [HDR SENT In this state the connection header has been sent to the peer but no connection header has been received.] */ + CONNECTION_STATE_HDR_SENT, + + /* Codes_S_R_S_CONNECTION_01_042: [HDR EXCH In this state the connection header has been sent to the peer and a connection header has been received from the peer.] */ + CONNECTION_STATE_HDR_EXCH, + + /* Codes_S_R_S_CONNECTION_01_043: [OPEN PIPE In this state both the connection header and the open frame have been sent but nothing has been received.] */ + CONNECTION_STATE_OPEN_PIPE, + + /* Codes_S_R_S_CONNECTION_01_044: [OC PIPE In this state, the connection header, the open frame, any pipelined connection traffic, and the close frame have been sent but nothing has been received.] */ + CONNECTION_STATE_OC_PIPE, + + /* Codes_S_R_S_CONNECTION_01_045: [OPEN RCVD In this state the connection headers have been exchanged. An open frame has been received from the peer but an open frame has not been sent.] */ + CONNECTION_STATE_OPEN_RCVD, + + /* Codes_S_R_S_CONNECTION_01_046: [OPEN SENT In this state the connection headers have been exchanged. An open frame has been sent to the peer but no open frame has yet been received.] */ + CONNECTION_STATE_OPEN_SENT, + + /* Codes_S_R_S_CONNECTION_01_047: [CLOSE PIPE In this state the connection headers have been exchanged. An open frame, any pipelined connection traffic, and the close frame have been sent but no open frame has yet been received from the peer.] */ + CONNECTION_STATE_CLOSE_PIPE, + + /* Codes_S_R_S_CONNECTION_01_048: [OPENED In this state the connection header and the open frame have been both sent and received.] */ + CONNECTION_STATE_OPENED, + + /* Codes_S_R_S_CONNECTION_01_049: [CLOSE RCVD In this state a close frame has been received indicating that the peer has initiated an AMQP close.] */ + CONNECTION_STATE_CLOSE_RCVD, + + /* Codes_S_R_S_CONNECTION_01_053: [CLOSE SENT In this state a close frame has been sent to the peer. It is illegal to write anything more onto the connection, however there could potentially still be incoming frames.] */ + CONNECTION_STATE_CLOSE_SENT, + + /* Codes_S_R_S_CONNECTION_01_055: [DISCARDING The DISCARDING state is a variant of the CLOSE SENT state where the close is triggered by an error.] */ + CONNECTION_STATE_DISCARDING, + + /* Codes_S_R_S_CONNECTION_01_057: [END In this state it is illegal for either endpoint to write anything more onto the connection. The connection can be safely closed and discarded.] */ + CONNECTION_STATE_END, + + /* Codes_S_R_S_CONNECTION_09_001: [ERROR In this state the connection has failed, most likely due to a socket error, and should not be reused.] */ + CONNECTION_STATE_ERROR + } CONNECTION_STATE; + + typedef void(*ON_ENDPOINT_FRAME_RECEIVED)(void* context, AMQP_VALUE performative, uint32_t frame_payload_size, const unsigned char* payload_bytes); + typedef void(*ON_CONNECTION_STATE_CHANGED)(void* context, CONNECTION_STATE new_connection_state, CONNECTION_STATE previous_connection_state); + typedef void(*ON_CONNECTION_CLOSE_RECEIVED)(void* context, ERROR_HANDLE error); + typedef bool(*ON_NEW_ENDPOINT)(void* context, ENDPOINT_HANDLE new_endpoint); + + MOCKABLE_FUNCTION(, CONNECTION_HANDLE, connection_create, XIO_HANDLE, io, const char*, hostname, const char*, container_id, ON_NEW_ENDPOINT, on_new_endpoint, void*, callback_context); + MOCKABLE_FUNCTION(, CONNECTION_HANDLE, connection_create2, XIO_HANDLE, xio, const char*, hostname, const char*, container_id, ON_NEW_ENDPOINT, on_new_endpoint, void*, callback_context, ON_CONNECTION_STATE_CHANGED, on_connection_state_changed, void*, on_connection_state_changed_context, ON_IO_ERROR, on_io_error, void*, on_io_error_context); + MOCKABLE_FUNCTION(, void, connection_destroy, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, int, connection_open, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, int, connection_listen, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, int, connection_close, CONNECTION_HANDLE, connection, const char*, condition_value, const char*, description, AMQP_VALUE, info); + MOCKABLE_FUNCTION(, int, connection_set_max_frame_size, CONNECTION_HANDLE, connection, uint32_t, max_frame_size); + MOCKABLE_FUNCTION(, int, connection_get_max_frame_size, CONNECTION_HANDLE, connection, uint32_t*, max_frame_size); + MOCKABLE_FUNCTION(, int, connection_set_channel_max, CONNECTION_HANDLE, connection, uint16_t, channel_max); + MOCKABLE_FUNCTION(, int, connection_get_channel_max, CONNECTION_HANDLE, connection, uint16_t*, channel_max); + MOCKABLE_FUNCTION(, int, connection_set_idle_timeout, CONNECTION_HANDLE, connection, milliseconds, idle_timeout); + MOCKABLE_FUNCTION(, int, connection_get_idle_timeout, CONNECTION_HANDLE, connection, milliseconds*, idle_timeout); + MOCKABLE_FUNCTION(, int, connection_set_properties, CONNECTION_HANDLE, connection, fields, properties); + MOCKABLE_FUNCTION(, int, connection_get_properties, CONNECTION_HANDLE, connection, fields*, properties); + MOCKABLE_FUNCTION(, int, connection_get_remote_max_frame_size, CONNECTION_HANDLE, connection, uint32_t*, remote_max_frame_size); + MOCKABLE_FUNCTION(, int, connection_set_remote_idle_timeout_empty_frame_send_ratio, CONNECTION_HANDLE, connection, double, idle_timeout_empty_frame_send_ratio); + MOCKABLE_FUNCTION(, uint64_t, connection_handle_deadlines, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, void, connection_dowork, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, ENDPOINT_HANDLE, connection_create_endpoint, CONNECTION_HANDLE, connection); + MOCKABLE_FUNCTION(, int, connection_start_endpoint, ENDPOINT_HANDLE, endpoint, ON_ENDPOINT_FRAME_RECEIVED, on_frame_received, ON_CONNECTION_STATE_CHANGED, on_connection_state_changed, void*, context); + MOCKABLE_FUNCTION(, int, connection_endpoint_get_incoming_channel, ENDPOINT_HANDLE, endpoint, uint16_t*, incoming_channel); + MOCKABLE_FUNCTION(, void, connection_destroy_endpoint, ENDPOINT_HANDLE, endpoint); + MOCKABLE_FUNCTION(, int, connection_encode_frame, ENDPOINT_HANDLE, endpoint, AMQP_VALUE, performative, PAYLOAD*, payloads, size_t, payload_count, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); + MOCKABLE_FUNCTION(, void, connection_set_trace, CONNECTION_HANDLE, connection, bool, trace_on); + + MOCKABLE_FUNCTION(, ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE, connection_subscribe_on_connection_close_received, CONNECTION_HANDLE, connection, ON_CONNECTION_CLOSE_RECEIVED, on_connection_close_received, void*, context); + MOCKABLE_FUNCTION(, void, connection_unsubscribe_on_connection_close_received, ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE, event_subscription); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CONNECTION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/frame_codec.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef FRAME_CODEC_H +#define FRAME_CODEC_H + +#include "azure_uamqp_c/amqpvalue.h" + +#ifdef __cplusplus +extern "C" { +#include <cstdint> +#include <cstddef> +#else +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct PAYLOAD_TAG +{ + const unsigned char* bytes; + size_t length; +} PAYLOAD; + +/* Codes_SRS_FRAME_CODEC_01_016: [The type code indicates the format and purpose of the frame.] */ +/* Codes_SRS_FRAME_CODEC_01_017: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */ +/* Codes_SRS_FRAME_CODEC_01_070: [The type code indicates the format and purpose of the frame.] */ +/* Codes_SRS_FRAME_CODEC_01_071: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */ +/* Codes_SRS_FRAME_CODEC_01_018: [A type code of 0x00 indicates that the frame is an AMQP frame.] */ +/* Codes_SRS_FRAME_CODEC_01_072: [A type code of 0x00 indicates that the frame is an AMQP frame.] */ +#define FRAME_TYPE_AMQP (uint8_t)0x00 + +/* Codes_SRS_FRAME_CODEC_01_073: [A type code of 0x01 indicates that the frame is a SASL frame] */ +/* Codes_SRS_FRAME_CODEC_01_019: [A type code of 0x01 indicates that the frame is a SASL frame] */ +#define FRAME_TYPE_SASL (uint8_t)0x01 + + typedef struct FRAME_CODEC_INSTANCE_TAG* FRAME_CODEC_HANDLE; + typedef void(*ON_FRAME_RECEIVED)(void* context, const unsigned char* type_specific, uint32_t type_specific_size, const unsigned char* frame_body, uint32_t frame_body_size); + typedef void(*ON_FRAME_CODEC_ERROR)(void* context); + typedef void(*ON_BYTES_ENCODED)(void* context, const unsigned char* bytes, size_t length, bool encode_complete); + + MOCKABLE_FUNCTION(, FRAME_CODEC_HANDLE, frame_codec_create, ON_FRAME_CODEC_ERROR, on_frame_codec_error, void*, callback_context); + MOCKABLE_FUNCTION(, void, frame_codec_destroy, FRAME_CODEC_HANDLE, frame_codec); + MOCKABLE_FUNCTION(, int, frame_codec_set_max_frame_size, FRAME_CODEC_HANDLE, frame_codec, uint32_t, max_frame_size); + MOCKABLE_FUNCTION(, int, frame_codec_subscribe, FRAME_CODEC_HANDLE, frame_codec, uint8_t, type, ON_FRAME_RECEIVED, on_frame_received, void*, callback_context); + MOCKABLE_FUNCTION(, int, frame_codec_unsubscribe, FRAME_CODEC_HANDLE, frame_codec, uint8_t, type); + MOCKABLE_FUNCTION(, int, frame_codec_receive_bytes, FRAME_CODEC_HANDLE, frame_codec, const unsigned char*, buffer, size_t, size); + MOCKABLE_FUNCTION(, int, frame_codec_encode_frame, FRAME_CODEC_HANDLE, frame_codec, uint8_t, type, const PAYLOAD*, payloads, size_t, payload_count, const unsigned char*, type_specific_bytes, uint32_t, type_specific_size, ON_BYTES_ENCODED, on_bytes_encoded, void*, callback_context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* FRAME_CODEC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/header_detect_io.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HEADER_DETECT_IO_H +#define HEADER_DETECT_IO_H + +#include "azure_c_shared_utility/xio.h" + +#ifdef __cplusplus +#include <cstddef> +extern "C" { +#else +#include <stddef.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/xio.h" + + typedef struct AMQP_HEADER_TAG + { + const unsigned char* header_bytes; + size_t header_size; + } AMQP_HEADER; + + typedef struct HEADER_DETECT_ENTRY_TAG + { + AMQP_HEADER header; + const IO_INTERFACE_DESCRIPTION* io_interface_description; + } HEADER_DETECT_ENTRY; + + typedef struct HEADER_DETECT_IO_CONFIG_TAG + { + XIO_HANDLE underlying_io; + HEADER_DETECT_ENTRY* header_detect_entries; + size_t header_detect_entry_count; + } HEADER_DETECT_IO_CONFIG; + + MOCKABLE_FUNCTION(, AMQP_HEADER, header_detect_io_get_amqp_header); + MOCKABLE_FUNCTION(, AMQP_HEADER, header_detect_io_get_sasl_amqp_header); + MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, header_detect_io_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* HEADER_DETECT_IO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/link.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LINK_H +#define LINK_H + +#include <stddef.h> +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/async_operation.h" +#include "azure_uamqp_c/amqp_definitions_sender_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_receiver_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_fields.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct LINK_INSTANCE_TAG* LINK_HANDLE; + +#define LINK_STATE_VALUES \ + LINK_STATE_DETACHED, \ + LINK_STATE_HALF_ATTACHED_ATTACH_SENT, \ + LINK_STATE_HALF_ATTACHED_ATTACH_RECEIVED, \ + LINK_STATE_ATTACHED, \ + LINK_STATE_ERROR + +DEFINE_ENUM(LINK_STATE, LINK_STATE_VALUES) + +#define LINK_TRANSFER_RESULT_VALUES \ + LINK_TRANSFER_ERROR, \ + LINK_TRANSFER_BUSY + +DEFINE_ENUM(LINK_TRANSFER_RESULT, LINK_TRANSFER_RESULT_VALUES) + +#define LINK_DELIVERY_SETTLE_REASON_VALUES \ + LINK_DELIVERY_SETTLE_REASON_DISPOSITION_RECEIVED, \ + LINK_DELIVERY_SETTLE_REASON_SETTLED, \ + LINK_DELIVERY_SETTLE_REASON_NOT_DELIVERED, \ + LINK_DELIVERY_SETTLE_REASON_TIMEOUT, \ + LINK_DELIVERY_SETTLE_REASON_CANCELLED + +DEFINE_ENUM(LINK_DELIVERY_SETTLE_REASON, LINK_DELIVERY_SETTLE_REASON_VALUES) + +typedef struct ON_LINK_DETACH_EVENT_SUBSCRIPTION_TAG* ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE; + +typedef void(*ON_DELIVERY_SETTLED)(void* context, delivery_number delivery_no, LINK_DELIVERY_SETTLE_REASON reason, AMQP_VALUE delivery_state); +typedef AMQP_VALUE(*ON_TRANSFER_RECEIVED)(void* context, TRANSFER_HANDLE transfer, uint32_t payload_size, const unsigned char* payload_bytes); +typedef void(*ON_LINK_STATE_CHANGED)(void* context, LINK_STATE new_link_state, LINK_STATE previous_link_state); +typedef void(*ON_LINK_FLOW_ON)(void* context); +typedef void(*ON_LINK_DETACH_RECEIVED)(void* context, ERROR_HANDLE error); + +MOCKABLE_FUNCTION(, LINK_HANDLE, link_create, SESSION_HANDLE, session, const char*, name, role, role, AMQP_VALUE, source, AMQP_VALUE, target); +MOCKABLE_FUNCTION(, LINK_HANDLE, link_create_from_endpoint, SESSION_HANDLE, session, LINK_ENDPOINT_HANDLE, link_endpoint, const char*, name, role, role, AMQP_VALUE, source, AMQP_VALUE, target); +MOCKABLE_FUNCTION(, void, link_destroy, LINK_HANDLE, handle); +MOCKABLE_FUNCTION(, int, link_set_snd_settle_mode, LINK_HANDLE, link, sender_settle_mode, snd_settle_mode); +MOCKABLE_FUNCTION(, int, link_get_snd_settle_mode, LINK_HANDLE, link, sender_settle_mode*, snd_settle_mode); +MOCKABLE_FUNCTION(, int, link_set_rcv_settle_mode, LINK_HANDLE, link, receiver_settle_mode, rcv_settle_mode); +MOCKABLE_FUNCTION(, int, link_get_rcv_settle_mode, LINK_HANDLE, link, receiver_settle_mode*, rcv_settle_mode); +MOCKABLE_FUNCTION(, int, link_set_initial_delivery_count, LINK_HANDLE, link, sequence_no, initial_delivery_count); +MOCKABLE_FUNCTION(, int, link_get_initial_delivery_count, LINK_HANDLE, link, sequence_no*, initial_delivery_count); +MOCKABLE_FUNCTION(, int, link_set_max_message_size, LINK_HANDLE, link, uint64_t, max_message_size); +MOCKABLE_FUNCTION(, int, link_get_max_message_size, LINK_HANDLE, link, uint64_t*, max_message_size); +MOCKABLE_FUNCTION(, int, link_get_peer_max_message_size, LINK_HANDLE, link, uint64_t*, peer_max_message_size); +MOCKABLE_FUNCTION(, int, link_set_attach_properties, LINK_HANDLE, link, fields, attach_properties); +MOCKABLE_FUNCTION(, int, link_set_max_link_credit, LINK_HANDLE, link, uint32_t, max_link_credit); +MOCKABLE_FUNCTION(, int, link_get_name, LINK_HANDLE, link, const char**, link_name); +MOCKABLE_FUNCTION(, int, link_get_received_message_id, LINK_HANDLE, link, delivery_number*, message_id); +MOCKABLE_FUNCTION(, int, link_send_disposition, LINK_HANDLE, link, delivery_number, message_number, AMQP_VALUE, delivery_state); +MOCKABLE_FUNCTION(, int, link_attach, LINK_HANDLE, link, ON_TRANSFER_RECEIVED, on_transfer_received, ON_LINK_STATE_CHANGED, on_link_state_changed, ON_LINK_FLOW_ON, on_link_flow_on, void*, callback_context); +MOCKABLE_FUNCTION(, int, link_detach, LINK_HANDLE, link, bool, close, const char*, error_condition, const char*, error_description, AMQP_VALUE, info); +MOCKABLE_FUNCTION(, ASYNC_OPERATION_HANDLE, link_transfer_async, LINK_HANDLE, handle, message_format, message_format, PAYLOAD*, payloads, size_t, payload_count, ON_DELIVERY_SETTLED, on_delivery_settled, void*, callback_context, LINK_TRANSFER_RESULT*, link_transfer_result,tickcounter_ms_t, timeout); +MOCKABLE_FUNCTION(, void, link_dowork, LINK_HANDLE, link); + +MOCKABLE_FUNCTION(, ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE, link_subscribe_on_link_detach_received, LINK_HANDLE, link, ON_LINK_DETACH_RECEIVED, on_link_detach_received, void*, context); +MOCKABLE_FUNCTION(, void, link_unsubscribe_on_link_detach_received, ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE, event_subscription); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LINK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/message.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions_annotations.h" +#include "azure_uamqp_c/amqp_definitions_message_annotations.h" +#include "azure_uamqp_c/amqp_definitions_sequence_no.h" +#include "azure_uamqp_c/amqp_definitions_properties.h" +#include "azure_uamqp_c/amqp_definitions_milliseconds.h" +#include "azure_uamqp_c/amqp_definitions_header.h" +#include "azure_uamqp_c/amqp_definitions_delivery_annotations.h" + + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#include <cstdint> +#else +#include <stddef.h> +#include <stdint.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + #define MESSAGE_BODY_TYPE_VALUES \ + MESSAGE_BODY_TYPE_NONE, \ + MESSAGE_BODY_TYPE_DATA, \ + MESSAGE_BODY_TYPE_SEQUENCE, \ + MESSAGE_BODY_TYPE_VALUE + + DEFINE_ENUM(MESSAGE_BODY_TYPE, MESSAGE_BODY_TYPE_VALUES) + + typedef struct MESSAGE_INSTANCE_TAG* MESSAGE_HANDLE; + typedef struct BINARY_DATA_TAG + { + const unsigned char* bytes; + size_t length; + } BINARY_DATA; + + MOCKABLE_FUNCTION(, MESSAGE_HANDLE, message_create); + MOCKABLE_FUNCTION(, MESSAGE_HANDLE, message_clone, MESSAGE_HANDLE, source_message); + MOCKABLE_FUNCTION(, void, message_destroy, MESSAGE_HANDLE, message); + MOCKABLE_FUNCTION(, int, message_set_header, MESSAGE_HANDLE, message, HEADER_HANDLE, message_header); + MOCKABLE_FUNCTION(, int, message_get_header, MESSAGE_HANDLE, message, HEADER_HANDLE*, message_header); + MOCKABLE_FUNCTION(, int, message_set_delivery_annotations, MESSAGE_HANDLE, message, delivery_annotations, annotations); + MOCKABLE_FUNCTION(, int, message_get_delivery_annotations, MESSAGE_HANDLE, message, delivery_annotations*, annotations); + MOCKABLE_FUNCTION(, int, message_set_message_annotations, MESSAGE_HANDLE, message, message_annotations, annotations); + MOCKABLE_FUNCTION(, int, message_get_message_annotations, MESSAGE_HANDLE, message, message_annotations*, annotations); + MOCKABLE_FUNCTION(, int, message_set_properties, MESSAGE_HANDLE, message, PROPERTIES_HANDLE, properties); + MOCKABLE_FUNCTION(, int, message_get_properties, MESSAGE_HANDLE, message, PROPERTIES_HANDLE*, properties); + MOCKABLE_FUNCTION(, int, message_set_application_properties, MESSAGE_HANDLE, message, AMQP_VALUE, application_properties); + MOCKABLE_FUNCTION(, int, message_get_application_properties, MESSAGE_HANDLE, message, AMQP_VALUE*, application_properties); + MOCKABLE_FUNCTION(, int, message_set_footer, MESSAGE_HANDLE, message, annotations, footer); + MOCKABLE_FUNCTION(, int, message_get_footer, MESSAGE_HANDLE, message, annotations*, footer); + MOCKABLE_FUNCTION(, int, message_add_body_amqp_data, MESSAGE_HANDLE, message, BINARY_DATA, amqp_data); + MOCKABLE_FUNCTION(, int, message_get_body_amqp_data_in_place, MESSAGE_HANDLE, message, size_t, index, BINARY_DATA*, amqp_data); + MOCKABLE_FUNCTION(, int, message_get_body_amqp_data_count, MESSAGE_HANDLE, message, size_t*, count); + MOCKABLE_FUNCTION(, int, message_set_body_amqp_value, MESSAGE_HANDLE, message, AMQP_VALUE, body_amqp_value); + MOCKABLE_FUNCTION(, int, message_get_body_amqp_value_in_place, MESSAGE_HANDLE, message, AMQP_VALUE*, body_amqp_value); + MOCKABLE_FUNCTION(, int, message_add_body_amqp_sequence, MESSAGE_HANDLE, message, AMQP_VALUE, sequence); + MOCKABLE_FUNCTION(, int, message_get_body_amqp_sequence_in_place, MESSAGE_HANDLE, message, size_t, index, AMQP_VALUE*, sequence); + MOCKABLE_FUNCTION(, int, message_get_body_amqp_sequence_count, MESSAGE_HANDLE, message, size_t*, count); + MOCKABLE_FUNCTION(, int, message_get_body_type, MESSAGE_HANDLE, message, MESSAGE_BODY_TYPE*, body_type); + MOCKABLE_FUNCTION(, int, message_set_message_format, MESSAGE_HANDLE, message, uint32_t, message_format); + MOCKABLE_FUNCTION(, int, message_get_message_format, MESSAGE_HANDLE, message, uint32_t*, message_format); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MESSAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/message_receiver.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MESSAGE_RECEIVER_H +#define MESSAGE_RECEIVER_H + +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/amqp_definitions_delivery_number.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif /* __cplusplus */ + +#define MESSAGE_RECEIVER_STATE_VALUES \ + MESSAGE_RECEIVER_STATE_IDLE, \ + MESSAGE_RECEIVER_STATE_OPENING, \ + MESSAGE_RECEIVER_STATE_OPEN, \ + MESSAGE_RECEIVER_STATE_CLOSING, \ + MESSAGE_RECEIVER_STATE_ERROR + +DEFINE_ENUM(MESSAGE_RECEIVER_STATE, MESSAGE_RECEIVER_STATE_VALUES) + + typedef struct MESSAGE_RECEIVER_INSTANCE_TAG* MESSAGE_RECEIVER_HANDLE; + typedef AMQP_VALUE (*ON_MESSAGE_RECEIVED)(const void* context, MESSAGE_HANDLE message); + typedef void(*ON_MESSAGE_RECEIVER_STATE_CHANGED)(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state); + + MOCKABLE_FUNCTION(, MESSAGE_RECEIVER_HANDLE, messagereceiver_create, LINK_HANDLE, link, ON_MESSAGE_RECEIVER_STATE_CHANGED, on_message_receiver_state_changed, void*, context); + MOCKABLE_FUNCTION(, void, messagereceiver_destroy, MESSAGE_RECEIVER_HANDLE, message_receiver); + MOCKABLE_FUNCTION(, int, messagereceiver_open, MESSAGE_RECEIVER_HANDLE, message_receiver, ON_MESSAGE_RECEIVED, on_message_received, void*, callback_context); + MOCKABLE_FUNCTION(, int, messagereceiver_close, MESSAGE_RECEIVER_HANDLE, message_receiver); + MOCKABLE_FUNCTION(, int, messagereceiver_get_link_name, MESSAGE_RECEIVER_HANDLE, message_receiver, const char**, link_name); + MOCKABLE_FUNCTION(, int, messagereceiver_get_received_message_id, MESSAGE_RECEIVER_HANDLE, message_receiver, delivery_number*, message_number); + MOCKABLE_FUNCTION(, int, messagereceiver_send_message_disposition, MESSAGE_RECEIVER_HANDLE, message_receiver, const char*, link_name, delivery_number, message_number, AMQP_VALUE, delivery_state); + MOCKABLE_FUNCTION(, void, messagereceiver_set_trace, MESSAGE_RECEIVER_HANDLE, message_receiver, bool, trace_on); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MESSAGE_RECEIVER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/message_sender.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MESSAGE_SENDER_H +#define MESSAGE_SENDER_H + +#include <stdbool.h> +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/message.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/async_operation.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MESSAGE_SEND_RESULT_VALUES \ + MESSAGE_SEND_OK, \ + MESSAGE_SEND_ERROR, \ + MESSAGE_SEND_TIMEOUT, \ + MESSAGE_SEND_CANCELLED + +DEFINE_ENUM(MESSAGE_SEND_RESULT, MESSAGE_SEND_RESULT_VALUES) + +#define MESSAGE_SENDER_STATE_VALUES \ + MESSAGE_SENDER_STATE_IDLE, \ + MESSAGE_SENDER_STATE_OPENING, \ + MESSAGE_SENDER_STATE_OPEN, \ + MESSAGE_SENDER_STATE_CLOSING, \ + MESSAGE_SENDER_STATE_ERROR + +DEFINE_ENUM(MESSAGE_SENDER_STATE, MESSAGE_SENDER_STATE_VALUES) + + typedef struct MESSAGE_SENDER_INSTANCE_TAG* MESSAGE_SENDER_HANDLE; + typedef void(*ON_MESSAGE_SEND_COMPLETE)(void* context, MESSAGE_SEND_RESULT send_result, AMQP_VALUE delivery_state); + typedef void(*ON_MESSAGE_SENDER_STATE_CHANGED)(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state); + + MOCKABLE_FUNCTION(, MESSAGE_SENDER_HANDLE, messagesender_create, LINK_HANDLE, link, ON_MESSAGE_SENDER_STATE_CHANGED, on_message_sender_state_changed, void*, context); + MOCKABLE_FUNCTION(, void, messagesender_destroy, MESSAGE_SENDER_HANDLE, message_sender); + MOCKABLE_FUNCTION(, int, messagesender_open, MESSAGE_SENDER_HANDLE, message_sender); + MOCKABLE_FUNCTION(, int, messagesender_close, MESSAGE_SENDER_HANDLE, message_sender); + MOCKABLE_FUNCTION(, ASYNC_OPERATION_HANDLE, messagesender_send_async, MESSAGE_SENDER_HANDLE, message_sender, MESSAGE_HANDLE, message, ON_MESSAGE_SEND_COMPLETE, on_message_send_complete, void*, callback_context, tickcounter_ms_t, timeout); + MOCKABLE_FUNCTION(, void, messagesender_set_trace, MESSAGE_SENDER_HANDLE, message_sender, bool, traceOn); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MESSAGE_SENDER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/messaging.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MESSAGING_H +#define MESSAGING_H + +#include "azure_uamqp_c/amqpvalue.h" + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_create_source, const char*, address); + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_create_target, const char*, address); + + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_delivery_received, uint32_t, section_number, uint64_t, section_offset); + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_delivery_accepted); + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_delivery_rejected, const char*, error_condition, const char*, error_description); + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_delivery_released); + MOCKABLE_FUNCTION(, AMQP_VALUE, messaging_delivery_modified, bool, delivery_failed, bool, undeliverable_here, fields, message_annotations); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MESSAGING_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_anonymous.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_ANONYMOUS_H +#define SASL_ANONYMOUS_H + +#include "azure_uamqp_c/sasl_mechanism.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + MOCKABLE_FUNCTION(, const SASL_MECHANISM_INTERFACE_DESCRIPTION*, saslanonymous_get_interface); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_ANONYMOUS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_frame_codec.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_FRAME_CODEC_H +#define SASL_FRAME_CODEC_H + +#ifdef __cplusplus +extern "C" { +#include <cstdint> +#include <cstddef> +#else +#include <stdint.h> +#include <stddef.h> +#endif /* __cplusplus */ +#include "azure_uamqp_c/frame_codec.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +#define SASL_MECHANISMS (uint64_t)0x40 +#define SASL_INIT (uint64_t)0x41 +#define SASL_CHALLENGE (uint64_t)0x42 +#define SASL_RESPONSE (uint64_t)0x43 +#define SASL_OUTCOME (uint64_t)0x44 + +typedef struct SASL_FRAME_CODEC_INSTANCE_TAG* SASL_FRAME_CODEC_HANDLE; +typedef void(*ON_SASL_FRAME_RECEIVED)(void* context, AMQP_VALUE sasl_frame_value); +typedef void(*ON_SASL_FRAME_CODEC_ERROR)(void* context); + +MOCKABLE_FUNCTION(, SASL_FRAME_CODEC_HANDLE, sasl_frame_codec_create, FRAME_CODEC_HANDLE, frame_codec, ON_SASL_FRAME_RECEIVED, on_sasl_frame_received, ON_SASL_FRAME_CODEC_ERROR, on_sasl_frame_codec_error, void*, callback_context); +MOCKABLE_FUNCTION(, void, sasl_frame_codec_destroy, SASL_FRAME_CODEC_HANDLE, sasl_frame_codec); +MOCKABLE_FUNCTION(, int, sasl_frame_codec_encode_frame, SASL_FRAME_CODEC_HANDLE, sasl_frame_codec, AMQP_VALUE, sasl_frame_value, ON_BYTES_ENCODED, on_bytes_encoded, void*, callback_context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_FRAME_CODEC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_mechanism.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_MECHANISM_H +#define SASL_MECHANISM_H + +#ifdef __cplusplus +extern "C" { +#include "cstdint" +#else +#include "stdint.h" +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_MECHANISM_INSTANCE_TAG* SASL_MECHANISM_HANDLE; + typedef void* CONCRETE_SASL_MECHANISM_HANDLE; + + typedef struct SASL_MECHANISM_BYTES_TAG + { + const void* bytes; + uint32_t length; + } SASL_MECHANISM_BYTES; + + typedef CONCRETE_SASL_MECHANISM_HANDLE(*SASL_MECHANISM_CREATE)(void* config); + typedef void(*SASL_MECHANISM_DESTROY)(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism); + typedef int(*SASL_MECHANISM_GET_INIT_BYTES)(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism, SASL_MECHANISM_BYTES* init_bytes); + typedef const char*(*SASL_MECHANISM_GET_MECHANISM_NAME)(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism); + typedef int(*SASL_MECHANISM_CHALLENGE)(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism, const SASL_MECHANISM_BYTES* challenge_bytes, SASL_MECHANISM_BYTES* response_bytes); + + typedef struct SASL_MECHANISM_INTERFACE_TAG + { + SASL_MECHANISM_CREATE concrete_sasl_mechanism_create; + SASL_MECHANISM_DESTROY concrete_sasl_mechanism_destroy; + SASL_MECHANISM_GET_INIT_BYTES concrete_sasl_mechanism_get_init_bytes; + SASL_MECHANISM_GET_MECHANISM_NAME concrete_sasl_mechanism_get_mechanism_name; + SASL_MECHANISM_CHALLENGE concrete_sasl_mechanism_challenge; + } SASL_MECHANISM_INTERFACE_DESCRIPTION; + + MOCKABLE_FUNCTION(, SASL_MECHANISM_HANDLE, saslmechanism_create, const SASL_MECHANISM_INTERFACE_DESCRIPTION*, sasl_mechanism_interface_description, void*, sasl_mechanism_create_parameters); + MOCKABLE_FUNCTION(, void, saslmechanism_destroy, SASL_MECHANISM_HANDLE, sasl_mechanism); + MOCKABLE_FUNCTION(, int, saslmechanism_get_init_bytes, SASL_MECHANISM_HANDLE, sasl_mechanism, SASL_MECHANISM_BYTES*, init_bytes); + MOCKABLE_FUNCTION(, const char*, saslmechanism_get_mechanism_name, SASL_MECHANISM_HANDLE, sasl_mechanism); + MOCKABLE_FUNCTION(, int, saslmechanism_challenge, SASL_MECHANISM_HANDLE, sasl_mechanism, const SASL_MECHANISM_BYTES*, challenge_bytes, SASL_MECHANISM_BYTES*, response_bytes); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_MECHANISM_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_mssbcbs.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_MSSBCBS_H +#define SASL_MSSBCBS_H + +#include "azure_uamqp_c/sasl_mechanism.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + MOCKABLE_FUNCTION(, CONCRETE_SASL_MECHANISM_HANDLE, saslmssbcbs_create, void*, config); + MOCKABLE_FUNCTION(, void, saslmssbcbs_destroy, CONCRETE_SASL_MECHANISM_HANDLE, sasl_mechanism_concrete_handle); + MOCKABLE_FUNCTION(, int, saslmssbcbs_get_init_bytes, CONCRETE_SASL_MECHANISM_HANDLE, sasl_mechanism_concrete_handle, SASL_MECHANISM_BYTES*, init_bytes); + MOCKABLE_FUNCTION(, const char*, saslmssbcbs_get_mechanism_name, CONCRETE_SASL_MECHANISM_HANDLE, sasl_mechanism); + MOCKABLE_FUNCTION(, int, saslmssbcbs_challenge, CONCRETE_SASL_MECHANISM_HANDLE, concrete_sasl_mechanism, const SASL_MECHANISM_BYTES*, challenge_bytes, SASL_MECHANISM_BYTES*, response_bytes); + MOCKABLE_FUNCTION(, const SASL_MECHANISM_INTERFACE_DESCRIPTION*, saslmssbcbs_get_interface); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_MSSBCBS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_plain.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_PLAIN_H +#define SASL_PLAIN_H + +#include "azure_uamqp_c/sasl_mechanism.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_PLAIN_CONFIG_TAG + { + const char* authcid; + const char* passwd; + const char* authzid; + } SASL_PLAIN_CONFIG; + + MOCKABLE_FUNCTION(, const SASL_MECHANISM_INTERFACE_DESCRIPTION*, saslplain_get_interface); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_PLAIN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_server_io.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_SERVER_IO_H +#define SASL_SERVER_IO_H + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#include <stdbool.h> +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/xio.h" +#include "azure_uamqp_c/server_protocol_io.h" +#include "azure_uamqp_c/sasl_server_mechanism.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct SASL_SERVER_IO_TAG +{ + SERVER_PROTOCOL_IO_CONFIG server_protocol_io; + const SASL_SERVER_MECHANISM_HANDLE* sasl_server_mechanisms; + size_t sasl_server_mechanism_count; +} SASL_SERVER_IO; + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, sasl_server_io_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_SERVER_IO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/sasl_server_mechanism.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASL_SERVER_MECHANISM_H +#define SASL_SERVER_MECHANISM_H + +#ifdef __cplusplus +extern "C" { +#include "cstdint" +#else +#include "stdint.h" +#include "stdbool.h" +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SASL_SERVER_MECHANISM_INSTANCE_TAG* SASL_SERVER_MECHANISM_HANDLE; + typedef void* CONCRETE_SASL_SERVER_MECHANISM_HANDLE; + + typedef struct SASL_SERVER_MECHANISM_BYTES_TAG + { + const void* bytes; + uint32_t length; + } SASL_SERVER_MECHANISM_BYTES; + + typedef CONCRETE_SASL_SERVER_MECHANISM_HANDLE(*SASL_SERVER_MECHANISM_CREATE)(void* config); + typedef void(*SASL_SERVER_MECHANISM_DESTROY)(CONCRETE_SASL_SERVER_MECHANISM_HANDLE concrete_sasl_server_mechanism); + typedef int(*SASL_SERVER_MECHANISM_HANDLE_INITIAL_RESPONSE)(CONCRETE_SASL_SERVER_MECHANISM_HANDLE concrete_sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES* initial_response_bytes, const char* hostname, bool* send_challenge, SASL_SERVER_MECHANISM_BYTES* challenge_bytes); + typedef int(*SASL_SERVER_MECHANISM_HANDLE_RESPONSE)(CONCRETE_SASL_SERVER_MECHANISM_HANDLE concrete_sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES* response_bytes, bool* send_next_challenge, SASL_SERVER_MECHANISM_BYTES* next_challenge_bytes); + typedef const char*(*SASL_SERVER_MECHANISM_GET_MECHANISM_NAME)(void); + + typedef struct SASL_SERVER_MECHANISM_INTERFACE_DESCRIPTION_TAG + { + SASL_SERVER_MECHANISM_CREATE create; + SASL_SERVER_MECHANISM_DESTROY destroy; + SASL_SERVER_MECHANISM_HANDLE_INITIAL_RESPONSE handle_initial_response; + SASL_SERVER_MECHANISM_HANDLE_RESPONSE handle_response; + SASL_SERVER_MECHANISM_GET_MECHANISM_NAME get_mechanism_name; + } SASL_SERVER_MECHANISM_INTERFACE_DESCRIPTION; + + MOCKABLE_FUNCTION(, SASL_SERVER_MECHANISM_HANDLE, sasl_server_mechanism_create, const SASL_SERVER_MECHANISM_INTERFACE_DESCRIPTION*, sasl_server_mechanism_interface_description, void*, sasl_server_mechanism_create_parameters); + MOCKABLE_FUNCTION(, void, sasl_server_mechanism_destroy, SASL_SERVER_MECHANISM_HANDLE, sasl_server_mechanism); + MOCKABLE_FUNCTION(, int, sasl_server_mechanism_handle_initial_response, SASL_SERVER_MECHANISM_HANDLE, sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES*, initial_response_bytes, const char*, hostname, bool*, send_challenge, SASL_SERVER_MECHANISM_BYTES*, challenge_bytes); + MOCKABLE_FUNCTION(, int, sasl_server_mechanism_handle_response, SASL_SERVER_MECHANISM_HANDLE, sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES*, response_bytes, bool*, send_next_challenge, SASL_SERVER_MECHANISM_BYTES*, next_challenge_bytes); + MOCKABLE_FUNCTION(, const char*, sasl_server_mechanism_get_mechanism_name, SASL_SERVER_MECHANISM_HANDLE, sasl_server_mechanism); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASL_SERVER_MECHANISM_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/saslclientio.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SASLCLIENTIO_H +#define SASLCLIENTIO_H + +#ifdef __cplusplus +extern "C" { +#include <cstddef> +#else +#include <stddef.h> +#endif /* __cplusplus */ + +#include <stdbool.h> + +#include "azure_c_shared_utility/xio.h" +#include "azure_uamqp_c/sasl_mechanism.h" + +#include "azure_c_shared_utility/umock_c_prod.h" + +typedef struct SASLCLIENTIO_CONFIG_TAG +{ + XIO_HANDLE underlying_io; + SASL_MECHANISM_HANDLE sasl_mechanism; +} SASLCLIENTIO_CONFIG; + +MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, saslclientio_get_interface_description); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SASLCLIENTIO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/server_protocol_io.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SERVER_PROTOCOL_IO_H +#define SERVER_PROTOCOL_IO_H + +#ifdef __cplusplus +extern "C" { +#include "cstdint" +#else +#include "stdint.h" +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/xio.h" + +typedef struct SERVER_PROTOCOL_IO_CONFIG_TAG +{ + XIO_HANDLE underlying_io; + ON_BYTES_RECEIVED* on_bytes_received; + void** on_bytes_received_context; +} SERVER_PROTOCOL_IO_CONFIG; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SERVER_PROTOCOL_IO_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/session.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SESSION_H +#define SESSION_H + +#include <stdint.h> +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/connection.h" + +#include "azure_uamqp_c/amqp_definitions_role.h" +#include "azure_uamqp_c/amqp_definitions_handle.h" +#include "azure_uamqp_c/amqp_definitions_fields.h" +#include "azure_uamqp_c/amqp_definitions_sender_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_receiver_settle_mode.h" +#include "azure_uamqp_c/amqp_definitions_sequence_no.h" +#include "azure_uamqp_c/amqp_definitions_delivery_number.h" +#include "azure_uamqp_c/amqp_definitions_disposition.h" +#include "azure_uamqp_c/amqp_definitions_transfer_number.h" +#include "azure_uamqp_c/amqp_definitions_flow.h" +#include "azure_uamqp_c/amqp_definitions_attach.h" +#include "azure_uamqp_c/amqp_definitions_error.h" +#include "azure_uamqp_c/amqp_definitions_detach.h" +#include "azure_uamqp_c/amqp_definitions_delivery_tag.h" +#include "azure_uamqp_c/amqp_definitions_message_format.h" +#include "azure_uamqp_c/amqp_definitions_transfer.h" + + +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_c_shared_utility/macro_utils.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct SESSION_INSTANCE_TAG* SESSION_HANDLE; +typedef struct LINK_ENDPOINT_INSTANCE_TAG* LINK_ENDPOINT_HANDLE; + +#define SESSION_STATE_VALUES \ + SESSION_STATE_UNMAPPED, \ + SESSION_STATE_BEGIN_SENT, \ + SESSION_STATE_BEGIN_RCVD, \ + SESSION_STATE_MAPPED, \ + SESSION_STATE_END_SENT, \ + SESSION_STATE_END_RCVD, \ + SESSION_STATE_DISCARDING, \ + SESSION_STATE_ERROR + +DEFINE_ENUM(SESSION_STATE, SESSION_STATE_VALUES) + +#define SESSION_SEND_TRANSFER_RESULT_VALUES \ + SESSION_SEND_TRANSFER_OK, \ + SESSION_SEND_TRANSFER_ERROR, \ + SESSION_SEND_TRANSFER_BUSY + +DEFINE_ENUM(SESSION_SEND_TRANSFER_RESULT, SESSION_SEND_TRANSFER_RESULT_VALUES) + + typedef void(*LINK_ENDPOINT_FRAME_RECEIVED_CALLBACK)(void* context, AMQP_VALUE performative, uint32_t frame_payload_size, const unsigned char* payload_bytes); + typedef void(*ON_SESSION_STATE_CHANGED)(void* context, SESSION_STATE new_session_state, SESSION_STATE previous_session_state); + typedef void(*ON_SESSION_FLOW_ON)(void* context); + typedef bool(*ON_LINK_ATTACHED)(void* context, LINK_ENDPOINT_HANDLE new_link_endpoint, const char* name, role role, AMQP_VALUE source, AMQP_VALUE target); + + MOCKABLE_FUNCTION(, SESSION_HANDLE, session_create, CONNECTION_HANDLE, connection, ON_LINK_ATTACHED, on_link_attached, void*, callback_context); + MOCKABLE_FUNCTION(, SESSION_HANDLE, session_create_from_endpoint, CONNECTION_HANDLE, connection, ENDPOINT_HANDLE, connection_endpoint, ON_LINK_ATTACHED, on_link_attached, void*, callback_context); + MOCKABLE_FUNCTION(, int, session_set_incoming_window, SESSION_HANDLE, session, uint32_t, incoming_window); + MOCKABLE_FUNCTION(, int, session_get_incoming_window, SESSION_HANDLE, session, uint32_t*, incoming_window); + MOCKABLE_FUNCTION(, int, session_set_outgoing_window, SESSION_HANDLE, session, uint32_t, outgoing_window); + MOCKABLE_FUNCTION(, int, session_get_outgoing_window, SESSION_HANDLE, session, uint32_t*, outgoing_window); + MOCKABLE_FUNCTION(, int, session_set_handle_max, SESSION_HANDLE, session, handle, handle_max); + MOCKABLE_FUNCTION(, int, session_get_handle_max, SESSION_HANDLE, session, handle*, handle_max); + MOCKABLE_FUNCTION(, void, session_destroy, SESSION_HANDLE, session); + MOCKABLE_FUNCTION(, int, session_begin, SESSION_HANDLE, session); + MOCKABLE_FUNCTION(, int, session_end, SESSION_HANDLE, session, const char*, condition_value, const char*, description); + MOCKABLE_FUNCTION(, LINK_ENDPOINT_HANDLE, session_create_link_endpoint, SESSION_HANDLE, session, const char*, name); + MOCKABLE_FUNCTION(, void, session_destroy_link_endpoint, LINK_ENDPOINT_HANDLE, link_endpoint); + MOCKABLE_FUNCTION(, int, session_start_link_endpoint, LINK_ENDPOINT_HANDLE, link_endpoint, ON_ENDPOINT_FRAME_RECEIVED, frame_received_callback, ON_SESSION_STATE_CHANGED, on_session_state_changed, ON_SESSION_FLOW_ON, on_session_flow_on, void*, context); + MOCKABLE_FUNCTION(, int, session_send_flow, LINK_ENDPOINT_HANDLE, link_endpoint, FLOW_HANDLE, flow); + MOCKABLE_FUNCTION(, int, session_send_attach, LINK_ENDPOINT_HANDLE, link_endpoint, ATTACH_HANDLE, attach); + MOCKABLE_FUNCTION(, int, session_send_disposition, LINK_ENDPOINT_HANDLE, link_endpoint, DISPOSITION_HANDLE, disposition); + MOCKABLE_FUNCTION(, int, session_send_detach, LINK_ENDPOINT_HANDLE, link_endpoint, DETACH_HANDLE, detach); + MOCKABLE_FUNCTION(, SESSION_SEND_TRANSFER_RESULT, session_send_transfer, LINK_ENDPOINT_HANDLE, link_endpoint, TRANSFER_HANDLE, transfer, PAYLOAD*, payloads, size_t, payload_count, delivery_number*, delivery_id, ON_SEND_COMPLETE, on_send_complete, void*, callback_context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SESSION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/socket_listener.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SOCKETLISTENER_H +#define SOCKETLISTENER_H + +#include "azure_c_shared_utility/xio.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "azure_c_shared_utility/umock_c_prod.h" + + typedef struct SOCKET_LISTENER_INSTANCE_TAG* SOCKET_LISTENER_HANDLE; + typedef void(*ON_SOCKET_ACCEPTED)(void* context, const IO_INTERFACE_DESCRIPTION* interface_description, void* io_parameters); + + MOCKABLE_FUNCTION(, SOCKET_LISTENER_HANDLE, socketlistener_create, int, port); + MOCKABLE_FUNCTION(, void, socketlistener_destroy, SOCKET_LISTENER_HANDLE, socket_listener); + MOCKABLE_FUNCTION(, int, socketlistener_start, SOCKET_LISTENER_HANDLE, socket_listener, ON_SOCKET_ACCEPTED, on_socket_accepted, void*, callback_context); + MOCKABLE_FUNCTION(, int, socketlistener_stop, SOCKET_LISTENER_HANDLE, socket_listener); + MOCKABLE_FUNCTION(, void, socketlistener_dowork, SOCKET_LISTENER_HANDLE, socket_listener); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SOCKETLISTENER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/inc/azure_uamqp_c/uamqp.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef UAMQP_H +#define UAMQP_H + +/* This header includes all the uAMQP headers +Unless advanced cherrypicking of which functionality is to be used, +a user can always inlcude this header */ + +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/amqp_management.h" +#include "azure_uamqp_c/amqp_types.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqpvalue_to_string.h" +#include "azure_uamqp_c/cbs.h" +#include "azure_uamqp_c/connection.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/header_detect_io.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/sasl_anonymous.h" +#include "azure_uamqp_c/sasl_frame_codec.h" +#include "azure_uamqp_c/sasl_mechanism.h" +#include "azure_uamqp_c/sasl_server_mechanism.h" +#include "azure_uamqp_c/sasl_mssbcbs.h" +#include "azure_uamqp_c/sasl_plain.h" +#include "azure_uamqp_c/saslclientio.h" +#include "azure_uamqp_c/sasl_server_io.h" +#include "azure_uamqp_c/server_protocol_io.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/socket_listener.h" + +#endif /* UAMQP_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/amqp_definitions.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,18475 @@ + + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include <stdlib.h> +#include <stdbool.h> + +/* role */ + +AMQP_VALUE amqpvalue_create_role(role value) +{ + return amqpvalue_create_boolean(value); +} + +/* sender-settle-mode */ + +AMQP_VALUE amqpvalue_create_sender_settle_mode(sender_settle_mode value) +{ + return amqpvalue_create_ubyte(value); +} + +/* receiver-settle-mode */ + +AMQP_VALUE amqpvalue_create_receiver_settle_mode(receiver_settle_mode value) +{ + return amqpvalue_create_ubyte(value); +} + +/* handle */ + +AMQP_VALUE amqpvalue_create_handle(handle value) +{ + return amqpvalue_create_uint(value); +} + +/* seconds */ + +AMQP_VALUE amqpvalue_create_seconds(seconds value) +{ + return amqpvalue_create_uint(value); +} + +/* milliseconds */ + +AMQP_VALUE amqpvalue_create_milliseconds(milliseconds value) +{ + return amqpvalue_create_uint(value); +} + +/* delivery-tag */ + +AMQP_VALUE amqpvalue_create_delivery_tag(delivery_tag value) +{ + return amqpvalue_create_binary(value); +} + +/* sequence-no */ + +AMQP_VALUE amqpvalue_create_sequence_no(sequence_no value) +{ + return amqpvalue_create_uint(value); +} + +/* delivery-number */ + +AMQP_VALUE amqpvalue_create_delivery_number(delivery_number value) +{ + return amqpvalue_create_sequence_no(value); +} + +/* transfer-number */ + +AMQP_VALUE amqpvalue_create_transfer_number(transfer_number value) +{ + return amqpvalue_create_sequence_no(value); +} + +/* message-format */ + +AMQP_VALUE amqpvalue_create_message_format(message_format value) +{ + return amqpvalue_create_uint(value); +} + +/* ietf-language-tag */ + +AMQP_VALUE amqpvalue_create_ietf_language_tag(ietf_language_tag value) +{ + return amqpvalue_create_symbol(value); +} + +/* fields */ + +AMQP_VALUE amqpvalue_create_fields(AMQP_VALUE value) +{ + return amqpvalue_clone(value); +} + +/* error */ + +typedef struct ERROR_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} ERROR_INSTANCE; + +static ERROR_HANDLE error_create_internal(void) +{ + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)malloc(sizeof(ERROR_INSTANCE)); + if (error_instance != NULL) + { + error_instance->composite_value = NULL; + } + + return error_instance; +} + +ERROR_HANDLE error_create(const char* condition_value) +{ + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)malloc(sizeof(ERROR_INSTANCE)); + if (error_instance != NULL) + { + error_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(29); + if (error_instance->composite_value == NULL) + { + free(error_instance); + error_instance = NULL; + } + else + { + AMQP_VALUE condition_amqp_value; + int result = 0; + + condition_amqp_value = amqpvalue_create_symbol(condition_value); + if ((result == 0) && (amqpvalue_set_composite_item(error_instance->composite_value, 0, condition_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(condition_amqp_value); + } + } + + return error_instance; +} + +ERROR_HANDLE error_clone(ERROR_HANDLE value) +{ + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)malloc(sizeof(ERROR_INSTANCE)); + if (error_instance != NULL) + { + error_instance->composite_value = amqpvalue_clone(((ERROR_INSTANCE*)value)->composite_value); + if (error_instance->composite_value == NULL) + { + free(error_instance); + error_instance = NULL; + } + } + + return error_instance; +} + +void error_destroy(ERROR_HANDLE error) +{ + if (error != NULL) + { + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + amqpvalue_destroy(error_instance->composite_value); + free(error_instance); + } +} + +AMQP_VALUE amqpvalue_create_error(ERROR_HANDLE error) +{ + AMQP_VALUE result; + + if (error == NULL) + { + result = NULL; + } + else + { + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + result = amqpvalue_clone(error_instance->composite_value); + } + + return result; +} + +bool is_error_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 29)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_error(AMQP_VALUE value, ERROR_HANDLE* error_handle) +{ + int result; + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error_create_internal(); + *error_handle = error_instance; + if (*error_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + error_destroy(*error_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* condition */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + error_destroy(*error_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + error_destroy(*error_handle); + result = __FAILURE__; + break; + } + else + { + const char* condition; + if (amqpvalue_get_symbol(item_value, &condition) != 0) + { + amqpvalue_destroy(item_value); + error_destroy(*error_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* description */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* description; + if (amqpvalue_get_string(item_value, &description) != 0) + { + amqpvalue_destroy(item_value); + error_destroy(*error_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* info */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields info; + if (amqpvalue_get_fields(item_value, &info) != 0) + { + amqpvalue_destroy(item_value); + error_destroy(*error_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + error_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int error_get_condition(ERROR_HANDLE error, const char** condition_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + if (amqpvalue_get_composite_item_count(error_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(error_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_symbol(item_value, condition_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int error_set_condition(ERROR_HANDLE error, const char* condition_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + AMQP_VALUE condition_amqp_value = amqpvalue_create_symbol(condition_value); + if (condition_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(error_instance->composite_value, 0, condition_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(condition_amqp_value); + } + } + + return result; +} + +int error_get_description(ERROR_HANDLE error, const char** description_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + if (amqpvalue_get_composite_item_count(error_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(error_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, description_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int error_set_description(ERROR_HANDLE error, const char* description_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + AMQP_VALUE description_amqp_value = amqpvalue_create_string(description_value); + if (description_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(error_instance->composite_value, 1, description_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(description_amqp_value); + } + } + + return result; +} + +int error_get_info(ERROR_HANDLE error, fields* info_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + if (amqpvalue_get_composite_item_count(error_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(error_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, info_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int error_set_info(ERROR_HANDLE error, fields info_value) +{ + int result; + + if (error == NULL) + { + result = __FAILURE__; + } + else + { + ERROR_INSTANCE* error_instance = (ERROR_INSTANCE*)error; + AMQP_VALUE info_amqp_value = amqpvalue_create_fields(info_value); + if (info_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(error_instance->composite_value, 2, info_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(info_amqp_value); + } + } + + return result; +} + + +/* amqp-error */ + +AMQP_VALUE amqpvalue_create_amqp_error(amqp_error value) +{ + return amqpvalue_create_symbol(value); +} + +/* connection-error */ + +AMQP_VALUE amqpvalue_create_connection_error(connection_error value) +{ + return amqpvalue_create_symbol(value); +} + +/* session-error */ + +AMQP_VALUE amqpvalue_create_session_error(session_error value) +{ + return amqpvalue_create_symbol(value); +} + +/* link-error */ + +AMQP_VALUE amqpvalue_create_link_error(link_error value) +{ + return amqpvalue_create_symbol(value); +} + +/* open */ + +typedef struct OPEN_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} OPEN_INSTANCE; + +static OPEN_HANDLE open_create_internal(void) +{ + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)malloc(sizeof(OPEN_INSTANCE)); + if (open_instance != NULL) + { + open_instance->composite_value = NULL; + } + + return open_instance; +} + +OPEN_HANDLE open_create(const char* container_id_value) +{ + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)malloc(sizeof(OPEN_INSTANCE)); + if (open_instance != NULL) + { + open_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(16); + if (open_instance->composite_value == NULL) + { + free(open_instance); + open_instance = NULL; + } + else + { + AMQP_VALUE container_id_amqp_value; + int result = 0; + + container_id_amqp_value = amqpvalue_create_string(container_id_value); + if ((result == 0) && (amqpvalue_set_composite_item(open_instance->composite_value, 0, container_id_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(container_id_amqp_value); + } + } + + return open_instance; +} + +OPEN_HANDLE open_clone(OPEN_HANDLE value) +{ + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)malloc(sizeof(OPEN_INSTANCE)); + if (open_instance != NULL) + { + open_instance->composite_value = amqpvalue_clone(((OPEN_INSTANCE*)value)->composite_value); + if (open_instance->composite_value == NULL) + { + free(open_instance); + open_instance = NULL; + } + } + + return open_instance; +} + +void open_destroy(OPEN_HANDLE open) +{ + if (open != NULL) + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + amqpvalue_destroy(open_instance->composite_value); + free(open_instance); + } +} + +AMQP_VALUE amqpvalue_create_open(OPEN_HANDLE open) +{ + AMQP_VALUE result; + + if (open == NULL) + { + result = NULL; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + result = amqpvalue_clone(open_instance->composite_value); + } + + return result; +} + +bool is_open_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 16)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_open(AMQP_VALUE value, OPEN_HANDLE* open_handle) +{ + int result; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open_create_internal(); + *open_handle = open_instance; + if (*open_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + open_destroy(*open_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* container-id */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + else + { + const char* container_id; + if (amqpvalue_get_string(item_value, &container_id) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* hostname */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* hostname; + if (amqpvalue_get_string(item_value, &hostname) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* max-frame-size */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint32_t max_frame_size; + if (amqpvalue_get_uint(item_value, &max_frame_size) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* channel-max */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint16_t channel_max; + if (amqpvalue_get_ushort(item_value, &channel_max) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* idle-time-out */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + milliseconds idle_time_out; + if (amqpvalue_get_milliseconds(item_value, &idle_time_out) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* outgoing-locales */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ietf_language_tag outgoing_locales = NULL; + AMQP_VALUE outgoing_locales_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &outgoing_locales_array) != 0)) && + (amqpvalue_get_ietf_language_tag(item_value, &outgoing_locales) != 0)) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* incoming-locales */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ietf_language_tag incoming_locales = NULL; + AMQP_VALUE incoming_locales_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &incoming_locales_array) != 0)) && + (amqpvalue_get_ietf_language_tag(item_value, &incoming_locales) != 0)) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* offered-capabilities */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* offered_capabilities = NULL; + AMQP_VALUE offered_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &offered_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &offered_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* desired-capabilities */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* desired_capabilities = NULL; + AMQP_VALUE desired_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &desired_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &desired_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* properties */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields properties; + if (amqpvalue_get_fields(item_value, &properties) != 0) + { + amqpvalue_destroy(item_value); + open_destroy(*open_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + open_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int open_get_container_id(OPEN_HANDLE open, const char** container_id_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, container_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_container_id(OPEN_HANDLE open, const char* container_id_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE container_id_amqp_value = amqpvalue_create_string(container_id_value); + if (container_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 0, container_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(container_id_amqp_value); + } + } + + return result; +} + +int open_get_hostname(OPEN_HANDLE open, const char** hostname_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, hostname_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_hostname(OPEN_HANDLE open, const char* hostname_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE hostname_amqp_value = amqpvalue_create_string(hostname_value); + if (hostname_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 1, hostname_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(hostname_amqp_value); + } + } + + return result; +} + +int open_get_max_frame_size(OPEN_HANDLE open, uint32_t* max_frame_size_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + *max_frame_size_value = 4294967295u; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *max_frame_size_value = 4294967295u; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, max_frame_size_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *max_frame_size_value = 4294967295u; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_max_frame_size(OPEN_HANDLE open, uint32_t max_frame_size_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE max_frame_size_amqp_value = amqpvalue_create_uint(max_frame_size_value); + if (max_frame_size_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 2, max_frame_size_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(max_frame_size_amqp_value); + } + } + + return result; +} + +int open_get_channel_max(OPEN_HANDLE open, uint16_t* channel_max_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *channel_max_value = 65535; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *channel_max_value = 65535; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_ushort(item_value, channel_max_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *channel_max_value = 65535; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_channel_max(OPEN_HANDLE open, uint16_t channel_max_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE channel_max_amqp_value = amqpvalue_create_ushort(channel_max_value); + if (channel_max_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 3, channel_max_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(channel_max_amqp_value); + } + } + + return result; +} + +int open_get_idle_time_out(OPEN_HANDLE open, milliseconds* idle_time_out_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_milliseconds(item_value, idle_time_out_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_idle_time_out(OPEN_HANDLE open, milliseconds idle_time_out_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE idle_time_out_amqp_value = amqpvalue_create_milliseconds(idle_time_out_value); + if (idle_time_out_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 4, idle_time_out_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(idle_time_out_amqp_value); + } + } + + return result; +} + +int open_get_outgoing_locales(OPEN_HANDLE open, AMQP_VALUE* outgoing_locales_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + ietf_language_tag outgoing_locales_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_ietf_language_tag(item_value, &outgoing_locales_single_value); + } + else + { + (void)memset((void*)&outgoing_locales_single_value, 0, sizeof(outgoing_locales_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, outgoing_locales_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *outgoing_locales_value = amqpvalue_create_array(); + if (*outgoing_locales_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_ietf_language_tag(outgoing_locales_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*outgoing_locales_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*outgoing_locales_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*outgoing_locales_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 5, *outgoing_locales_value) != 0) + { + amqpvalue_destroy(*outgoing_locales_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*outgoing_locales_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int open_set_outgoing_locales(OPEN_HANDLE open, AMQP_VALUE outgoing_locales_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE outgoing_locales_amqp_value; + if (outgoing_locales_value == NULL) + { + outgoing_locales_amqp_value = NULL; + } + else + { + outgoing_locales_amqp_value = amqpvalue_clone(outgoing_locales_value); + } + if (outgoing_locales_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 5, outgoing_locales_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(outgoing_locales_amqp_value); + } + } + + return result; +} + +int open_get_incoming_locales(OPEN_HANDLE open, AMQP_VALUE* incoming_locales_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + ietf_language_tag incoming_locales_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_ietf_language_tag(item_value, &incoming_locales_single_value); + } + else + { + (void)memset((void*)&incoming_locales_single_value, 0, sizeof(incoming_locales_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, incoming_locales_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *incoming_locales_value = amqpvalue_create_array(); + if (*incoming_locales_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_ietf_language_tag(incoming_locales_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*incoming_locales_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*incoming_locales_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*incoming_locales_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 6, *incoming_locales_value) != 0) + { + amqpvalue_destroy(*incoming_locales_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*incoming_locales_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int open_set_incoming_locales(OPEN_HANDLE open, AMQP_VALUE incoming_locales_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE incoming_locales_amqp_value; + if (incoming_locales_value == NULL) + { + incoming_locales_amqp_value = NULL; + } + else + { + incoming_locales_amqp_value = amqpvalue_clone(incoming_locales_value); + } + if (incoming_locales_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 6, incoming_locales_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(incoming_locales_amqp_value); + } + } + + return result; +} + +int open_get_offered_capabilities(OPEN_HANDLE open, AMQP_VALUE* offered_capabilities_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* offered_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &offered_capabilities_single_value); + } + else + { + (void)memset((void*)&offered_capabilities_single_value, 0, sizeof(offered_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, offered_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *offered_capabilities_value = amqpvalue_create_array(); + if (*offered_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(offered_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*offered_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 7, *offered_capabilities_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*offered_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int open_set_offered_capabilities(OPEN_HANDLE open, AMQP_VALUE offered_capabilities_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE offered_capabilities_amqp_value; + if (offered_capabilities_value == NULL) + { + offered_capabilities_amqp_value = NULL; + } + else + { + offered_capabilities_amqp_value = amqpvalue_clone(offered_capabilities_value); + } + if (offered_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 7, offered_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(offered_capabilities_amqp_value); + } + } + + return result; +} + +int open_get_desired_capabilities(OPEN_HANDLE open, AMQP_VALUE* desired_capabilities_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* desired_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &desired_capabilities_single_value); + } + else + { + (void)memset((void*)&desired_capabilities_single_value, 0, sizeof(desired_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, desired_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *desired_capabilities_value = amqpvalue_create_array(); + if (*desired_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(desired_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*desired_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 8, *desired_capabilities_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*desired_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int open_set_desired_capabilities(OPEN_HANDLE open, AMQP_VALUE desired_capabilities_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE desired_capabilities_amqp_value; + if (desired_capabilities_value == NULL) + { + desired_capabilities_amqp_value = NULL; + } + else + { + desired_capabilities_amqp_value = amqpvalue_clone(desired_capabilities_value); + } + if (desired_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 8, desired_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(desired_capabilities_amqp_value); + } + } + + return result; +} + +int open_get_properties(OPEN_HANDLE open, fields* properties_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + if (amqpvalue_get_composite_item_count(open_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(open_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int open_set_properties(OPEN_HANDLE open, fields properties_value) +{ + int result; + + if (open == NULL) + { + result = __FAILURE__; + } + else + { + OPEN_INSTANCE* open_instance = (OPEN_INSTANCE*)open; + AMQP_VALUE properties_amqp_value = amqpvalue_create_fields(properties_value); + if (properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(open_instance->composite_value, 9, properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(properties_amqp_value); + } + } + + return result; +} + + +/* begin */ + +typedef struct BEGIN_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} BEGIN_INSTANCE; + +static BEGIN_HANDLE begin_create_internal(void) +{ + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)malloc(sizeof(BEGIN_INSTANCE)); + if (begin_instance != NULL) + { + begin_instance->composite_value = NULL; + } + + return begin_instance; +} + +BEGIN_HANDLE begin_create(transfer_number next_outgoing_id_value, uint32_t incoming_window_value, uint32_t outgoing_window_value) +{ + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)malloc(sizeof(BEGIN_INSTANCE)); + if (begin_instance != NULL) + { + begin_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(17); + if (begin_instance->composite_value == NULL) + { + free(begin_instance); + begin_instance = NULL; + } + else + { + AMQP_VALUE next_outgoing_id_amqp_value; + AMQP_VALUE incoming_window_amqp_value; + AMQP_VALUE outgoing_window_amqp_value; + int result = 0; + + next_outgoing_id_amqp_value = amqpvalue_create_transfer_number(next_outgoing_id_value); + if ((result == 0) && (amqpvalue_set_composite_item(begin_instance->composite_value, 1, next_outgoing_id_amqp_value) != 0)) + { + result = __FAILURE__; + } + incoming_window_amqp_value = amqpvalue_create_uint(incoming_window_value); + if ((result == 0) && (amqpvalue_set_composite_item(begin_instance->composite_value, 2, incoming_window_amqp_value) != 0)) + { + result = __FAILURE__; + } + outgoing_window_amqp_value = amqpvalue_create_uint(outgoing_window_value); + if ((result == 0) && (amqpvalue_set_composite_item(begin_instance->composite_value, 3, outgoing_window_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(next_outgoing_id_amqp_value); + amqpvalue_destroy(incoming_window_amqp_value); + amqpvalue_destroy(outgoing_window_amqp_value); + } + } + + return begin_instance; +} + +BEGIN_HANDLE begin_clone(BEGIN_HANDLE value) +{ + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)malloc(sizeof(BEGIN_INSTANCE)); + if (begin_instance != NULL) + { + begin_instance->composite_value = amqpvalue_clone(((BEGIN_INSTANCE*)value)->composite_value); + if (begin_instance->composite_value == NULL) + { + free(begin_instance); + begin_instance = NULL; + } + } + + return begin_instance; +} + +void begin_destroy(BEGIN_HANDLE begin) +{ + if (begin != NULL) + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + amqpvalue_destroy(begin_instance->composite_value); + free(begin_instance); + } +} + +AMQP_VALUE amqpvalue_create_begin(BEGIN_HANDLE begin) +{ + AMQP_VALUE result; + + if (begin == NULL) + { + result = NULL; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + result = amqpvalue_clone(begin_instance->composite_value); + } + + return result; +} + +bool is_begin_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 17)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_begin(AMQP_VALUE value, BEGIN_HANDLE* begin_handle) +{ + int result; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin_create_internal(); + *begin_handle = begin_instance; + if (*begin_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + begin_destroy(*begin_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* remote-channel */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint16_t remote_channel; + if (amqpvalue_get_ushort(item_value, &remote_channel) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* next-outgoing-id */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + { + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + else + { + transfer_number next_outgoing_id; + if (amqpvalue_get_transfer_number(item_value, &next_outgoing_id) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* incoming-window */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + { + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + else + { + uint32_t incoming_window; + if (amqpvalue_get_uint(item_value, &incoming_window) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* outgoing-window */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + { + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + else + { + uint32_t outgoing_window; + if (amqpvalue_get_uint(item_value, &outgoing_window) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* handle-max */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + handle handle_max; + if (amqpvalue_get_handle(item_value, &handle_max) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* offered-capabilities */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* offered_capabilities = NULL; + AMQP_VALUE offered_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &offered_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &offered_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* desired-capabilities */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* desired_capabilities = NULL; + AMQP_VALUE desired_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &desired_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &desired_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* properties */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields properties; + if (amqpvalue_get_fields(item_value, &properties) != 0) + { + amqpvalue_destroy(item_value); + begin_destroy(*begin_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + begin_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int begin_get_remote_channel(BEGIN_HANDLE begin, uint16_t* remote_channel_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_ushort(item_value, remote_channel_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_remote_channel(BEGIN_HANDLE begin, uint16_t remote_channel_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE remote_channel_amqp_value = amqpvalue_create_ushort(remote_channel_value); + if (remote_channel_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 0, remote_channel_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(remote_channel_amqp_value); + } + } + + return result; +} + +int begin_get_next_outgoing_id(BEGIN_HANDLE begin, transfer_number* next_outgoing_id_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_transfer_number(item_value, next_outgoing_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_next_outgoing_id(BEGIN_HANDLE begin, transfer_number next_outgoing_id_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE next_outgoing_id_amqp_value = amqpvalue_create_transfer_number(next_outgoing_id_value); + if (next_outgoing_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 1, next_outgoing_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(next_outgoing_id_amqp_value); + } + } + + return result; +} + +int begin_get_incoming_window(BEGIN_HANDLE begin, uint32_t* incoming_window_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, incoming_window_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_incoming_window(BEGIN_HANDLE begin, uint32_t incoming_window_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE incoming_window_amqp_value = amqpvalue_create_uint(incoming_window_value); + if (incoming_window_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 2, incoming_window_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(incoming_window_amqp_value); + } + } + + return result; +} + +int begin_get_outgoing_window(BEGIN_HANDLE begin, uint32_t* outgoing_window_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, outgoing_window_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_outgoing_window(BEGIN_HANDLE begin, uint32_t outgoing_window_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE outgoing_window_amqp_value = amqpvalue_create_uint(outgoing_window_value); + if (outgoing_window_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 3, outgoing_window_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(outgoing_window_amqp_value); + } + } + + return result; +} + +int begin_get_handle_max(BEGIN_HANDLE begin, handle* handle_max_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + *handle_max_value = 4294967295u; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *handle_max_value = 4294967295u; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_handle(item_value, handle_max_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *handle_max_value = 4294967295u; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_handle_max(BEGIN_HANDLE begin, handle handle_max_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE handle_max_amqp_value = amqpvalue_create_handle(handle_max_value); + if (handle_max_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 4, handle_max_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(handle_max_amqp_value); + } + } + + return result; +} + +int begin_get_offered_capabilities(BEGIN_HANDLE begin, AMQP_VALUE* offered_capabilities_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* offered_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &offered_capabilities_single_value); + } + else + { + (void)memset((void*)&offered_capabilities_single_value, 0, sizeof(offered_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, offered_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *offered_capabilities_value = amqpvalue_create_array(); + if (*offered_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(offered_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*offered_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 5, *offered_capabilities_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*offered_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int begin_set_offered_capabilities(BEGIN_HANDLE begin, AMQP_VALUE offered_capabilities_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE offered_capabilities_amqp_value; + if (offered_capabilities_value == NULL) + { + offered_capabilities_amqp_value = NULL; + } + else + { + offered_capabilities_amqp_value = amqpvalue_clone(offered_capabilities_value); + } + if (offered_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 5, offered_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(offered_capabilities_amqp_value); + } + } + + return result; +} + +int begin_get_desired_capabilities(BEGIN_HANDLE begin, AMQP_VALUE* desired_capabilities_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* desired_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &desired_capabilities_single_value); + } + else + { + (void)memset((void*)&desired_capabilities_single_value, 0, sizeof(desired_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, desired_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *desired_capabilities_value = amqpvalue_create_array(); + if (*desired_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(desired_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*desired_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 6, *desired_capabilities_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*desired_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int begin_set_desired_capabilities(BEGIN_HANDLE begin, AMQP_VALUE desired_capabilities_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE desired_capabilities_amqp_value; + if (desired_capabilities_value == NULL) + { + desired_capabilities_amqp_value = NULL; + } + else + { + desired_capabilities_amqp_value = amqpvalue_clone(desired_capabilities_value); + } + if (desired_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 6, desired_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(desired_capabilities_amqp_value); + } + } + + return result; +} + +int begin_get_properties(BEGIN_HANDLE begin, fields* properties_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + if (amqpvalue_get_composite_item_count(begin_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(begin_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int begin_set_properties(BEGIN_HANDLE begin, fields properties_value) +{ + int result; + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + BEGIN_INSTANCE* begin_instance = (BEGIN_INSTANCE*)begin; + AMQP_VALUE properties_amqp_value = amqpvalue_create_fields(properties_value); + if (properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(begin_instance->composite_value, 7, properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(properties_amqp_value); + } + } + + return result; +} + + +/* attach */ + +typedef struct ATTACH_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} ATTACH_INSTANCE; + +static ATTACH_HANDLE attach_create_internal(void) +{ + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)malloc(sizeof(ATTACH_INSTANCE)); + if (attach_instance != NULL) + { + attach_instance->composite_value = NULL; + } + + return attach_instance; +} + +ATTACH_HANDLE attach_create(const char* name_value, handle handle_value, role role_value) +{ + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)malloc(sizeof(ATTACH_INSTANCE)); + if (attach_instance != NULL) + { + attach_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(18); + if (attach_instance->composite_value == NULL) + { + free(attach_instance); + attach_instance = NULL; + } + else + { + AMQP_VALUE name_amqp_value; + AMQP_VALUE handle_amqp_value; + AMQP_VALUE role_amqp_value; + int result = 0; + + name_amqp_value = amqpvalue_create_string(name_value); + if ((result == 0) && (amqpvalue_set_composite_item(attach_instance->composite_value, 0, name_amqp_value) != 0)) + { + result = __FAILURE__; + } + handle_amqp_value = amqpvalue_create_handle(handle_value); + if ((result == 0) && (amqpvalue_set_composite_item(attach_instance->composite_value, 1, handle_amqp_value) != 0)) + { + result = __FAILURE__; + } + role_amqp_value = amqpvalue_create_role(role_value); + if ((result == 0) && (amqpvalue_set_composite_item(attach_instance->composite_value, 2, role_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(name_amqp_value); + amqpvalue_destroy(handle_amqp_value); + amqpvalue_destroy(role_amqp_value); + } + } + + return attach_instance; +} + +ATTACH_HANDLE attach_clone(ATTACH_HANDLE value) +{ + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)malloc(sizeof(ATTACH_INSTANCE)); + if (attach_instance != NULL) + { + attach_instance->composite_value = amqpvalue_clone(((ATTACH_INSTANCE*)value)->composite_value); + if (attach_instance->composite_value == NULL) + { + free(attach_instance); + attach_instance = NULL; + } + } + + return attach_instance; +} + +void attach_destroy(ATTACH_HANDLE attach) +{ + if (attach != NULL) + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + amqpvalue_destroy(attach_instance->composite_value); + free(attach_instance); + } +} + +AMQP_VALUE amqpvalue_create_attach(ATTACH_HANDLE attach) +{ + AMQP_VALUE result; + + if (attach == NULL) + { + result = NULL; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + result = amqpvalue_clone(attach_instance->composite_value); + } + + return result; +} + +bool is_attach_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 18)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_attach(AMQP_VALUE value, ATTACH_HANDLE* attach_handle) +{ + int result; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach_create_internal(); + *attach_handle = attach_instance; + if (*attach_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + attach_destroy(*attach_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* name */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + else + { + const char* name; + if (amqpvalue_get_string(item_value, &name) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* handle */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + { + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + else + { + handle handle; + if (amqpvalue_get_handle(item_value, &handle) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* role */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + { + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + else + { + role role; + if (amqpvalue_get_role(item_value, &role) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* snd-settle-mode */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + sender_settle_mode snd_settle_mode; + if (amqpvalue_get_sender_settle_mode(item_value, &snd_settle_mode) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* rcv-settle-mode */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + receiver_settle_mode rcv_settle_mode; + if (amqpvalue_get_receiver_settle_mode(item_value, &rcv_settle_mode) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* source */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* target */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* unsettled */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + AMQP_VALUE unsettled; + if (amqpvalue_get_map(item_value, &unsettled) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* incomplete-unsettled */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool incomplete_unsettled; + if (amqpvalue_get_boolean(item_value, &incomplete_unsettled) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* initial-delivery-count */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + sequence_no initial_delivery_count; + if (amqpvalue_get_sequence_no(item_value, &initial_delivery_count) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* max-message-size */ + if (list_item_count > 10) + { + item_value = amqpvalue_get_list_item(list_value, 10); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint64_t max_message_size; + if (amqpvalue_get_ulong(item_value, &max_message_size) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* offered-capabilities */ + if (list_item_count > 11) + { + item_value = amqpvalue_get_list_item(list_value, 11); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* offered_capabilities = NULL; + AMQP_VALUE offered_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &offered_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &offered_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* desired-capabilities */ + if (list_item_count > 12) + { + item_value = amqpvalue_get_list_item(list_value, 12); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* desired_capabilities = NULL; + AMQP_VALUE desired_capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &desired_capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &desired_capabilities) != 0)) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* properties */ + if (list_item_count > 13) + { + item_value = amqpvalue_get_list_item(list_value, 13); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields properties; + if (amqpvalue_get_fields(item_value, &properties) != 0) + { + amqpvalue_destroy(item_value); + attach_destroy(*attach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + attach_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int attach_get_name(ATTACH_HANDLE attach, const char** name_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, name_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_name(ATTACH_HANDLE attach, const char* name_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE name_amqp_value = amqpvalue_create_string(name_value); + if (name_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 0, name_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(name_amqp_value); + } + } + + return result; +} + +int attach_get_handle(ATTACH_HANDLE attach, handle* handle_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_handle(item_value, handle_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_handle(ATTACH_HANDLE attach, handle handle_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE handle_amqp_value = amqpvalue_create_handle(handle_value); + if (handle_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 1, handle_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return result; +} + +int attach_get_role(ATTACH_HANDLE attach, role* role_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_role(item_value, role_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_role(ATTACH_HANDLE attach, role role_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE role_amqp_value = amqpvalue_create_role(role_value); + if (role_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 2, role_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(role_amqp_value); + } + } + + return result; +} + +int attach_get_snd_settle_mode(ATTACH_HANDLE attach, sender_settle_mode* snd_settle_mode_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *snd_settle_mode_value = sender_settle_mode_mixed; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *snd_settle_mode_value = sender_settle_mode_mixed; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_sender_settle_mode(item_value, snd_settle_mode_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *snd_settle_mode_value = sender_settle_mode_mixed; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_snd_settle_mode(ATTACH_HANDLE attach, sender_settle_mode snd_settle_mode_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE snd_settle_mode_amqp_value = amqpvalue_create_sender_settle_mode(snd_settle_mode_value); + if (snd_settle_mode_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 3, snd_settle_mode_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(snd_settle_mode_amqp_value); + } + } + + return result; +} + +int attach_get_rcv_settle_mode(ATTACH_HANDLE attach, receiver_settle_mode* rcv_settle_mode_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + *rcv_settle_mode_value = receiver_settle_mode_first; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *rcv_settle_mode_value = receiver_settle_mode_first; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_receiver_settle_mode(item_value, rcv_settle_mode_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *rcv_settle_mode_value = receiver_settle_mode_first; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_rcv_settle_mode(ATTACH_HANDLE attach, receiver_settle_mode rcv_settle_mode_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE rcv_settle_mode_amqp_value = amqpvalue_create_receiver_settle_mode(rcv_settle_mode_value); + if (rcv_settle_mode_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 4, rcv_settle_mode_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(rcv_settle_mode_amqp_value); + } + } + + return result; +} + +int attach_get_source(ATTACH_HANDLE attach, AMQP_VALUE* source_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *source_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int attach_set_source(ATTACH_HANDLE attach, AMQP_VALUE source_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE source_amqp_value; + if (source_value == NULL) + { + source_amqp_value = NULL; + } + else + { + source_amqp_value = amqpvalue_clone(source_value); + } + if (source_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 5, source_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(source_amqp_value); + } + } + + return result; +} + +int attach_get_target(ATTACH_HANDLE attach, AMQP_VALUE* target_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *target_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int attach_set_target(ATTACH_HANDLE attach, AMQP_VALUE target_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE target_amqp_value; + if (target_value == NULL) + { + target_amqp_value = NULL; + } + else + { + target_amqp_value = amqpvalue_clone(target_value); + } + if (target_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 6, target_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(target_amqp_value); + } + } + + return result; +} + +int attach_get_unsettled(ATTACH_HANDLE attach, AMQP_VALUE* unsettled_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_map(item_value, unsettled_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_unsettled(ATTACH_HANDLE attach, AMQP_VALUE unsettled_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE unsettled_amqp_value; + if (unsettled_value == NULL) + { + unsettled_amqp_value = NULL; + } + else + { + unsettled_amqp_value = amqpvalue_clone(unsettled_value); + } + if (unsettled_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 7, unsettled_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(unsettled_amqp_value); + } + } + + return result; +} + +int attach_get_incomplete_unsettled(ATTACH_HANDLE attach, bool* incomplete_unsettled_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + *incomplete_unsettled_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *incomplete_unsettled_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, incomplete_unsettled_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *incomplete_unsettled_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_incomplete_unsettled(ATTACH_HANDLE attach, bool incomplete_unsettled_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE incomplete_unsettled_amqp_value = amqpvalue_create_boolean(incomplete_unsettled_value); + if (incomplete_unsettled_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 8, incomplete_unsettled_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(incomplete_unsettled_amqp_value); + } + } + + return result; +} + +int attach_get_initial_delivery_count(ATTACH_HANDLE attach, sequence_no* initial_delivery_count_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_sequence_no(item_value, initial_delivery_count_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_initial_delivery_count(ATTACH_HANDLE attach, sequence_no initial_delivery_count_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE initial_delivery_count_amqp_value = amqpvalue_create_sequence_no(initial_delivery_count_value); + if (initial_delivery_count_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 9, initial_delivery_count_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(initial_delivery_count_amqp_value); + } + } + + return result; +} + +int attach_get_max_message_size(ATTACH_HANDLE attach, uint64_t* max_message_size_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 10) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 10); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_ulong(item_value, max_message_size_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_max_message_size(ATTACH_HANDLE attach, uint64_t max_message_size_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE max_message_size_amqp_value = amqpvalue_create_ulong(max_message_size_value); + if (max_message_size_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 10, max_message_size_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(max_message_size_amqp_value); + } + } + + return result; +} + +int attach_get_offered_capabilities(ATTACH_HANDLE attach, AMQP_VALUE* offered_capabilities_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 11) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 11); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* offered_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &offered_capabilities_single_value); + } + else + { + (void)memset((void*)&offered_capabilities_single_value, 0, sizeof(offered_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, offered_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *offered_capabilities_value = amqpvalue_create_array(); + if (*offered_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(offered_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*offered_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 11, *offered_capabilities_value) != 0) + { + amqpvalue_destroy(*offered_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*offered_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int attach_set_offered_capabilities(ATTACH_HANDLE attach, AMQP_VALUE offered_capabilities_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE offered_capabilities_amqp_value; + if (offered_capabilities_value == NULL) + { + offered_capabilities_amqp_value = NULL; + } + else + { + offered_capabilities_amqp_value = amqpvalue_clone(offered_capabilities_value); + } + if (offered_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 11, offered_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(offered_capabilities_amqp_value); + } + } + + return result; +} + +int attach_get_desired_capabilities(ATTACH_HANDLE attach, AMQP_VALUE* desired_capabilities_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 12) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 12); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* desired_capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &desired_capabilities_single_value); + } + else + { + (void)memset((void*)&desired_capabilities_single_value, 0, sizeof(desired_capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, desired_capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *desired_capabilities_value = amqpvalue_create_array(); + if (*desired_capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(desired_capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*desired_capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 12, *desired_capabilities_value) != 0) + { + amqpvalue_destroy(*desired_capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*desired_capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int attach_set_desired_capabilities(ATTACH_HANDLE attach, AMQP_VALUE desired_capabilities_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE desired_capabilities_amqp_value; + if (desired_capabilities_value == NULL) + { + desired_capabilities_amqp_value = NULL; + } + else + { + desired_capabilities_amqp_value = amqpvalue_clone(desired_capabilities_value); + } + if (desired_capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 12, desired_capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(desired_capabilities_amqp_value); + } + } + + return result; +} + +int attach_get_properties(ATTACH_HANDLE attach, fields* properties_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + if (amqpvalue_get_composite_item_count(attach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 13) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(attach_instance->composite_value, 13); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int attach_set_properties(ATTACH_HANDLE attach, fields properties_value) +{ + int result; + + if (attach == NULL) + { + result = __FAILURE__; + } + else + { + ATTACH_INSTANCE* attach_instance = (ATTACH_INSTANCE*)attach; + AMQP_VALUE properties_amqp_value = amqpvalue_create_fields(properties_value); + if (properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(attach_instance->composite_value, 13, properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(properties_amqp_value); + } + } + + return result; +} + + +/* flow */ + +typedef struct FLOW_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} FLOW_INSTANCE; + +static FLOW_HANDLE flow_create_internal(void) +{ + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)malloc(sizeof(FLOW_INSTANCE)); + if (flow_instance != NULL) + { + flow_instance->composite_value = NULL; + } + + return flow_instance; +} + +FLOW_HANDLE flow_create(uint32_t incoming_window_value, transfer_number next_outgoing_id_value, uint32_t outgoing_window_value) +{ + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)malloc(sizeof(FLOW_INSTANCE)); + if (flow_instance != NULL) + { + flow_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(19); + if (flow_instance->composite_value == NULL) + { + free(flow_instance); + flow_instance = NULL; + } + else + { + AMQP_VALUE incoming_window_amqp_value; + AMQP_VALUE next_outgoing_id_amqp_value; + AMQP_VALUE outgoing_window_amqp_value; + int result = 0; + + incoming_window_amqp_value = amqpvalue_create_uint(incoming_window_value); + if ((result == 0) && (amqpvalue_set_composite_item(flow_instance->composite_value, 1, incoming_window_amqp_value) != 0)) + { + result = __FAILURE__; + } + next_outgoing_id_amqp_value = amqpvalue_create_transfer_number(next_outgoing_id_value); + if ((result == 0) && (amqpvalue_set_composite_item(flow_instance->composite_value, 2, next_outgoing_id_amqp_value) != 0)) + { + result = __FAILURE__; + } + outgoing_window_amqp_value = amqpvalue_create_uint(outgoing_window_value); + if ((result == 0) && (amqpvalue_set_composite_item(flow_instance->composite_value, 3, outgoing_window_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(incoming_window_amqp_value); + amqpvalue_destroy(next_outgoing_id_amqp_value); + amqpvalue_destroy(outgoing_window_amqp_value); + } + } + + return flow_instance; +} + +FLOW_HANDLE flow_clone(FLOW_HANDLE value) +{ + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)malloc(sizeof(FLOW_INSTANCE)); + if (flow_instance != NULL) + { + flow_instance->composite_value = amqpvalue_clone(((FLOW_INSTANCE*)value)->composite_value); + if (flow_instance->composite_value == NULL) + { + free(flow_instance); + flow_instance = NULL; + } + } + + return flow_instance; +} + +void flow_destroy(FLOW_HANDLE flow) +{ + if (flow != NULL) + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + amqpvalue_destroy(flow_instance->composite_value); + free(flow_instance); + } +} + +AMQP_VALUE amqpvalue_create_flow(FLOW_HANDLE flow) +{ + AMQP_VALUE result; + + if (flow == NULL) + { + result = NULL; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + result = amqpvalue_clone(flow_instance->composite_value); + } + + return result; +} + +bool is_flow_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 19)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_flow(AMQP_VALUE value, FLOW_HANDLE* flow_handle) +{ + int result; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow_create_internal(); + *flow_handle = flow_instance; + if (*flow_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + flow_destroy(*flow_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* next-incoming-id */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + transfer_number next_incoming_id; + if (amqpvalue_get_transfer_number(item_value, &next_incoming_id) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* incoming-window */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + { + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + else + { + uint32_t incoming_window; + if (amqpvalue_get_uint(item_value, &incoming_window) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* next-outgoing-id */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + { + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + else + { + transfer_number next_outgoing_id; + if (amqpvalue_get_transfer_number(item_value, &next_outgoing_id) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* outgoing-window */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + { + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + else + { + uint32_t outgoing_window; + if (amqpvalue_get_uint(item_value, &outgoing_window) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* handle */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + handle handle; + if (amqpvalue_get_handle(item_value, &handle) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* delivery-count */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + sequence_no delivery_count; + if (amqpvalue_get_sequence_no(item_value, &delivery_count) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* link-credit */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint32_t link_credit; + if (amqpvalue_get_uint(item_value, &link_credit) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* available */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint32_t available; + if (amqpvalue_get_uint(item_value, &available) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* drain */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool drain; + if (amqpvalue_get_boolean(item_value, &drain) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* echo */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool echo; + if (amqpvalue_get_boolean(item_value, &echo) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* properties */ + if (list_item_count > 10) + { + item_value = amqpvalue_get_list_item(list_value, 10); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields properties; + if (amqpvalue_get_fields(item_value, &properties) != 0) + { + amqpvalue_destroy(item_value); + flow_destroy(*flow_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + flow_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int flow_get_next_incoming_id(FLOW_HANDLE flow, transfer_number* next_incoming_id_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_transfer_number(item_value, next_incoming_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_next_incoming_id(FLOW_HANDLE flow, transfer_number next_incoming_id_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE next_incoming_id_amqp_value = amqpvalue_create_transfer_number(next_incoming_id_value); + if (next_incoming_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 0, next_incoming_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(next_incoming_id_amqp_value); + } + } + + return result; +} + +int flow_get_incoming_window(FLOW_HANDLE flow, uint32_t* incoming_window_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, incoming_window_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_incoming_window(FLOW_HANDLE flow, uint32_t incoming_window_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE incoming_window_amqp_value = amqpvalue_create_uint(incoming_window_value); + if (incoming_window_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 1, incoming_window_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(incoming_window_amqp_value); + } + } + + return result; +} + +int flow_get_next_outgoing_id(FLOW_HANDLE flow, transfer_number* next_outgoing_id_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_transfer_number(item_value, next_outgoing_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_next_outgoing_id(FLOW_HANDLE flow, transfer_number next_outgoing_id_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE next_outgoing_id_amqp_value = amqpvalue_create_transfer_number(next_outgoing_id_value); + if (next_outgoing_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 2, next_outgoing_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(next_outgoing_id_amqp_value); + } + } + + return result; +} + +int flow_get_outgoing_window(FLOW_HANDLE flow, uint32_t* outgoing_window_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, outgoing_window_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_outgoing_window(FLOW_HANDLE flow, uint32_t outgoing_window_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE outgoing_window_amqp_value = amqpvalue_create_uint(outgoing_window_value); + if (outgoing_window_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 3, outgoing_window_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(outgoing_window_amqp_value); + } + } + + return result; +} + +int flow_get_handle(FLOW_HANDLE flow, handle* handle_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_handle(item_value, handle_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_handle(FLOW_HANDLE flow, handle handle_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE handle_amqp_value = amqpvalue_create_handle(handle_value); + if (handle_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 4, handle_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return result; +} + +int flow_get_delivery_count(FLOW_HANDLE flow, sequence_no* delivery_count_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_sequence_no(item_value, delivery_count_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_delivery_count(FLOW_HANDLE flow, sequence_no delivery_count_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE delivery_count_amqp_value = amqpvalue_create_sequence_no(delivery_count_value); + if (delivery_count_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 5, delivery_count_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(delivery_count_amqp_value); + } + } + + return result; +} + +int flow_get_link_credit(FLOW_HANDLE flow, uint32_t* link_credit_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, link_credit_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_link_credit(FLOW_HANDLE flow, uint32_t link_credit_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE link_credit_amqp_value = amqpvalue_create_uint(link_credit_value); + if (link_credit_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 6, link_credit_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(link_credit_amqp_value); + } + } + + return result; +} + +int flow_get_available(FLOW_HANDLE flow, uint32_t* available_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, available_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_available(FLOW_HANDLE flow, uint32_t available_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE available_amqp_value = amqpvalue_create_uint(available_value); + if (available_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 7, available_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(available_amqp_value); + } + } + + return result; +} + +int flow_get_drain(FLOW_HANDLE flow, bool* drain_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + *drain_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *drain_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, drain_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *drain_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_drain(FLOW_HANDLE flow, bool drain_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE drain_amqp_value = amqpvalue_create_boolean(drain_value); + if (drain_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 8, drain_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(drain_amqp_value); + } + } + + return result; +} + +int flow_get_echo(FLOW_HANDLE flow, bool* echo_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + *echo_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *echo_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, echo_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *echo_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_echo(FLOW_HANDLE flow, bool echo_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE echo_amqp_value = amqpvalue_create_boolean(echo_value); + if (echo_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 9, echo_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(echo_amqp_value); + } + } + + return result; +} + +int flow_get_properties(FLOW_HANDLE flow, fields* properties_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + if (amqpvalue_get_composite_item_count(flow_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 10) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(flow_instance->composite_value, 10); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int flow_set_properties(FLOW_HANDLE flow, fields properties_value) +{ + int result; + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_INSTANCE* flow_instance = (FLOW_INSTANCE*)flow; + AMQP_VALUE properties_amqp_value = amqpvalue_create_fields(properties_value); + if (properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(flow_instance->composite_value, 10, properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(properties_amqp_value); + } + } + + return result; +} + + +/* transfer */ + +typedef struct TRANSFER_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} TRANSFER_INSTANCE; + +static TRANSFER_HANDLE transfer_create_internal(void) +{ + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)malloc(sizeof(TRANSFER_INSTANCE)); + if (transfer_instance != NULL) + { + transfer_instance->composite_value = NULL; + } + + return transfer_instance; +} + +TRANSFER_HANDLE transfer_create(handle handle_value) +{ + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)malloc(sizeof(TRANSFER_INSTANCE)); + if (transfer_instance != NULL) + { + transfer_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(20); + if (transfer_instance->composite_value == NULL) + { + free(transfer_instance); + transfer_instance = NULL; + } + else + { + AMQP_VALUE handle_amqp_value; + int result = 0; + + handle_amqp_value = amqpvalue_create_handle(handle_value); + if ((result == 0) && (amqpvalue_set_composite_item(transfer_instance->composite_value, 0, handle_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return transfer_instance; +} + +TRANSFER_HANDLE transfer_clone(TRANSFER_HANDLE value) +{ + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)malloc(sizeof(TRANSFER_INSTANCE)); + if (transfer_instance != NULL) + { + transfer_instance->composite_value = amqpvalue_clone(((TRANSFER_INSTANCE*)value)->composite_value); + if (transfer_instance->composite_value == NULL) + { + free(transfer_instance); + transfer_instance = NULL; + } + } + + return transfer_instance; +} + +void transfer_destroy(TRANSFER_HANDLE transfer) +{ + if (transfer != NULL) + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + amqpvalue_destroy(transfer_instance->composite_value); + free(transfer_instance); + } +} + +AMQP_VALUE amqpvalue_create_transfer(TRANSFER_HANDLE transfer) +{ + AMQP_VALUE result; + + if (transfer == NULL) + { + result = NULL; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + result = amqpvalue_clone(transfer_instance->composite_value); + } + + return result; +} + +bool is_transfer_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 20)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_transfer(AMQP_VALUE value, TRANSFER_HANDLE* transfer_handle) +{ + int result; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer_create_internal(); + *transfer_handle = transfer_instance; + if (*transfer_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + transfer_destroy(*transfer_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* handle */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + else + { + handle handle; + if (amqpvalue_get_handle(item_value, &handle) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* delivery-id */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + delivery_number delivery_id; + if (amqpvalue_get_delivery_number(item_value, &delivery_id) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* delivery-tag */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + delivery_tag delivery_tag; + if (amqpvalue_get_delivery_tag(item_value, &delivery_tag) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* message-format */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + message_format message_format; + if (amqpvalue_get_message_format(item_value, &message_format) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* settled */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool settled; + if (amqpvalue_get_boolean(item_value, &settled) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* more */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool more; + if (amqpvalue_get_boolean(item_value, &more) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* rcv-settle-mode */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + receiver_settle_mode rcv_settle_mode; + if (amqpvalue_get_receiver_settle_mode(item_value, &rcv_settle_mode) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* state */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* resume */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool resume; + if (amqpvalue_get_boolean(item_value, &resume) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* aborted */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool aborted; + if (amqpvalue_get_boolean(item_value, &aborted) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* batchable */ + if (list_item_count > 10) + { + item_value = amqpvalue_get_list_item(list_value, 10); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool batchable; + if (amqpvalue_get_boolean(item_value, &batchable) != 0) + { + amqpvalue_destroy(item_value); + transfer_destroy(*transfer_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + transfer_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int transfer_get_handle(TRANSFER_HANDLE transfer, handle* handle_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_handle(item_value, handle_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_handle(TRANSFER_HANDLE transfer, handle handle_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE handle_amqp_value = amqpvalue_create_handle(handle_value); + if (handle_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 0, handle_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return result; +} + +int transfer_get_delivery_id(TRANSFER_HANDLE transfer, delivery_number* delivery_id_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_delivery_number(item_value, delivery_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_delivery_id(TRANSFER_HANDLE transfer, delivery_number delivery_id_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE delivery_id_amqp_value = amqpvalue_create_delivery_number(delivery_id_value); + if (delivery_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 1, delivery_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(delivery_id_amqp_value); + } + } + + return result; +} + +int transfer_get_delivery_tag(TRANSFER_HANDLE transfer, delivery_tag* delivery_tag_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_delivery_tag(item_value, delivery_tag_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_delivery_tag(TRANSFER_HANDLE transfer, delivery_tag delivery_tag_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE delivery_tag_amqp_value = amqpvalue_create_delivery_tag(delivery_tag_value); + if (delivery_tag_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 2, delivery_tag_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(delivery_tag_amqp_value); + } + } + + return result; +} + +int transfer_get_message_format(TRANSFER_HANDLE transfer, message_format* message_format_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_message_format(item_value, message_format_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_message_format(TRANSFER_HANDLE transfer, message_format message_format_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE message_format_amqp_value = amqpvalue_create_message_format(message_format_value); + if (message_format_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 3, message_format_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(message_format_amqp_value); + } + } + + return result; +} + +int transfer_get_settled(TRANSFER_HANDLE transfer, bool* settled_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, settled_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_settled(TRANSFER_HANDLE transfer, bool settled_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE settled_amqp_value = amqpvalue_create_boolean(settled_value); + if (settled_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 4, settled_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(settled_amqp_value); + } + } + + return result; +} + +int transfer_get_more(TRANSFER_HANDLE transfer, bool* more_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + *more_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *more_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, more_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *more_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_more(TRANSFER_HANDLE transfer, bool more_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE more_amqp_value = amqpvalue_create_boolean(more_value); + if (more_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 5, more_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(more_amqp_value); + } + } + + return result; +} + +int transfer_get_rcv_settle_mode(TRANSFER_HANDLE transfer, receiver_settle_mode* rcv_settle_mode_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_receiver_settle_mode(item_value, rcv_settle_mode_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_rcv_settle_mode(TRANSFER_HANDLE transfer, receiver_settle_mode rcv_settle_mode_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE rcv_settle_mode_amqp_value = amqpvalue_create_receiver_settle_mode(rcv_settle_mode_value); + if (rcv_settle_mode_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 6, rcv_settle_mode_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(rcv_settle_mode_amqp_value); + } + } + + return result; +} + +int transfer_get_state(TRANSFER_HANDLE transfer, AMQP_VALUE* state_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *state_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int transfer_set_state(TRANSFER_HANDLE transfer, AMQP_VALUE state_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE state_amqp_value; + if (state_value == NULL) + { + state_amqp_value = NULL; + } + else + { + state_amqp_value = amqpvalue_clone(state_value); + } + if (state_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 7, state_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(state_amqp_value); + } + } + + return result; +} + +int transfer_get_resume(TRANSFER_HANDLE transfer, bool* resume_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + *resume_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *resume_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, resume_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *resume_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_resume(TRANSFER_HANDLE transfer, bool resume_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE resume_amqp_value = amqpvalue_create_boolean(resume_value); + if (resume_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 8, resume_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(resume_amqp_value); + } + } + + return result; +} + +int transfer_get_aborted(TRANSFER_HANDLE transfer, bool* aborted_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + *aborted_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *aborted_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, aborted_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *aborted_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_aborted(TRANSFER_HANDLE transfer, bool aborted_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE aborted_amqp_value = amqpvalue_create_boolean(aborted_value); + if (aborted_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 9, aborted_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(aborted_amqp_value); + } + } + + return result; +} + +int transfer_get_batchable(TRANSFER_HANDLE transfer, bool* batchable_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + if (amqpvalue_get_composite_item_count(transfer_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 10) + { + *batchable_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(transfer_instance->composite_value, 10); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *batchable_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, batchable_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *batchable_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int transfer_set_batchable(TRANSFER_HANDLE transfer, bool batchable_value) +{ + int result; + + if (transfer == NULL) + { + result = __FAILURE__; + } + else + { + TRANSFER_INSTANCE* transfer_instance = (TRANSFER_INSTANCE*)transfer; + AMQP_VALUE batchable_amqp_value = amqpvalue_create_boolean(batchable_value); + if (batchable_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(transfer_instance->composite_value, 10, batchable_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(batchable_amqp_value); + } + } + + return result; +} + + +/* disposition */ + +typedef struct DISPOSITION_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} DISPOSITION_INSTANCE; + +static DISPOSITION_HANDLE disposition_create_internal(void) +{ + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)malloc(sizeof(DISPOSITION_INSTANCE)); + if (disposition_instance != NULL) + { + disposition_instance->composite_value = NULL; + } + + return disposition_instance; +} + +DISPOSITION_HANDLE disposition_create(role role_value, delivery_number first_value) +{ + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)malloc(sizeof(DISPOSITION_INSTANCE)); + if (disposition_instance != NULL) + { + disposition_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(21); + if (disposition_instance->composite_value == NULL) + { + free(disposition_instance); + disposition_instance = NULL; + } + else + { + AMQP_VALUE role_amqp_value; + AMQP_VALUE first_amqp_value; + int result = 0; + + role_amqp_value = amqpvalue_create_role(role_value); + if ((result == 0) && (amqpvalue_set_composite_item(disposition_instance->composite_value, 0, role_amqp_value) != 0)) + { + result = __FAILURE__; + } + first_amqp_value = amqpvalue_create_delivery_number(first_value); + if ((result == 0) && (amqpvalue_set_composite_item(disposition_instance->composite_value, 1, first_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(role_amqp_value); + amqpvalue_destroy(first_amqp_value); + } + } + + return disposition_instance; +} + +DISPOSITION_HANDLE disposition_clone(DISPOSITION_HANDLE value) +{ + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)malloc(sizeof(DISPOSITION_INSTANCE)); + if (disposition_instance != NULL) + { + disposition_instance->composite_value = amqpvalue_clone(((DISPOSITION_INSTANCE*)value)->composite_value); + if (disposition_instance->composite_value == NULL) + { + free(disposition_instance); + disposition_instance = NULL; + } + } + + return disposition_instance; +} + +void disposition_destroy(DISPOSITION_HANDLE disposition) +{ + if (disposition != NULL) + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + amqpvalue_destroy(disposition_instance->composite_value); + free(disposition_instance); + } +} + +AMQP_VALUE amqpvalue_create_disposition(DISPOSITION_HANDLE disposition) +{ + AMQP_VALUE result; + + if (disposition == NULL) + { + result = NULL; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + result = amqpvalue_clone(disposition_instance->composite_value); + } + + return result; +} + +bool is_disposition_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 21)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_disposition(AMQP_VALUE value, DISPOSITION_HANDLE* disposition_handle) +{ + int result; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition_create_internal(); + *disposition_handle = disposition_instance; + if (*disposition_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + disposition_destroy(*disposition_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* role */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + else + { + role role; + if (amqpvalue_get_role(item_value, &role) != 0) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* first */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + { + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + else + { + delivery_number first; + if (amqpvalue_get_delivery_number(item_value, &first) != 0) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* last */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + delivery_number last; + if (amqpvalue_get_delivery_number(item_value, &last) != 0) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* settled */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool settled; + if (amqpvalue_get_boolean(item_value, &settled) != 0) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* state */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* batchable */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool batchable; + if (amqpvalue_get_boolean(item_value, &batchable) != 0) + { + amqpvalue_destroy(item_value); + disposition_destroy(*disposition_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + disposition_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int disposition_get_role(DISPOSITION_HANDLE disposition, role* role_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_role(item_value, role_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int disposition_set_role(DISPOSITION_HANDLE disposition, role role_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE role_amqp_value = amqpvalue_create_role(role_value); + if (role_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 0, role_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(role_amqp_value); + } + } + + return result; +} + +int disposition_get_first(DISPOSITION_HANDLE disposition, delivery_number* first_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_delivery_number(item_value, first_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int disposition_set_first(DISPOSITION_HANDLE disposition, delivery_number first_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE first_amqp_value = amqpvalue_create_delivery_number(first_value); + if (first_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 1, first_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(first_amqp_value); + } + } + + return result; +} + +int disposition_get_last(DISPOSITION_HANDLE disposition, delivery_number* last_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_delivery_number(item_value, last_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int disposition_set_last(DISPOSITION_HANDLE disposition, delivery_number last_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE last_amqp_value = amqpvalue_create_delivery_number(last_value); + if (last_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 2, last_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(last_amqp_value); + } + } + + return result; +} + +int disposition_get_settled(DISPOSITION_HANDLE disposition, bool* settled_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *settled_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *settled_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, settled_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *settled_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int disposition_set_settled(DISPOSITION_HANDLE disposition, bool settled_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE settled_amqp_value = amqpvalue_create_boolean(settled_value); + if (settled_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 3, settled_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(settled_amqp_value); + } + } + + return result; +} + +int disposition_get_state(DISPOSITION_HANDLE disposition, AMQP_VALUE* state_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *state_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int disposition_set_state(DISPOSITION_HANDLE disposition, AMQP_VALUE state_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE state_amqp_value; + if (state_value == NULL) + { + state_amqp_value = NULL; + } + else + { + state_amqp_value = amqpvalue_clone(state_value); + } + if (state_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 4, state_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(state_amqp_value); + } + } + + return result; +} + +int disposition_get_batchable(DISPOSITION_HANDLE disposition, bool* batchable_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + if (amqpvalue_get_composite_item_count(disposition_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + *batchable_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(disposition_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *batchable_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, batchable_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *batchable_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int disposition_set_batchable(DISPOSITION_HANDLE disposition, bool batchable_value) +{ + int result; + + if (disposition == NULL) + { + result = __FAILURE__; + } + else + { + DISPOSITION_INSTANCE* disposition_instance = (DISPOSITION_INSTANCE*)disposition; + AMQP_VALUE batchable_amqp_value = amqpvalue_create_boolean(batchable_value); + if (batchable_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(disposition_instance->composite_value, 5, batchable_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(batchable_amqp_value); + } + } + + return result; +} + + +/* detach */ + +typedef struct DETACH_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} DETACH_INSTANCE; + +static DETACH_HANDLE detach_create_internal(void) +{ + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)malloc(sizeof(DETACH_INSTANCE)); + if (detach_instance != NULL) + { + detach_instance->composite_value = NULL; + } + + return detach_instance; +} + +DETACH_HANDLE detach_create(handle handle_value) +{ + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)malloc(sizeof(DETACH_INSTANCE)); + if (detach_instance != NULL) + { + detach_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(22); + if (detach_instance->composite_value == NULL) + { + free(detach_instance); + detach_instance = NULL; + } + else + { + AMQP_VALUE handle_amqp_value; + int result = 0; + + handle_amqp_value = amqpvalue_create_handle(handle_value); + if ((result == 0) && (amqpvalue_set_composite_item(detach_instance->composite_value, 0, handle_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return detach_instance; +} + +DETACH_HANDLE detach_clone(DETACH_HANDLE value) +{ + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)malloc(sizeof(DETACH_INSTANCE)); + if (detach_instance != NULL) + { + detach_instance->composite_value = amqpvalue_clone(((DETACH_INSTANCE*)value)->composite_value); + if (detach_instance->composite_value == NULL) + { + free(detach_instance); + detach_instance = NULL; + } + } + + return detach_instance; +} + +void detach_destroy(DETACH_HANDLE detach) +{ + if (detach != NULL) + { + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + amqpvalue_destroy(detach_instance->composite_value); + free(detach_instance); + } +} + +AMQP_VALUE amqpvalue_create_detach(DETACH_HANDLE detach) +{ + AMQP_VALUE result; + + if (detach == NULL) + { + result = NULL; + } + else + { + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + result = amqpvalue_clone(detach_instance->composite_value); + } + + return result; +} + +bool is_detach_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 22)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_detach(AMQP_VALUE value, DETACH_HANDLE* detach_handle) +{ + int result; + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach_create_internal(); + *detach_handle = detach_instance; + if (*detach_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + detach_destroy(*detach_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* handle */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + detach_destroy(*detach_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + detach_destroy(*detach_handle); + result = __FAILURE__; + break; + } + else + { + handle handle; + if (amqpvalue_get_handle(item_value, &handle) != 0) + { + amqpvalue_destroy(item_value); + detach_destroy(*detach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* closed */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool closed; + if (amqpvalue_get_boolean(item_value, &closed) != 0) + { + amqpvalue_destroy(item_value); + detach_destroy(*detach_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* error */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ERROR_HANDLE error; + if (amqpvalue_get_error(item_value, &error) != 0) + { + amqpvalue_destroy(item_value); + detach_destroy(*detach_handle); + result = __FAILURE__; + break; + } + else + { + error_destroy(error); + } + } + + amqpvalue_destroy(item_value); + } + } + + detach_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int detach_get_handle(DETACH_HANDLE detach, handle* handle_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + if (amqpvalue_get_composite_item_count(detach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(detach_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_handle(item_value, handle_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int detach_set_handle(DETACH_HANDLE detach, handle handle_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + AMQP_VALUE handle_amqp_value = amqpvalue_create_handle(handle_value); + if (handle_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(detach_instance->composite_value, 0, handle_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(handle_amqp_value); + } + } + + return result; +} + +int detach_get_closed(DETACH_HANDLE detach, bool* closed_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + if (amqpvalue_get_composite_item_count(detach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + *closed_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(detach_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *closed_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, closed_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *closed_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int detach_set_closed(DETACH_HANDLE detach, bool closed_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + AMQP_VALUE closed_amqp_value = amqpvalue_create_boolean(closed_value); + if (closed_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(detach_instance->composite_value, 1, closed_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(closed_amqp_value); + } + } + + return result; +} + +int detach_get_error(DETACH_HANDLE detach, ERROR_HANDLE* error_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + if (amqpvalue_get_composite_item_count(detach_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(detach_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_error(item_value, error_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int detach_set_error(DETACH_HANDLE detach, ERROR_HANDLE error_value) +{ + int result; + + if (detach == NULL) + { + result = __FAILURE__; + } + else + { + DETACH_INSTANCE* detach_instance = (DETACH_INSTANCE*)detach; + AMQP_VALUE error_amqp_value = amqpvalue_create_error(error_value); + if (error_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(detach_instance->composite_value, 2, error_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(error_amqp_value); + } + } + + return result; +} + + +/* end */ + +typedef struct END_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} END_INSTANCE; + +static END_HANDLE end_create_internal(void) +{ + END_INSTANCE* end_instance = (END_INSTANCE*)malloc(sizeof(END_INSTANCE)); + if (end_instance != NULL) + { + end_instance->composite_value = NULL; + } + + return end_instance; +} + +END_HANDLE end_create(void) +{ + END_INSTANCE* end_instance = (END_INSTANCE*)malloc(sizeof(END_INSTANCE)); + if (end_instance != NULL) + { + end_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(23); + if (end_instance->composite_value == NULL) + { + free(end_instance); + end_instance = NULL; + } + } + + return end_instance; +} + +END_HANDLE end_clone(END_HANDLE value) +{ + END_INSTANCE* end_instance = (END_INSTANCE*)malloc(sizeof(END_INSTANCE)); + if (end_instance != NULL) + { + end_instance->composite_value = amqpvalue_clone(((END_INSTANCE*)value)->composite_value); + if (end_instance->composite_value == NULL) + { + free(end_instance); + end_instance = NULL; + } + } + + return end_instance; +} + +void end_destroy(END_HANDLE end) +{ + if (end != NULL) + { + END_INSTANCE* end_instance = (END_INSTANCE*)end; + amqpvalue_destroy(end_instance->composite_value); + free(end_instance); + } +} + +AMQP_VALUE amqpvalue_create_end(END_HANDLE end) +{ + AMQP_VALUE result; + + if (end == NULL) + { + result = NULL; + } + else + { + END_INSTANCE* end_instance = (END_INSTANCE*)end; + result = amqpvalue_clone(end_instance->composite_value); + } + + return result; +} + +bool is_end_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 23)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_end(AMQP_VALUE value, END_HANDLE* end_handle) +{ + int result; + END_INSTANCE* end_instance = (END_INSTANCE*)end_create_internal(); + *end_handle = end_instance; + if (*end_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + end_destroy(*end_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* error */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ERROR_HANDLE error; + if (amqpvalue_get_error(item_value, &error) != 0) + { + amqpvalue_destroy(item_value); + end_destroy(*end_handle); + result = __FAILURE__; + break; + } + else + { + error_destroy(error); + } + } + + amqpvalue_destroy(item_value); + } + } + + end_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int end_get_error(END_HANDLE end, ERROR_HANDLE* error_value) +{ + int result; + + if (end == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + END_INSTANCE* end_instance = (END_INSTANCE*)end; + if (amqpvalue_get_composite_item_count(end_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(end_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_error(item_value, error_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int end_set_error(END_HANDLE end, ERROR_HANDLE error_value) +{ + int result; + + if (end == NULL) + { + result = __FAILURE__; + } + else + { + END_INSTANCE* end_instance = (END_INSTANCE*)end; + AMQP_VALUE error_amqp_value = amqpvalue_create_error(error_value); + if (error_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(end_instance->composite_value, 0, error_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(error_amqp_value); + } + } + + return result; +} + + +/* close */ + +typedef struct CLOSE_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} CLOSE_INSTANCE; + +static CLOSE_HANDLE close_create_internal(void) +{ + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)malloc(sizeof(CLOSE_INSTANCE)); + if (close_instance != NULL) + { + close_instance->composite_value = NULL; + } + + return close_instance; +} + +CLOSE_HANDLE close_create(void) +{ + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)malloc(sizeof(CLOSE_INSTANCE)); + if (close_instance != NULL) + { + close_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(24); + if (close_instance->composite_value == NULL) + { + free(close_instance); + close_instance = NULL; + } + } + + return close_instance; +} + +CLOSE_HANDLE close_clone(CLOSE_HANDLE value) +{ + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)malloc(sizeof(CLOSE_INSTANCE)); + if (close_instance != NULL) + { + close_instance->composite_value = amqpvalue_clone(((CLOSE_INSTANCE*)value)->composite_value); + if (close_instance->composite_value == NULL) + { + free(close_instance); + close_instance = NULL; + } + } + + return close_instance; +} + +void close_destroy(CLOSE_HANDLE close) +{ + if (close != NULL) + { + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)close; + amqpvalue_destroy(close_instance->composite_value); + free(close_instance); + } +} + +AMQP_VALUE amqpvalue_create_close(CLOSE_HANDLE close) +{ + AMQP_VALUE result; + + if (close == NULL) + { + result = NULL; + } + else + { + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)close; + result = amqpvalue_clone(close_instance->composite_value); + } + + return result; +} + +bool is_close_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 24)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_close(AMQP_VALUE value, CLOSE_HANDLE* close_handle) +{ + int result; + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)close_create_internal(); + *close_handle = close_instance; + if (*close_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + close_destroy(*close_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* error */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ERROR_HANDLE error; + if (amqpvalue_get_error(item_value, &error) != 0) + { + amqpvalue_destroy(item_value); + close_destroy(*close_handle); + result = __FAILURE__; + break; + } + else + { + error_destroy(error); + } + } + + amqpvalue_destroy(item_value); + } + } + + close_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int close_get_error(CLOSE_HANDLE close, ERROR_HANDLE* error_value) +{ + int result; + + if (close == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)close; + if (amqpvalue_get_composite_item_count(close_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(close_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_error(item_value, error_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int close_set_error(CLOSE_HANDLE close, ERROR_HANDLE error_value) +{ + int result; + + if (close == NULL) + { + result = __FAILURE__; + } + else + { + CLOSE_INSTANCE* close_instance = (CLOSE_INSTANCE*)close; + AMQP_VALUE error_amqp_value = amqpvalue_create_error(error_value); + if (error_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(close_instance->composite_value, 0, error_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(error_amqp_value); + } + } + + return result; +} + + +/* sasl-code */ + +AMQP_VALUE amqpvalue_create_sasl_code(sasl_code value) +{ + return amqpvalue_create_ubyte(value); +} + +/* sasl-mechanisms */ + +typedef struct SASL_MECHANISMS_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SASL_MECHANISMS_INSTANCE; + +static SASL_MECHANISMS_HANDLE sasl_mechanisms_create_internal(void) +{ + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)malloc(sizeof(SASL_MECHANISMS_INSTANCE)); + if (sasl_mechanisms_instance != NULL) + { + sasl_mechanisms_instance->composite_value = NULL; + } + + return sasl_mechanisms_instance; +} + +SASL_MECHANISMS_HANDLE sasl_mechanisms_create(AMQP_VALUE sasl_server_mechanisms_value) +{ + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)malloc(sizeof(SASL_MECHANISMS_INSTANCE)); + if (sasl_mechanisms_instance != NULL) + { + sasl_mechanisms_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(64); + if (sasl_mechanisms_instance->composite_value == NULL) + { + free(sasl_mechanisms_instance); + sasl_mechanisms_instance = NULL; + } + else + { + AMQP_VALUE sasl_server_mechanisms_amqp_value; + int result = 0; + + sasl_server_mechanisms_amqp_value = sasl_server_mechanisms_value; + if ((result == 0) && (amqpvalue_set_composite_item(sasl_mechanisms_instance->composite_value, 0, sasl_server_mechanisms_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(sasl_server_mechanisms_amqp_value); + } + } + + return sasl_mechanisms_instance; +} + +SASL_MECHANISMS_HANDLE sasl_mechanisms_clone(SASL_MECHANISMS_HANDLE value) +{ + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)malloc(sizeof(SASL_MECHANISMS_INSTANCE)); + if (sasl_mechanisms_instance != NULL) + { + sasl_mechanisms_instance->composite_value = amqpvalue_clone(((SASL_MECHANISMS_INSTANCE*)value)->composite_value); + if (sasl_mechanisms_instance->composite_value == NULL) + { + free(sasl_mechanisms_instance); + sasl_mechanisms_instance = NULL; + } + } + + return sasl_mechanisms_instance; +} + +void sasl_mechanisms_destroy(SASL_MECHANISMS_HANDLE sasl_mechanisms) +{ + if (sasl_mechanisms != NULL) + { + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)sasl_mechanisms; + amqpvalue_destroy(sasl_mechanisms_instance->composite_value); + free(sasl_mechanisms_instance); + } +} + +AMQP_VALUE amqpvalue_create_sasl_mechanisms(SASL_MECHANISMS_HANDLE sasl_mechanisms) +{ + AMQP_VALUE result; + + if (sasl_mechanisms == NULL) + { + result = NULL; + } + else + { + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)sasl_mechanisms; + result = amqpvalue_clone(sasl_mechanisms_instance->composite_value); + } + + return result; +} + +bool is_sasl_mechanisms_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 64)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_sasl_mechanisms(AMQP_VALUE value, SASL_MECHANISMS_HANDLE* sasl_mechanisms_handle) +{ + int result; + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)sasl_mechanisms_create_internal(); + *sasl_mechanisms_handle = sasl_mechanisms_instance; + if (*sasl_mechanisms_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + sasl_mechanisms_destroy(*sasl_mechanisms_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* sasl-server-mechanisms */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + sasl_mechanisms_destroy(*sasl_mechanisms_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + sasl_mechanisms_destroy(*sasl_mechanisms_handle); + result = __FAILURE__; + break; + } + else + { + const char* sasl_server_mechanisms = NULL; + AMQP_VALUE sasl_server_mechanisms_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &sasl_server_mechanisms_array) != 0)) && + (amqpvalue_get_symbol(item_value, &sasl_server_mechanisms) != 0)) + { + amqpvalue_destroy(item_value); + sasl_mechanisms_destroy(*sasl_mechanisms_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + + sasl_mechanisms_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int sasl_mechanisms_get_sasl_server_mechanisms(SASL_MECHANISMS_HANDLE sasl_mechanisms, AMQP_VALUE* sasl_server_mechanisms_value) +{ + int result; + + if (sasl_mechanisms == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)sasl_mechanisms; + if (amqpvalue_get_composite_item_count(sasl_mechanisms_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_mechanisms_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* sasl_server_mechanisms_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &sasl_server_mechanisms_single_value); + } + else + { + (void)memset((void*)&sasl_server_mechanisms_single_value, 0, sizeof(sasl_server_mechanisms_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, sasl_server_mechanisms_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *sasl_server_mechanisms_value = amqpvalue_create_array(); + if (*sasl_server_mechanisms_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(sasl_server_mechanisms_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*sasl_server_mechanisms_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*sasl_server_mechanisms_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*sasl_server_mechanisms_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_mechanisms_instance->composite_value, 0, *sasl_server_mechanisms_value) != 0) + { + amqpvalue_destroy(*sasl_server_mechanisms_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*sasl_server_mechanisms_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int sasl_mechanisms_set_sasl_server_mechanisms(SASL_MECHANISMS_HANDLE sasl_mechanisms, AMQP_VALUE sasl_server_mechanisms_value) +{ + int result; + + if (sasl_mechanisms == NULL) + { + result = __FAILURE__; + } + else + { + SASL_MECHANISMS_INSTANCE* sasl_mechanisms_instance = (SASL_MECHANISMS_INSTANCE*)sasl_mechanisms; + AMQP_VALUE sasl_server_mechanisms_amqp_value; + if (sasl_server_mechanisms_value == NULL) + { + sasl_server_mechanisms_amqp_value = NULL; + } + else + { + sasl_server_mechanisms_amqp_value = amqpvalue_clone(sasl_server_mechanisms_value); + } + if (sasl_server_mechanisms_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_mechanisms_instance->composite_value, 0, sasl_server_mechanisms_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(sasl_server_mechanisms_amqp_value); + } + } + + return result; +} + + +/* sasl-init */ + +typedef struct SASL_INIT_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SASL_INIT_INSTANCE; + +static SASL_INIT_HANDLE sasl_init_create_internal(void) +{ + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)malloc(sizeof(SASL_INIT_INSTANCE)); + if (sasl_init_instance != NULL) + { + sasl_init_instance->composite_value = NULL; + } + + return sasl_init_instance; +} + +SASL_INIT_HANDLE sasl_init_create(const char* mechanism_value) +{ + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)malloc(sizeof(SASL_INIT_INSTANCE)); + if (sasl_init_instance != NULL) + { + sasl_init_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(65); + if (sasl_init_instance->composite_value == NULL) + { + free(sasl_init_instance); + sasl_init_instance = NULL; + } + else + { + AMQP_VALUE mechanism_amqp_value; + int result = 0; + + mechanism_amqp_value = amqpvalue_create_symbol(mechanism_value); + if ((result == 0) && (amqpvalue_set_composite_item(sasl_init_instance->composite_value, 0, mechanism_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(mechanism_amqp_value); + } + } + + return sasl_init_instance; +} + +SASL_INIT_HANDLE sasl_init_clone(SASL_INIT_HANDLE value) +{ + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)malloc(sizeof(SASL_INIT_INSTANCE)); + if (sasl_init_instance != NULL) + { + sasl_init_instance->composite_value = amqpvalue_clone(((SASL_INIT_INSTANCE*)value)->composite_value); + if (sasl_init_instance->composite_value == NULL) + { + free(sasl_init_instance); + sasl_init_instance = NULL; + } + } + + return sasl_init_instance; +} + +void sasl_init_destroy(SASL_INIT_HANDLE sasl_init) +{ + if (sasl_init != NULL) + { + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + amqpvalue_destroy(sasl_init_instance->composite_value); + free(sasl_init_instance); + } +} + +AMQP_VALUE amqpvalue_create_sasl_init(SASL_INIT_HANDLE sasl_init) +{ + AMQP_VALUE result; + + if (sasl_init == NULL) + { + result = NULL; + } + else + { + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + result = amqpvalue_clone(sasl_init_instance->composite_value); + } + + return result; +} + +bool is_sasl_init_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 65)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_sasl_init(AMQP_VALUE value, SASL_INIT_HANDLE* sasl_init_handle) +{ + int result; + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init_create_internal(); + *sasl_init_handle = sasl_init_instance; + if (*sasl_init_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* mechanism */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + break; + } + else + { + const char* mechanism; + if (amqpvalue_get_symbol(item_value, &mechanism) != 0) + { + amqpvalue_destroy(item_value); + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* initial-response */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + amqp_binary initial_response; + if (amqpvalue_get_binary(item_value, &initial_response) != 0) + { + amqpvalue_destroy(item_value); + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* hostname */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* hostname; + if (amqpvalue_get_string(item_value, &hostname) != 0) + { + amqpvalue_destroy(item_value); + sasl_init_destroy(*sasl_init_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + sasl_init_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int sasl_init_get_mechanism(SASL_INIT_HANDLE sasl_init, const char** mechanism_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + if (amqpvalue_get_composite_item_count(sasl_init_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_init_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_symbol(item_value, mechanism_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_init_set_mechanism(SASL_INIT_HANDLE sasl_init, const char* mechanism_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + AMQP_VALUE mechanism_amqp_value = amqpvalue_create_symbol(mechanism_value); + if (mechanism_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_init_instance->composite_value, 0, mechanism_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(mechanism_amqp_value); + } + } + + return result; +} + +int sasl_init_get_initial_response(SASL_INIT_HANDLE sasl_init, amqp_binary* initial_response_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + if (amqpvalue_get_composite_item_count(sasl_init_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_init_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_binary(item_value, initial_response_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_init_set_initial_response(SASL_INIT_HANDLE sasl_init, amqp_binary initial_response_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + AMQP_VALUE initial_response_amqp_value = amqpvalue_create_binary(initial_response_value); + if (initial_response_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_init_instance->composite_value, 1, initial_response_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(initial_response_amqp_value); + } + } + + return result; +} + +int sasl_init_get_hostname(SASL_INIT_HANDLE sasl_init, const char** hostname_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + if (amqpvalue_get_composite_item_count(sasl_init_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_init_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, hostname_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_init_set_hostname(SASL_INIT_HANDLE sasl_init, const char* hostname_value) +{ + int result; + + if (sasl_init == NULL) + { + result = __FAILURE__; + } + else + { + SASL_INIT_INSTANCE* sasl_init_instance = (SASL_INIT_INSTANCE*)sasl_init; + AMQP_VALUE hostname_amqp_value = amqpvalue_create_string(hostname_value); + if (hostname_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_init_instance->composite_value, 2, hostname_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(hostname_amqp_value); + } + } + + return result; +} + + +/* sasl-challenge */ + +typedef struct SASL_CHALLENGE_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SASL_CHALLENGE_INSTANCE; + +static SASL_CHALLENGE_HANDLE sasl_challenge_create_internal(void) +{ + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)malloc(sizeof(SASL_CHALLENGE_INSTANCE)); + if (sasl_challenge_instance != NULL) + { + sasl_challenge_instance->composite_value = NULL; + } + + return sasl_challenge_instance; +} + +SASL_CHALLENGE_HANDLE sasl_challenge_create(amqp_binary challenge_value) +{ + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)malloc(sizeof(SASL_CHALLENGE_INSTANCE)); + if (sasl_challenge_instance != NULL) + { + sasl_challenge_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(66); + if (sasl_challenge_instance->composite_value == NULL) + { + free(sasl_challenge_instance); + sasl_challenge_instance = NULL; + } + else + { + AMQP_VALUE challenge_amqp_value; + int result = 0; + + challenge_amqp_value = amqpvalue_create_binary(challenge_value); + if ((result == 0) && (amqpvalue_set_composite_item(sasl_challenge_instance->composite_value, 0, challenge_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(challenge_amqp_value); + } + } + + return sasl_challenge_instance; +} + +SASL_CHALLENGE_HANDLE sasl_challenge_clone(SASL_CHALLENGE_HANDLE value) +{ + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)malloc(sizeof(SASL_CHALLENGE_INSTANCE)); + if (sasl_challenge_instance != NULL) + { + sasl_challenge_instance->composite_value = amqpvalue_clone(((SASL_CHALLENGE_INSTANCE*)value)->composite_value); + if (sasl_challenge_instance->composite_value == NULL) + { + free(sasl_challenge_instance); + sasl_challenge_instance = NULL; + } + } + + return sasl_challenge_instance; +} + +void sasl_challenge_destroy(SASL_CHALLENGE_HANDLE sasl_challenge) +{ + if (sasl_challenge != NULL) + { + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)sasl_challenge; + amqpvalue_destroy(sasl_challenge_instance->composite_value); + free(sasl_challenge_instance); + } +} + +AMQP_VALUE amqpvalue_create_sasl_challenge(SASL_CHALLENGE_HANDLE sasl_challenge) +{ + AMQP_VALUE result; + + if (sasl_challenge == NULL) + { + result = NULL; + } + else + { + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)sasl_challenge; + result = amqpvalue_clone(sasl_challenge_instance->composite_value); + } + + return result; +} + +bool is_sasl_challenge_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 66)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_sasl_challenge(AMQP_VALUE value, SASL_CHALLENGE_HANDLE* sasl_challenge_handle) +{ + int result; + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)sasl_challenge_create_internal(); + *sasl_challenge_handle = sasl_challenge_instance; + if (*sasl_challenge_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + sasl_challenge_destroy(*sasl_challenge_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* challenge */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + sasl_challenge_destroy(*sasl_challenge_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + sasl_challenge_destroy(*sasl_challenge_handle); + result = __FAILURE__; + break; + } + else + { + amqp_binary challenge; + if (amqpvalue_get_binary(item_value, &challenge) != 0) + { + amqpvalue_destroy(item_value); + sasl_challenge_destroy(*sasl_challenge_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + + sasl_challenge_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int sasl_challenge_get_challenge(SASL_CHALLENGE_HANDLE sasl_challenge, amqp_binary* challenge_value) +{ + int result; + + if (sasl_challenge == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)sasl_challenge; + if (amqpvalue_get_composite_item_count(sasl_challenge_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_challenge_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_binary(item_value, challenge_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_challenge_set_challenge(SASL_CHALLENGE_HANDLE sasl_challenge, amqp_binary challenge_value) +{ + int result; + + if (sasl_challenge == NULL) + { + result = __FAILURE__; + } + else + { + SASL_CHALLENGE_INSTANCE* sasl_challenge_instance = (SASL_CHALLENGE_INSTANCE*)sasl_challenge; + AMQP_VALUE challenge_amqp_value = amqpvalue_create_binary(challenge_value); + if (challenge_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_challenge_instance->composite_value, 0, challenge_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(challenge_amqp_value); + } + } + + return result; +} + + +/* sasl-response */ + +typedef struct SASL_RESPONSE_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SASL_RESPONSE_INSTANCE; + +static SASL_RESPONSE_HANDLE sasl_response_create_internal(void) +{ + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)malloc(sizeof(SASL_RESPONSE_INSTANCE)); + if (sasl_response_instance != NULL) + { + sasl_response_instance->composite_value = NULL; + } + + return sasl_response_instance; +} + +SASL_RESPONSE_HANDLE sasl_response_create(amqp_binary response_value) +{ + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)malloc(sizeof(SASL_RESPONSE_INSTANCE)); + if (sasl_response_instance != NULL) + { + sasl_response_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(67); + if (sasl_response_instance->composite_value == NULL) + { + free(sasl_response_instance); + sasl_response_instance = NULL; + } + else + { + AMQP_VALUE response_amqp_value; + int result = 0; + + response_amqp_value = amqpvalue_create_binary(response_value); + if ((result == 0) && (amqpvalue_set_composite_item(sasl_response_instance->composite_value, 0, response_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(response_amqp_value); + } + } + + return sasl_response_instance; +} + +SASL_RESPONSE_HANDLE sasl_response_clone(SASL_RESPONSE_HANDLE value) +{ + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)malloc(sizeof(SASL_RESPONSE_INSTANCE)); + if (sasl_response_instance != NULL) + { + sasl_response_instance->composite_value = amqpvalue_clone(((SASL_RESPONSE_INSTANCE*)value)->composite_value); + if (sasl_response_instance->composite_value == NULL) + { + free(sasl_response_instance); + sasl_response_instance = NULL; + } + } + + return sasl_response_instance; +} + +void sasl_response_destroy(SASL_RESPONSE_HANDLE sasl_response) +{ + if (sasl_response != NULL) + { + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)sasl_response; + amqpvalue_destroy(sasl_response_instance->composite_value); + free(sasl_response_instance); + } +} + +AMQP_VALUE amqpvalue_create_sasl_response(SASL_RESPONSE_HANDLE sasl_response) +{ + AMQP_VALUE result; + + if (sasl_response == NULL) + { + result = NULL; + } + else + { + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)sasl_response; + result = amqpvalue_clone(sasl_response_instance->composite_value); + } + + return result; +} + +bool is_sasl_response_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 67)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_sasl_response(AMQP_VALUE value, SASL_RESPONSE_HANDLE* sasl_response_handle) +{ + int result; + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)sasl_response_create_internal(); + *sasl_response_handle = sasl_response_instance; + if (*sasl_response_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + sasl_response_destroy(*sasl_response_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* response */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + sasl_response_destroy(*sasl_response_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + sasl_response_destroy(*sasl_response_handle); + result = __FAILURE__; + break; + } + else + { + amqp_binary response; + if (amqpvalue_get_binary(item_value, &response) != 0) + { + amqpvalue_destroy(item_value); + sasl_response_destroy(*sasl_response_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + + sasl_response_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int sasl_response_get_response(SASL_RESPONSE_HANDLE sasl_response, amqp_binary* response_value) +{ + int result; + + if (sasl_response == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)sasl_response; + if (amqpvalue_get_composite_item_count(sasl_response_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_response_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_binary(item_value, response_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_response_set_response(SASL_RESPONSE_HANDLE sasl_response, amqp_binary response_value) +{ + int result; + + if (sasl_response == NULL) + { + result = __FAILURE__; + } + else + { + SASL_RESPONSE_INSTANCE* sasl_response_instance = (SASL_RESPONSE_INSTANCE*)sasl_response; + AMQP_VALUE response_amqp_value = amqpvalue_create_binary(response_value); + if (response_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_response_instance->composite_value, 0, response_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(response_amqp_value); + } + } + + return result; +} + + +/* sasl-outcome */ + +typedef struct SASL_OUTCOME_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SASL_OUTCOME_INSTANCE; + +static SASL_OUTCOME_HANDLE sasl_outcome_create_internal(void) +{ + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)malloc(sizeof(SASL_OUTCOME_INSTANCE)); + if (sasl_outcome_instance != NULL) + { + sasl_outcome_instance->composite_value = NULL; + } + + return sasl_outcome_instance; +} + +SASL_OUTCOME_HANDLE sasl_outcome_create(sasl_code code_value) +{ + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)malloc(sizeof(SASL_OUTCOME_INSTANCE)); + if (sasl_outcome_instance != NULL) + { + sasl_outcome_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(68); + if (sasl_outcome_instance->composite_value == NULL) + { + free(sasl_outcome_instance); + sasl_outcome_instance = NULL; + } + else + { + AMQP_VALUE code_amqp_value; + int result = 0; + + code_amqp_value = amqpvalue_create_sasl_code(code_value); + if ((result == 0) && (amqpvalue_set_composite_item(sasl_outcome_instance->composite_value, 0, code_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(code_amqp_value); + } + } + + return sasl_outcome_instance; +} + +SASL_OUTCOME_HANDLE sasl_outcome_clone(SASL_OUTCOME_HANDLE value) +{ + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)malloc(sizeof(SASL_OUTCOME_INSTANCE)); + if (sasl_outcome_instance != NULL) + { + sasl_outcome_instance->composite_value = amqpvalue_clone(((SASL_OUTCOME_INSTANCE*)value)->composite_value); + if (sasl_outcome_instance->composite_value == NULL) + { + free(sasl_outcome_instance); + sasl_outcome_instance = NULL; + } + } + + return sasl_outcome_instance; +} + +void sasl_outcome_destroy(SASL_OUTCOME_HANDLE sasl_outcome) +{ + if (sasl_outcome != NULL) + { + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + amqpvalue_destroy(sasl_outcome_instance->composite_value); + free(sasl_outcome_instance); + } +} + +AMQP_VALUE amqpvalue_create_sasl_outcome(SASL_OUTCOME_HANDLE sasl_outcome) +{ + AMQP_VALUE result; + + if (sasl_outcome == NULL) + { + result = NULL; + } + else + { + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + result = amqpvalue_clone(sasl_outcome_instance->composite_value); + } + + return result; +} + +bool is_sasl_outcome_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 68)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_sasl_outcome(AMQP_VALUE value, SASL_OUTCOME_HANDLE* sasl_outcome_handle) +{ + int result; + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome_create_internal(); + *sasl_outcome_handle = sasl_outcome_instance; + if (*sasl_outcome_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + sasl_outcome_destroy(*sasl_outcome_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* code */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + sasl_outcome_destroy(*sasl_outcome_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + sasl_outcome_destroy(*sasl_outcome_handle); + result = __FAILURE__; + break; + } + else + { + sasl_code code; + if (amqpvalue_get_sasl_code(item_value, &code) != 0) + { + amqpvalue_destroy(item_value); + sasl_outcome_destroy(*sasl_outcome_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* additional-data */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + amqp_binary additional_data; + if (amqpvalue_get_binary(item_value, &additional_data) != 0) + { + amqpvalue_destroy(item_value); + sasl_outcome_destroy(*sasl_outcome_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + sasl_outcome_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int sasl_outcome_get_code(SASL_OUTCOME_HANDLE sasl_outcome, sasl_code* code_value) +{ + int result; + + if (sasl_outcome == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + if (amqpvalue_get_composite_item_count(sasl_outcome_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_outcome_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_sasl_code(item_value, code_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_outcome_set_code(SASL_OUTCOME_HANDLE sasl_outcome, sasl_code code_value) +{ + int result; + + if (sasl_outcome == NULL) + { + result = __FAILURE__; + } + else + { + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + AMQP_VALUE code_amqp_value = amqpvalue_create_sasl_code(code_value); + if (code_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_outcome_instance->composite_value, 0, code_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(code_amqp_value); + } + } + + return result; +} + +int sasl_outcome_get_additional_data(SASL_OUTCOME_HANDLE sasl_outcome, amqp_binary* additional_data_value) +{ + int result; + + if (sasl_outcome == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + if (amqpvalue_get_composite_item_count(sasl_outcome_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(sasl_outcome_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_binary(item_value, additional_data_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int sasl_outcome_set_additional_data(SASL_OUTCOME_HANDLE sasl_outcome, amqp_binary additional_data_value) +{ + int result; + + if (sasl_outcome == NULL) + { + result = __FAILURE__; + } + else + { + SASL_OUTCOME_INSTANCE* sasl_outcome_instance = (SASL_OUTCOME_INSTANCE*)sasl_outcome; + AMQP_VALUE additional_data_amqp_value = amqpvalue_create_binary(additional_data_value); + if (additional_data_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(sasl_outcome_instance->composite_value, 1, additional_data_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(additional_data_amqp_value); + } + } + + return result; +} + + +/* terminus-durability */ + +AMQP_VALUE amqpvalue_create_terminus_durability(terminus_durability value) +{ + return amqpvalue_create_uint(value); +} + +/* terminus-expiry-policy */ + +AMQP_VALUE amqpvalue_create_terminus_expiry_policy(terminus_expiry_policy value) +{ + return amqpvalue_create_symbol(value); +} + +/* node-properties */ + +AMQP_VALUE amqpvalue_create_node_properties(node_properties value) +{ + return amqpvalue_create_fields(value); +} + +/* filter-set */ + +AMQP_VALUE amqpvalue_create_filter_set(AMQP_VALUE value) +{ + return amqpvalue_clone(value); +} + +/* source */ + +typedef struct SOURCE_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} SOURCE_INSTANCE; + +static SOURCE_HANDLE source_create_internal(void) +{ + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)malloc(sizeof(SOURCE_INSTANCE)); + if (source_instance != NULL) + { + source_instance->composite_value = NULL; + } + + return source_instance; +} + +SOURCE_HANDLE source_create(void) +{ + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)malloc(sizeof(SOURCE_INSTANCE)); + if (source_instance != NULL) + { + source_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(40); + if (source_instance->composite_value == NULL) + { + free(source_instance); + source_instance = NULL; + } + } + + return source_instance; +} + +SOURCE_HANDLE source_clone(SOURCE_HANDLE value) +{ + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)malloc(sizeof(SOURCE_INSTANCE)); + if (source_instance != NULL) + { + source_instance->composite_value = amqpvalue_clone(((SOURCE_INSTANCE*)value)->composite_value); + if (source_instance->composite_value == NULL) + { + free(source_instance); + source_instance = NULL; + } + } + + return source_instance; +} + +void source_destroy(SOURCE_HANDLE source) +{ + if (source != NULL) + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + amqpvalue_destroy(source_instance->composite_value); + free(source_instance); + } +} + +AMQP_VALUE amqpvalue_create_source(SOURCE_HANDLE source) +{ + AMQP_VALUE result; + + if (source == NULL) + { + result = NULL; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + result = amqpvalue_clone(source_instance->composite_value); + } + + return result; +} + +bool is_source_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 40)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_source(AMQP_VALUE value, SOURCE_HANDLE* source_handle) +{ + int result; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source_create_internal(); + *source_handle = source_instance; + if (*source_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + source_destroy(*source_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* address */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* durable */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + terminus_durability durable; + if (amqpvalue_get_terminus_durability(item_value, &durable) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* expiry-policy */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + terminus_expiry_policy expiry_policy; + if (amqpvalue_get_terminus_expiry_policy(item_value, &expiry_policy) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* timeout */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + seconds timeout; + if (amqpvalue_get_seconds(item_value, &timeout) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* dynamic */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool dynamic; + if (amqpvalue_get_boolean(item_value, &dynamic) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* dynamic-node-properties */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + node_properties dynamic_node_properties; + if (amqpvalue_get_node_properties(item_value, &dynamic_node_properties) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* distribution-mode */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* distribution_mode; + if (amqpvalue_get_symbol(item_value, &distribution_mode) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* filter */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + filter_set filter; + if (amqpvalue_get_filter_set(item_value, &filter) != 0) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* default-outcome */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* outcomes */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* outcomes = NULL; + AMQP_VALUE outcomes_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &outcomes_array) != 0)) && + (amqpvalue_get_symbol(item_value, &outcomes) != 0)) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* capabilities */ + if (list_item_count > 10) + { + item_value = amqpvalue_get_list_item(list_value, 10); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* capabilities = NULL; + AMQP_VALUE capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &capabilities) != 0)) + { + amqpvalue_destroy(item_value); + source_destroy(*source_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + source_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int source_get_address(SOURCE_HANDLE source, AMQP_VALUE* address_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *address_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int source_set_address(SOURCE_HANDLE source, AMQP_VALUE address_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE address_amqp_value; + if (address_value == NULL) + { + address_amqp_value = NULL; + } + else + { + address_amqp_value = amqpvalue_clone(address_value); + } + if (address_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 0, address_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(address_amqp_value); + } + } + + return result; +} + +int source_get_durable(SOURCE_HANDLE source, terminus_durability* durable_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + *durable_value = terminus_durability_none; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *durable_value = terminus_durability_none; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_terminus_durability(item_value, durable_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *durable_value = terminus_durability_none; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_durable(SOURCE_HANDLE source, terminus_durability durable_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE durable_amqp_value = amqpvalue_create_terminus_durability(durable_value); + if (durable_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 1, durable_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(durable_amqp_value); + } + } + + return result; +} + +int source_get_expiry_policy(SOURCE_HANDLE source, terminus_expiry_policy* expiry_policy_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_terminus_expiry_policy(item_value, expiry_policy_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_expiry_policy(SOURCE_HANDLE source, terminus_expiry_policy expiry_policy_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE expiry_policy_amqp_value = amqpvalue_create_terminus_expiry_policy(expiry_policy_value); + if (expiry_policy_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 2, expiry_policy_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(expiry_policy_amqp_value); + } + } + + return result; +} + +int source_get_timeout(SOURCE_HANDLE source, seconds* timeout_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *timeout_value = 0; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *timeout_value = 0; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_seconds(item_value, timeout_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *timeout_value = 0; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_timeout(SOURCE_HANDLE source, seconds timeout_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE timeout_amqp_value = amqpvalue_create_seconds(timeout_value); + if (timeout_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 3, timeout_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(timeout_amqp_value); + } + } + + return result; +} + +int source_get_dynamic(SOURCE_HANDLE source, bool* dynamic_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + *dynamic_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *dynamic_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, dynamic_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *dynamic_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_dynamic(SOURCE_HANDLE source, bool dynamic_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE dynamic_amqp_value = amqpvalue_create_boolean(dynamic_value); + if (dynamic_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 4, dynamic_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(dynamic_amqp_value); + } + } + + return result; +} + +int source_get_dynamic_node_properties(SOURCE_HANDLE source, node_properties* dynamic_node_properties_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_node_properties(item_value, dynamic_node_properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_dynamic_node_properties(SOURCE_HANDLE source, node_properties dynamic_node_properties_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE dynamic_node_properties_amqp_value = amqpvalue_create_node_properties(dynamic_node_properties_value); + if (dynamic_node_properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 5, dynamic_node_properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(dynamic_node_properties_amqp_value); + } + } + + return result; +} + +int source_get_distribution_mode(SOURCE_HANDLE source, const char** distribution_mode_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_symbol(item_value, distribution_mode_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_distribution_mode(SOURCE_HANDLE source, const char* distribution_mode_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE distribution_mode_amqp_value = amqpvalue_create_symbol(distribution_mode_value); + if (distribution_mode_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 6, distribution_mode_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(distribution_mode_amqp_value); + } + } + + return result; +} + +int source_get_filter(SOURCE_HANDLE source, filter_set* filter_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_filter_set(item_value, filter_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int source_set_filter(SOURCE_HANDLE source, filter_set filter_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE filter_amqp_value = amqpvalue_create_filter_set(filter_value); + if (filter_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 7, filter_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(filter_amqp_value); + } + } + + return result; +} + +int source_get_default_outcome(SOURCE_HANDLE source, AMQP_VALUE* default_outcome_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *default_outcome_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int source_set_default_outcome(SOURCE_HANDLE source, AMQP_VALUE default_outcome_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE default_outcome_amqp_value; + if (default_outcome_value == NULL) + { + default_outcome_amqp_value = NULL; + } + else + { + default_outcome_amqp_value = amqpvalue_clone(default_outcome_value); + } + if (default_outcome_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 8, default_outcome_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(default_outcome_amqp_value); + } + } + + return result; +} + +int source_get_outcomes(SOURCE_HANDLE source, AMQP_VALUE* outcomes_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* outcomes_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &outcomes_single_value); + } + else + { + (void)memset((void*)&outcomes_single_value, 0, sizeof(outcomes_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, outcomes_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *outcomes_value = amqpvalue_create_array(); + if (*outcomes_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(outcomes_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*outcomes_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*outcomes_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*outcomes_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 9, *outcomes_value) != 0) + { + amqpvalue_destroy(*outcomes_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*outcomes_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int source_set_outcomes(SOURCE_HANDLE source, AMQP_VALUE outcomes_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE outcomes_amqp_value; + if (outcomes_value == NULL) + { + outcomes_amqp_value = NULL; + } + else + { + outcomes_amqp_value = amqpvalue_clone(outcomes_value); + } + if (outcomes_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 9, outcomes_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(outcomes_amqp_value); + } + } + + return result; +} + +int source_get_capabilities(SOURCE_HANDLE source, AMQP_VALUE* capabilities_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + if (amqpvalue_get_composite_item_count(source_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 10) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(source_instance->composite_value, 10); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &capabilities_single_value); + } + else + { + (void)memset((void*)&capabilities_single_value, 0, sizeof(capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *capabilities_value = amqpvalue_create_array(); + if (*capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 10, *capabilities_value) != 0) + { + amqpvalue_destroy(*capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int source_set_capabilities(SOURCE_HANDLE source, AMQP_VALUE capabilities_value) +{ + int result; + + if (source == NULL) + { + result = __FAILURE__; + } + else + { + SOURCE_INSTANCE* source_instance = (SOURCE_INSTANCE*)source; + AMQP_VALUE capabilities_amqp_value; + if (capabilities_value == NULL) + { + capabilities_amqp_value = NULL; + } + else + { + capabilities_amqp_value = amqpvalue_clone(capabilities_value); + } + if (capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(source_instance->composite_value, 10, capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(capabilities_amqp_value); + } + } + + return result; +} + + +/* target */ + +typedef struct TARGET_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} TARGET_INSTANCE; + +static TARGET_HANDLE target_create_internal(void) +{ + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)malloc(sizeof(TARGET_INSTANCE)); + if (target_instance != NULL) + { + target_instance->composite_value = NULL; + } + + return target_instance; +} + +TARGET_HANDLE target_create(void) +{ + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)malloc(sizeof(TARGET_INSTANCE)); + if (target_instance != NULL) + { + target_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(41); + if (target_instance->composite_value == NULL) + { + free(target_instance); + target_instance = NULL; + } + } + + return target_instance; +} + +TARGET_HANDLE target_clone(TARGET_HANDLE value) +{ + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)malloc(sizeof(TARGET_INSTANCE)); + if (target_instance != NULL) + { + target_instance->composite_value = amqpvalue_clone(((TARGET_INSTANCE*)value)->composite_value); + if (target_instance->composite_value == NULL) + { + free(target_instance); + target_instance = NULL; + } + } + + return target_instance; +} + +void target_destroy(TARGET_HANDLE target) +{ + if (target != NULL) + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + amqpvalue_destroy(target_instance->composite_value); + free(target_instance); + } +} + +AMQP_VALUE amqpvalue_create_target(TARGET_HANDLE target) +{ + AMQP_VALUE result; + + if (target == NULL) + { + result = NULL; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + result = amqpvalue_clone(target_instance->composite_value); + } + + return result; +} + +bool is_target_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 41)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_target(AMQP_VALUE value, TARGET_HANDLE* target_handle) +{ + int result; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target_create_internal(); + *target_handle = target_instance; + if (*target_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + target_destroy(*target_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* address */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* durable */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + terminus_durability durable; + if (amqpvalue_get_terminus_durability(item_value, &durable) != 0) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* expiry-policy */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + terminus_expiry_policy expiry_policy; + if (amqpvalue_get_terminus_expiry_policy(item_value, &expiry_policy) != 0) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* timeout */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + seconds timeout; + if (amqpvalue_get_seconds(item_value, &timeout) != 0) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* dynamic */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool dynamic; + if (amqpvalue_get_boolean(item_value, &dynamic) != 0) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* dynamic-node-properties */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + node_properties dynamic_node_properties; + if (amqpvalue_get_node_properties(item_value, &dynamic_node_properties) != 0) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* capabilities */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* capabilities = NULL; + AMQP_VALUE capabilities_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &capabilities_array) != 0)) && + (amqpvalue_get_symbol(item_value, &capabilities) != 0)) + { + amqpvalue_destroy(item_value); + target_destroy(*target_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + target_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int target_get_address(TARGET_HANDLE target, AMQP_VALUE* address_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *address_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int target_set_address(TARGET_HANDLE target, AMQP_VALUE address_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE address_amqp_value; + if (address_value == NULL) + { + address_amqp_value = NULL; + } + else + { + address_amqp_value = amqpvalue_clone(address_value); + } + if (address_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 0, address_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(address_amqp_value); + } + } + + return result; +} + +int target_get_durable(TARGET_HANDLE target, terminus_durability* durable_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + *durable_value = terminus_durability_none; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *durable_value = terminus_durability_none; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_terminus_durability(item_value, durable_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *durable_value = terminus_durability_none; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int target_set_durable(TARGET_HANDLE target, terminus_durability durable_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE durable_amqp_value = amqpvalue_create_terminus_durability(durable_value); + if (durable_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 1, durable_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(durable_amqp_value); + } + } + + return result; +} + +int target_get_expiry_policy(TARGET_HANDLE target, terminus_expiry_policy* expiry_policy_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_terminus_expiry_policy(item_value, expiry_policy_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *expiry_policy_value = terminus_expiry_policy_session_end; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int target_set_expiry_policy(TARGET_HANDLE target, terminus_expiry_policy expiry_policy_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE expiry_policy_amqp_value = amqpvalue_create_terminus_expiry_policy(expiry_policy_value); + if (expiry_policy_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 2, expiry_policy_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(expiry_policy_amqp_value); + } + } + + return result; +} + +int target_get_timeout(TARGET_HANDLE target, seconds* timeout_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *timeout_value = 0; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *timeout_value = 0; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_seconds(item_value, timeout_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *timeout_value = 0; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int target_set_timeout(TARGET_HANDLE target, seconds timeout_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE timeout_amqp_value = amqpvalue_create_seconds(timeout_value); + if (timeout_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 3, timeout_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(timeout_amqp_value); + } + } + + return result; +} + +int target_get_dynamic(TARGET_HANDLE target, bool* dynamic_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + *dynamic_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *dynamic_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, dynamic_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *dynamic_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int target_set_dynamic(TARGET_HANDLE target, bool dynamic_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE dynamic_amqp_value = amqpvalue_create_boolean(dynamic_value); + if (dynamic_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 4, dynamic_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(dynamic_amqp_value); + } + } + + return result; +} + +int target_get_dynamic_node_properties(TARGET_HANDLE target, node_properties* dynamic_node_properties_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_node_properties(item_value, dynamic_node_properties_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int target_set_dynamic_node_properties(TARGET_HANDLE target, node_properties dynamic_node_properties_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE dynamic_node_properties_amqp_value = amqpvalue_create_node_properties(dynamic_node_properties_value); + if (dynamic_node_properties_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 5, dynamic_node_properties_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(dynamic_node_properties_amqp_value); + } + } + + return result; +} + +int target_get_capabilities(TARGET_HANDLE target, AMQP_VALUE* capabilities_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + if (amqpvalue_get_composite_item_count(target_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(target_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + const char* capabilities_single_value; + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_symbol(item_value, &capabilities_single_value); + } + else + { + (void)memset((void*)&capabilities_single_value, 0, sizeof(capabilities_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, capabilities_value) != 0)) && + (get_single_value_result != 0)) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *capabilities_value = amqpvalue_create_array(); + if (*capabilities_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_symbol(capabilities_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*capabilities_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*capabilities_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*capabilities_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 6, *capabilities_value) != 0) + { + amqpvalue_destroy(*capabilities_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*capabilities_value); + } + } + else + { + result = 0; + } + } + } + } + } + } + + return result; +} + +int target_set_capabilities(TARGET_HANDLE target, AMQP_VALUE capabilities_value) +{ + int result; + + if (target == NULL) + { + result = __FAILURE__; + } + else + { + TARGET_INSTANCE* target_instance = (TARGET_INSTANCE*)target; + AMQP_VALUE capabilities_amqp_value; + if (capabilities_value == NULL) + { + capabilities_amqp_value = NULL; + } + else + { + capabilities_amqp_value = amqpvalue_clone(capabilities_value); + } + if (capabilities_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(target_instance->composite_value, 6, capabilities_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(capabilities_amqp_value); + } + } + + return result; +} + + +/* annotations */ + +AMQP_VALUE amqpvalue_create_annotations(AMQP_VALUE value) +{ + return amqpvalue_clone(value); +} + +/* message-id-ulong */ + +AMQP_VALUE amqpvalue_create_message_id_ulong(message_id_ulong value) +{ + return amqpvalue_create_ulong(value); +} + +/* message-id-uuid */ + +AMQP_VALUE amqpvalue_create_message_id_uuid(message_id_uuid value) +{ + return amqpvalue_create_uuid(value); +} + +/* message-id-binary */ + +AMQP_VALUE amqpvalue_create_message_id_binary(message_id_binary value) +{ + return amqpvalue_create_binary(value); +} + +/* message-id-string */ + +AMQP_VALUE amqpvalue_create_message_id_string(message_id_string value) +{ + return amqpvalue_create_string(value); +} + +/* address-string */ + +AMQP_VALUE amqpvalue_create_address_string(address_string value) +{ + return amqpvalue_create_string(value); +} + +/* header */ + +typedef struct HEADER_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} HEADER_INSTANCE; + +static HEADER_HANDLE header_create_internal(void) +{ + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)malloc(sizeof(HEADER_INSTANCE)); + if (header_instance != NULL) + { + header_instance->composite_value = NULL; + } + + return header_instance; +} + +HEADER_HANDLE header_create(void) +{ + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)malloc(sizeof(HEADER_INSTANCE)); + if (header_instance != NULL) + { + header_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(112); + if (header_instance->composite_value == NULL) + { + free(header_instance); + header_instance = NULL; + } + } + + return header_instance; +} + +HEADER_HANDLE header_clone(HEADER_HANDLE value) +{ + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)malloc(sizeof(HEADER_INSTANCE)); + if (header_instance != NULL) + { + header_instance->composite_value = amqpvalue_clone(((HEADER_INSTANCE*)value)->composite_value); + if (header_instance->composite_value == NULL) + { + free(header_instance); + header_instance = NULL; + } + } + + return header_instance; +} + +void header_destroy(HEADER_HANDLE header) +{ + if (header != NULL) + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + amqpvalue_destroy(header_instance->composite_value); + free(header_instance); + } +} + +AMQP_VALUE amqpvalue_create_header(HEADER_HANDLE header) +{ + AMQP_VALUE result; + + if (header == NULL) + { + result = NULL; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + result = amqpvalue_clone(header_instance->composite_value); + } + + return result; +} + +bool is_header_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 112)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_header(AMQP_VALUE value, HEADER_HANDLE* header_handle) +{ + int result; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header_create_internal(); + *header_handle = header_instance; + if (*header_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + header_destroy(*header_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* durable */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool durable; + if (amqpvalue_get_boolean(item_value, &durable) != 0) + { + amqpvalue_destroy(item_value); + header_destroy(*header_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* priority */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint8_t priority; + if (amqpvalue_get_ubyte(item_value, &priority) != 0) + { + amqpvalue_destroy(item_value); + header_destroy(*header_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* ttl */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + milliseconds ttl; + if (amqpvalue_get_milliseconds(item_value, &ttl) != 0) + { + amqpvalue_destroy(item_value); + header_destroy(*header_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* first-acquirer */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool first_acquirer; + if (amqpvalue_get_boolean(item_value, &first_acquirer) != 0) + { + amqpvalue_destroy(item_value); + header_destroy(*header_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* delivery-count */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + uint32_t delivery_count; + if (amqpvalue_get_uint(item_value, &delivery_count) != 0) + { + amqpvalue_destroy(item_value); + header_destroy(*header_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + header_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int header_get_durable(HEADER_HANDLE header, bool* durable_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + if (amqpvalue_get_composite_item_count(header_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + *durable_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(header_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *durable_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, durable_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *durable_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int header_set_durable(HEADER_HANDLE header, bool durable_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + AMQP_VALUE durable_amqp_value = amqpvalue_create_boolean(durable_value); + if (durable_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(header_instance->composite_value, 0, durable_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(durable_amqp_value); + } + } + + return result; +} + +int header_get_priority(HEADER_HANDLE header, uint8_t* priority_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + if (amqpvalue_get_composite_item_count(header_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + *priority_value = 4; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(header_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *priority_value = 4; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_ubyte(item_value, priority_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *priority_value = 4; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int header_set_priority(HEADER_HANDLE header, uint8_t priority_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + AMQP_VALUE priority_amqp_value = amqpvalue_create_ubyte(priority_value); + if (priority_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(header_instance->composite_value, 1, priority_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(priority_amqp_value); + } + } + + return result; +} + +int header_get_ttl(HEADER_HANDLE header, milliseconds* ttl_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + if (amqpvalue_get_composite_item_count(header_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(header_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_milliseconds(item_value, ttl_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int header_set_ttl(HEADER_HANDLE header, milliseconds ttl_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + AMQP_VALUE ttl_amqp_value = amqpvalue_create_milliseconds(ttl_value); + if (ttl_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(header_instance->composite_value, 2, ttl_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(ttl_amqp_value); + } + } + + return result; +} + +int header_get_first_acquirer(HEADER_HANDLE header, bool* first_acquirer_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + if (amqpvalue_get_composite_item_count(header_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + *first_acquirer_value = false; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(header_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *first_acquirer_value = false; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, first_acquirer_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *first_acquirer_value = false; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int header_set_first_acquirer(HEADER_HANDLE header, bool first_acquirer_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + AMQP_VALUE first_acquirer_amqp_value = amqpvalue_create_boolean(first_acquirer_value); + if (first_acquirer_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(header_instance->composite_value, 3, first_acquirer_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(first_acquirer_amqp_value); + } + } + + return result; +} + +int header_get_delivery_count(HEADER_HANDLE header, uint32_t* delivery_count_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + if (amqpvalue_get_composite_item_count(header_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + *delivery_count_value = 0; + result = 0; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(header_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + *delivery_count_value = 0; + result = 0; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, delivery_count_value); + if (get_single_value_result != 0) + { + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { + *delivery_count_value = 0; + result = 0; + } + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int header_set_delivery_count(HEADER_HANDLE header, uint32_t delivery_count_value) +{ + int result; + + if (header == NULL) + { + result = __FAILURE__; + } + else + { + HEADER_INSTANCE* header_instance = (HEADER_INSTANCE*)header; + AMQP_VALUE delivery_count_amqp_value = amqpvalue_create_uint(delivery_count_value); + if (delivery_count_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(header_instance->composite_value, 4, delivery_count_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(delivery_count_amqp_value); + } + } + + return result; +} + + +/* delivery-annotations */ + +AMQP_VALUE amqpvalue_create_delivery_annotations(delivery_annotations value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_create_annotations(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(113); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_delivery_annotations_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 113)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* message-annotations */ + +AMQP_VALUE amqpvalue_create_message_annotations(message_annotations value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_create_annotations(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(114); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_message_annotations_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 114)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* application-properties */ + +AMQP_VALUE amqpvalue_create_application_properties(AMQP_VALUE value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_clone(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(116); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_application_properties_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 116)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* data */ + +AMQP_VALUE amqpvalue_create_data(data value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_create_binary(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(117); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_data_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 117)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* amqp-sequence */ + +AMQP_VALUE amqpvalue_create_amqp_sequence(AMQP_VALUE value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_clone(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(118); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_amqp_sequence_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 118)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* amqp-value */ + +AMQP_VALUE amqpvalue_create_amqp_value(AMQP_VALUE value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_clone(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(119); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_amqp_value_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 119)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* footer */ + +AMQP_VALUE amqpvalue_create_footer(footer value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_create_annotations(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(120); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_footer_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 120)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/* properties */ + +typedef struct PROPERTIES_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} PROPERTIES_INSTANCE; + +static PROPERTIES_HANDLE properties_create_internal(void) +{ + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)malloc(sizeof(PROPERTIES_INSTANCE)); + if (properties_instance != NULL) + { + properties_instance->composite_value = NULL; + } + + return properties_instance; +} + +PROPERTIES_HANDLE properties_create(void) +{ + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)malloc(sizeof(PROPERTIES_INSTANCE)); + if (properties_instance != NULL) + { + properties_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(115); + if (properties_instance->composite_value == NULL) + { + free(properties_instance); + properties_instance = NULL; + } + } + + return properties_instance; +} + +PROPERTIES_HANDLE properties_clone(PROPERTIES_HANDLE value) +{ + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)malloc(sizeof(PROPERTIES_INSTANCE)); + if (properties_instance != NULL) + { + properties_instance->composite_value = amqpvalue_clone(((PROPERTIES_INSTANCE*)value)->composite_value); + if (properties_instance->composite_value == NULL) + { + free(properties_instance); + properties_instance = NULL; + } + } + + return properties_instance; +} + +void properties_destroy(PROPERTIES_HANDLE properties) +{ + if (properties != NULL) + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + amqpvalue_destroy(properties_instance->composite_value); + free(properties_instance); + } +} + +AMQP_VALUE amqpvalue_create_properties(PROPERTIES_HANDLE properties) +{ + AMQP_VALUE result; + + if (properties == NULL) + { + result = NULL; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + result = amqpvalue_clone(properties_instance->composite_value); + } + + return result; +} + +bool is_properties_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 115)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_properties(AMQP_VALUE value, PROPERTIES_HANDLE* properties_handle) +{ + int result; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties_create_internal(); + *properties_handle = properties_instance; + if (*properties_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + properties_destroy(*properties_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* message-id */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* user-id */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + amqp_binary user_id; + if (amqpvalue_get_binary(item_value, &user_id) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* to */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* subject */ + if (list_item_count > 3) + { + item_value = amqpvalue_get_list_item(list_value, 3); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* subject; + if (amqpvalue_get_string(item_value, &subject) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* reply-to */ + if (list_item_count > 4) + { + item_value = amqpvalue_get_list_item(list_value, 4); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* correlation-id */ + if (list_item_count > 5) + { + item_value = amqpvalue_get_list_item(list_value, 5); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + amqpvalue_destroy(item_value); + } + } + /* content-type */ + if (list_item_count > 6) + { + item_value = amqpvalue_get_list_item(list_value, 6); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* content_type; + if (amqpvalue_get_symbol(item_value, &content_type) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* content-encoding */ + if (list_item_count > 7) + { + item_value = amqpvalue_get_list_item(list_value, 7); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* content_encoding; + if (amqpvalue_get_symbol(item_value, &content_encoding) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* absolute-expiry-time */ + if (list_item_count > 8) + { + item_value = amqpvalue_get_list_item(list_value, 8); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + timestamp absolute_expiry_time; + if (amqpvalue_get_timestamp(item_value, &absolute_expiry_time) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* creation-time */ + if (list_item_count > 9) + { + item_value = amqpvalue_get_list_item(list_value, 9); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + timestamp creation_time; + if (amqpvalue_get_timestamp(item_value, &creation_time) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* group-id */ + if (list_item_count > 10) + { + item_value = amqpvalue_get_list_item(list_value, 10); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* group_id; + if (amqpvalue_get_string(item_value, &group_id) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* group-sequence */ + if (list_item_count > 11) + { + item_value = amqpvalue_get_list_item(list_value, 11); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + sequence_no group_sequence; + if (amqpvalue_get_sequence_no(item_value, &group_sequence) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* reply-to-group-id */ + if (list_item_count > 12) + { + item_value = amqpvalue_get_list_item(list_value, 12); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + const char* reply_to_group_id; + if (amqpvalue_get_string(item_value, &reply_to_group_id) != 0) + { + amqpvalue_destroy(item_value); + properties_destroy(*properties_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + properties_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int properties_get_message_id(PROPERTIES_HANDLE properties, AMQP_VALUE* message_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *message_id_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int properties_set_message_id(PROPERTIES_HANDLE properties, AMQP_VALUE message_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE message_id_amqp_value; + if (message_id_value == NULL) + { + message_id_amqp_value = NULL; + } + else + { + message_id_amqp_value = amqpvalue_clone(message_id_value); + } + if (message_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 0, message_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(message_id_amqp_value); + } + } + + return result; +} + +int properties_get_user_id(PROPERTIES_HANDLE properties, amqp_binary* user_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_binary(item_value, user_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_user_id(PROPERTIES_HANDLE properties, amqp_binary user_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE user_id_amqp_value = amqpvalue_create_binary(user_id_value); + if (user_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 1, user_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(user_id_amqp_value); + } + } + + return result; +} + +int properties_get_to(PROPERTIES_HANDLE properties, AMQP_VALUE* to_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *to_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int properties_set_to(PROPERTIES_HANDLE properties, AMQP_VALUE to_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE to_amqp_value; + if (to_value == NULL) + { + to_amqp_value = NULL; + } + else + { + to_amqp_value = amqpvalue_clone(to_value); + } + if (to_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 2, to_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(to_amqp_value); + } + } + + return result; +} + +int properties_get_subject(PROPERTIES_HANDLE properties, const char** subject_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 3) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 3); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, subject_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_subject(PROPERTIES_HANDLE properties, const char* subject_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE subject_amqp_value = amqpvalue_create_string(subject_value); + if (subject_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 3, subject_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(subject_amqp_value); + } + } + + return result; +} + +int properties_get_reply_to(PROPERTIES_HANDLE properties, AMQP_VALUE* reply_to_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 4) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 4); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *reply_to_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int properties_set_reply_to(PROPERTIES_HANDLE properties, AMQP_VALUE reply_to_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE reply_to_amqp_value; + if (reply_to_value == NULL) + { + reply_to_amqp_value = NULL; + } + else + { + reply_to_amqp_value = amqpvalue_clone(reply_to_value); + } + if (reply_to_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 4, reply_to_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(reply_to_amqp_value); + } + } + + return result; +} + +int properties_get_correlation_id(PROPERTIES_HANDLE properties, AMQP_VALUE* correlation_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 5) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 5); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + *correlation_id_value = item_value; + result = 0; + } + } + } + } + + return result; +} + +int properties_set_correlation_id(PROPERTIES_HANDLE properties, AMQP_VALUE correlation_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE correlation_id_amqp_value; + if (correlation_id_value == NULL) + { + correlation_id_amqp_value = NULL; + } + else + { + correlation_id_amqp_value = amqpvalue_clone(correlation_id_value); + } + if (correlation_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 5, correlation_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(correlation_id_amqp_value); + } + } + + return result; +} + +int properties_get_content_type(PROPERTIES_HANDLE properties, const char** content_type_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 6) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 6); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_symbol(item_value, content_type_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_content_type(PROPERTIES_HANDLE properties, const char* content_type_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE content_type_amqp_value = amqpvalue_create_symbol(content_type_value); + if (content_type_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 6, content_type_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(content_type_amqp_value); + } + } + + return result; +} + +int properties_get_content_encoding(PROPERTIES_HANDLE properties, const char** content_encoding_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 7) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 7); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_symbol(item_value, content_encoding_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_content_encoding(PROPERTIES_HANDLE properties, const char* content_encoding_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE content_encoding_amqp_value = amqpvalue_create_symbol(content_encoding_value); + if (content_encoding_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 7, content_encoding_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(content_encoding_amqp_value); + } + } + + return result; +} + +int properties_get_absolute_expiry_time(PROPERTIES_HANDLE properties, timestamp* absolute_expiry_time_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 8) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 8); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_timestamp(item_value, absolute_expiry_time_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_absolute_expiry_time(PROPERTIES_HANDLE properties, timestamp absolute_expiry_time_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE absolute_expiry_time_amqp_value = amqpvalue_create_timestamp(absolute_expiry_time_value); + if (absolute_expiry_time_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 8, absolute_expiry_time_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(absolute_expiry_time_amqp_value); + } + } + + return result; +} + +int properties_get_creation_time(PROPERTIES_HANDLE properties, timestamp* creation_time_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 9) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 9); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_timestamp(item_value, creation_time_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_creation_time(PROPERTIES_HANDLE properties, timestamp creation_time_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE creation_time_amqp_value = amqpvalue_create_timestamp(creation_time_value); + if (creation_time_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 9, creation_time_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(creation_time_amqp_value); + } + } + + return result; +} + +int properties_get_group_id(PROPERTIES_HANDLE properties, const char** group_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 10) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 10); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, group_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_group_id(PROPERTIES_HANDLE properties, const char* group_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE group_id_amqp_value = amqpvalue_create_string(group_id_value); + if (group_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 10, group_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(group_id_amqp_value); + } + } + + return result; +} + +int properties_get_group_sequence(PROPERTIES_HANDLE properties, sequence_no* group_sequence_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 11) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 11); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_sequence_no(item_value, group_sequence_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_group_sequence(PROPERTIES_HANDLE properties, sequence_no group_sequence_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE group_sequence_amqp_value = amqpvalue_create_sequence_no(group_sequence_value); + if (group_sequence_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 11, group_sequence_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(group_sequence_amqp_value); + } + } + + return result; +} + +int properties_get_reply_to_group_id(PROPERTIES_HANDLE properties, const char** reply_to_group_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + if (amqpvalue_get_composite_item_count(properties_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 12) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(properties_instance->composite_value, 12); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_string(item_value, reply_to_group_id_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int properties_set_reply_to_group_id(PROPERTIES_HANDLE properties, const char* reply_to_group_id_value) +{ + int result; + + if (properties == NULL) + { + result = __FAILURE__; + } + else + { + PROPERTIES_INSTANCE* properties_instance = (PROPERTIES_INSTANCE*)properties; + AMQP_VALUE reply_to_group_id_amqp_value = amqpvalue_create_string(reply_to_group_id_value); + if (reply_to_group_id_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(properties_instance->composite_value, 12, reply_to_group_id_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(reply_to_group_id_amqp_value); + } + } + + return result; +} + + +/* received */ + +typedef struct RECEIVED_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} RECEIVED_INSTANCE; + +static RECEIVED_HANDLE received_create_internal(void) +{ + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)malloc(sizeof(RECEIVED_INSTANCE)); + if (received_instance != NULL) + { + received_instance->composite_value = NULL; + } + + return received_instance; +} + +RECEIVED_HANDLE received_create(uint32_t section_number_value, uint64_t section_offset_value) +{ + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)malloc(sizeof(RECEIVED_INSTANCE)); + if (received_instance != NULL) + { + received_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(35); + if (received_instance->composite_value == NULL) + { + free(received_instance); + received_instance = NULL; + } + else + { + AMQP_VALUE section_number_amqp_value; + AMQP_VALUE section_offset_amqp_value; + int result = 0; + + section_number_amqp_value = amqpvalue_create_uint(section_number_value); + if ((result == 0) && (amqpvalue_set_composite_item(received_instance->composite_value, 0, section_number_amqp_value) != 0)) + { + result = __FAILURE__; + } + section_offset_amqp_value = amqpvalue_create_ulong(section_offset_value); + if ((result == 0) && (amqpvalue_set_composite_item(received_instance->composite_value, 1, section_offset_amqp_value) != 0)) + { + result = __FAILURE__; + } + + amqpvalue_destroy(section_number_amqp_value); + amqpvalue_destroy(section_offset_amqp_value); + } + } + + return received_instance; +} + +RECEIVED_HANDLE received_clone(RECEIVED_HANDLE value) +{ + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)malloc(sizeof(RECEIVED_INSTANCE)); + if (received_instance != NULL) + { + received_instance->composite_value = amqpvalue_clone(((RECEIVED_INSTANCE*)value)->composite_value); + if (received_instance->composite_value == NULL) + { + free(received_instance); + received_instance = NULL; + } + } + + return received_instance; +} + +void received_destroy(RECEIVED_HANDLE received) +{ + if (received != NULL) + { + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + amqpvalue_destroy(received_instance->composite_value); + free(received_instance); + } +} + +AMQP_VALUE amqpvalue_create_received(RECEIVED_HANDLE received) +{ + AMQP_VALUE result; + + if (received == NULL) + { + result = NULL; + } + else + { + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + result = amqpvalue_clone(received_instance->composite_value); + } + + return result; +} + +bool is_received_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 35)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_received(AMQP_VALUE value, RECEIVED_HANDLE* received_handle) +{ + int result; + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received_create_internal(); + *received_handle = received_instance; + if (*received_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + received_destroy(*received_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* section-number */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + { + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + else + { + uint32_t section_number; + if (amqpvalue_get_uint(item_value, §ion_number) != 0) + { + amqpvalue_destroy(item_value); + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + /* section-offset */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + { + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + amqpvalue_destroy(item_value); + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + else + { + uint64_t section_offset; + if (amqpvalue_get_ulong(item_value, §ion_offset) != 0) + { + amqpvalue_destroy(item_value); + received_destroy(*received_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + else + { + result = __FAILURE__; + } + + received_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int received_get_section_number(RECEIVED_HANDLE received, uint32_t* section_number_value) +{ + int result; + + if (received == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + if (amqpvalue_get_composite_item_count(received_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(received_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_uint(item_value, section_number_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int received_set_section_number(RECEIVED_HANDLE received, uint32_t section_number_value) +{ + int result; + + if (received == NULL) + { + result = __FAILURE__; + } + else + { + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + AMQP_VALUE section_number_amqp_value = amqpvalue_create_uint(section_number_value); + if (section_number_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(received_instance->composite_value, 0, section_number_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(section_number_amqp_value); + } + } + + return result; +} + +int received_get_section_offset(RECEIVED_HANDLE received, uint64_t* section_offset_value) +{ + int result; + + if (received == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + if (amqpvalue_get_composite_item_count(received_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(received_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_ulong(item_value, section_offset_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int received_set_section_offset(RECEIVED_HANDLE received, uint64_t section_offset_value) +{ + int result; + + if (received == NULL) + { + result = __FAILURE__; + } + else + { + RECEIVED_INSTANCE* received_instance = (RECEIVED_INSTANCE*)received; + AMQP_VALUE section_offset_amqp_value = amqpvalue_create_ulong(section_offset_value); + if (section_offset_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(received_instance->composite_value, 1, section_offset_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(section_offset_amqp_value); + } + } + + return result; +} + + +/* accepted */ + +typedef struct ACCEPTED_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} ACCEPTED_INSTANCE; + +static ACCEPTED_HANDLE accepted_create_internal(void) +{ + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)malloc(sizeof(ACCEPTED_INSTANCE)); + if (accepted_instance != NULL) + { + accepted_instance->composite_value = NULL; + } + + return accepted_instance; +} + +ACCEPTED_HANDLE accepted_create(void) +{ + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)malloc(sizeof(ACCEPTED_INSTANCE)); + if (accepted_instance != NULL) + { + accepted_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(36); + if (accepted_instance->composite_value == NULL) + { + free(accepted_instance); + accepted_instance = NULL; + } + } + + return accepted_instance; +} + +ACCEPTED_HANDLE accepted_clone(ACCEPTED_HANDLE value) +{ + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)malloc(sizeof(ACCEPTED_INSTANCE)); + if (accepted_instance != NULL) + { + accepted_instance->composite_value = amqpvalue_clone(((ACCEPTED_INSTANCE*)value)->composite_value); + if (accepted_instance->composite_value == NULL) + { + free(accepted_instance); + accepted_instance = NULL; + } + } + + return accepted_instance; +} + +void accepted_destroy(ACCEPTED_HANDLE accepted) +{ + if (accepted != NULL) + { + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)accepted; + amqpvalue_destroy(accepted_instance->composite_value); + free(accepted_instance); + } +} + +AMQP_VALUE amqpvalue_create_accepted(ACCEPTED_HANDLE accepted) +{ + AMQP_VALUE result; + + if (accepted == NULL) + { + result = NULL; + } + else + { + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)accepted; + result = amqpvalue_clone(accepted_instance->composite_value); + } + + return result; +} + +bool is_accepted_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 36)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_accepted(AMQP_VALUE value, ACCEPTED_HANDLE* accepted_handle) +{ + int result; + ACCEPTED_INSTANCE* accepted_instance = (ACCEPTED_INSTANCE*)accepted_create_internal(); + *accepted_handle = accepted_instance; + if (*accepted_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + accepted_destroy(*accepted_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + + accepted_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + + +/* rejected */ + +typedef struct REJECTED_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} REJECTED_INSTANCE; + +static REJECTED_HANDLE rejected_create_internal(void) +{ + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)malloc(sizeof(REJECTED_INSTANCE)); + if (rejected_instance != NULL) + { + rejected_instance->composite_value = NULL; + } + + return rejected_instance; +} + +REJECTED_HANDLE rejected_create(void) +{ + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)malloc(sizeof(REJECTED_INSTANCE)); + if (rejected_instance != NULL) + { + rejected_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(37); + if (rejected_instance->composite_value == NULL) + { + free(rejected_instance); + rejected_instance = NULL; + } + } + + return rejected_instance; +} + +REJECTED_HANDLE rejected_clone(REJECTED_HANDLE value) +{ + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)malloc(sizeof(REJECTED_INSTANCE)); + if (rejected_instance != NULL) + { + rejected_instance->composite_value = amqpvalue_clone(((REJECTED_INSTANCE*)value)->composite_value); + if (rejected_instance->composite_value == NULL) + { + free(rejected_instance); + rejected_instance = NULL; + } + } + + return rejected_instance; +} + +void rejected_destroy(REJECTED_HANDLE rejected) +{ + if (rejected != NULL) + { + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)rejected; + amqpvalue_destroy(rejected_instance->composite_value); + free(rejected_instance); + } +} + +AMQP_VALUE amqpvalue_create_rejected(REJECTED_HANDLE rejected) +{ + AMQP_VALUE result; + + if (rejected == NULL) + { + result = NULL; + } + else + { + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)rejected; + result = amqpvalue_clone(rejected_instance->composite_value); + } + + return result; +} + +bool is_rejected_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 37)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_rejected(AMQP_VALUE value, REJECTED_HANDLE* rejected_handle) +{ + int result; + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)rejected_create_internal(); + *rejected_handle = rejected_instance; + if (*rejected_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + rejected_destroy(*rejected_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* error */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + ERROR_HANDLE error; + if (amqpvalue_get_error(item_value, &error) != 0) + { + amqpvalue_destroy(item_value); + rejected_destroy(*rejected_handle); + result = __FAILURE__; + break; + } + else + { + error_destroy(error); + } + } + + amqpvalue_destroy(item_value); + } + } + + rejected_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int rejected_get_error(REJECTED_HANDLE rejected, ERROR_HANDLE* error_value) +{ + int result; + + if (rejected == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)rejected; + if (amqpvalue_get_composite_item_count(rejected_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(rejected_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_error(item_value, error_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int rejected_set_error(REJECTED_HANDLE rejected, ERROR_HANDLE error_value) +{ + int result; + + if (rejected == NULL) + { + result = __FAILURE__; + } + else + { + REJECTED_INSTANCE* rejected_instance = (REJECTED_INSTANCE*)rejected; + AMQP_VALUE error_amqp_value = amqpvalue_create_error(error_value); + if (error_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(rejected_instance->composite_value, 0, error_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(error_amqp_value); + } + } + + return result; +} + + +/* released */ + +typedef struct RELEASED_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} RELEASED_INSTANCE; + +static RELEASED_HANDLE released_create_internal(void) +{ + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)malloc(sizeof(RELEASED_INSTANCE)); + if (released_instance != NULL) + { + released_instance->composite_value = NULL; + } + + return released_instance; +} + +RELEASED_HANDLE released_create(void) +{ + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)malloc(sizeof(RELEASED_INSTANCE)); + if (released_instance != NULL) + { + released_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(38); + if (released_instance->composite_value == NULL) + { + free(released_instance); + released_instance = NULL; + } + } + + return released_instance; +} + +RELEASED_HANDLE released_clone(RELEASED_HANDLE value) +{ + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)malloc(sizeof(RELEASED_INSTANCE)); + if (released_instance != NULL) + { + released_instance->composite_value = amqpvalue_clone(((RELEASED_INSTANCE*)value)->composite_value); + if (released_instance->composite_value == NULL) + { + free(released_instance); + released_instance = NULL; + } + } + + return released_instance; +} + +void released_destroy(RELEASED_HANDLE released) +{ + if (released != NULL) + { + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)released; + amqpvalue_destroy(released_instance->composite_value); + free(released_instance); + } +} + +AMQP_VALUE amqpvalue_create_released(RELEASED_HANDLE released) +{ + AMQP_VALUE result; + + if (released == NULL) + { + result = NULL; + } + else + { + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)released; + result = amqpvalue_clone(released_instance->composite_value); + } + + return result; +} + +bool is_released_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 38)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_released(AMQP_VALUE value, RELEASED_HANDLE* released_handle) +{ + int result; + RELEASED_INSTANCE* released_instance = (RELEASED_INSTANCE*)released_create_internal(); + *released_handle = released_instance; + if (*released_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + released_destroy(*released_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + + released_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + + +/* modified */ + +typedef struct MODIFIED_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} MODIFIED_INSTANCE; + +static MODIFIED_HANDLE modified_create_internal(void) +{ + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)malloc(sizeof(MODIFIED_INSTANCE)); + if (modified_instance != NULL) + { + modified_instance->composite_value = NULL; + } + + return modified_instance; +} + +MODIFIED_HANDLE modified_create(void) +{ + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)malloc(sizeof(MODIFIED_INSTANCE)); + if (modified_instance != NULL) + { + modified_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(39); + if (modified_instance->composite_value == NULL) + { + free(modified_instance); + modified_instance = NULL; + } + } + + return modified_instance; +} + +MODIFIED_HANDLE modified_clone(MODIFIED_HANDLE value) +{ + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)malloc(sizeof(MODIFIED_INSTANCE)); + if (modified_instance != NULL) + { + modified_instance->composite_value = amqpvalue_clone(((MODIFIED_INSTANCE*)value)->composite_value); + if (modified_instance->composite_value == NULL) + { + free(modified_instance); + modified_instance = NULL; + } + } + + return modified_instance; +} + +void modified_destroy(MODIFIED_HANDLE modified) +{ + if (modified != NULL) + { + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + amqpvalue_destroy(modified_instance->composite_value); + free(modified_instance); + } +} + +AMQP_VALUE amqpvalue_create_modified(MODIFIED_HANDLE modified) +{ + AMQP_VALUE result; + + if (modified == NULL) + { + result = NULL; + } + else + { + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + result = amqpvalue_clone(modified_instance->composite_value); + } + + return result; +} + +bool is_modified_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == 39)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_modified(AMQP_VALUE value, MODIFIED_HANDLE* modified_handle) +{ + int result; + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified_create_internal(); + *modified_handle = modified_instance; + if (*modified_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + modified_destroy(*modified_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { + AMQP_VALUE item_value; + /* delivery-failed */ + if (list_item_count > 0) + { + item_value = amqpvalue_get_list_item(list_value, 0); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool delivery_failed; + if (amqpvalue_get_boolean(item_value, &delivery_failed) != 0) + { + amqpvalue_destroy(item_value); + modified_destroy(*modified_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* undeliverable-here */ + if (list_item_count > 1) + { + item_value = amqpvalue_get_list_item(list_value, 1); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + bool undeliverable_here; + if (amqpvalue_get_boolean(item_value, &undeliverable_here) != 0) + { + amqpvalue_destroy(item_value); + modified_destroy(*modified_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + /* message-annotations */ + if (list_item_count > 2) + { + item_value = amqpvalue_get_list_item(list_value, 2); + if (item_value == NULL) + { + /* do nothing */ + } + else + { + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { + /* no error, field is not mandatory */ + } + else + { + fields message_annotations; + if (amqpvalue_get_fields(item_value, &message_annotations) != 0) + { + amqpvalue_destroy(item_value); + modified_destroy(*modified_handle); + result = __FAILURE__; + break; + } + } + + amqpvalue_destroy(item_value); + } + } + + modified_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +int modified_get_delivery_failed(MODIFIED_HANDLE modified, bool* delivery_failed_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + if (amqpvalue_get_composite_item_count(modified_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(modified_instance->composite_value, 0); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, delivery_failed_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int modified_set_delivery_failed(MODIFIED_HANDLE modified, bool delivery_failed_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + AMQP_VALUE delivery_failed_amqp_value = amqpvalue_create_boolean(delivery_failed_value); + if (delivery_failed_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(modified_instance->composite_value, 0, delivery_failed_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(delivery_failed_amqp_value); + } + } + + return result; +} + +int modified_get_undeliverable_here(MODIFIED_HANDLE modified, bool* undeliverable_here_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + if (amqpvalue_get_composite_item_count(modified_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 1) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(modified_instance->composite_value, 1); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_boolean(item_value, undeliverable_here_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int modified_set_undeliverable_here(MODIFIED_HANDLE modified, bool undeliverable_here_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + AMQP_VALUE undeliverable_here_amqp_value = amqpvalue_create_boolean(undeliverable_here_value); + if (undeliverable_here_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(modified_instance->composite_value, 1, undeliverable_here_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(undeliverable_here_amqp_value); + } + } + + return result; +} + +int modified_get_message_annotations(MODIFIED_HANDLE modified, fields* message_annotations_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + if (amqpvalue_get_composite_item_count(modified_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= 2) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(modified_instance->composite_value, 2); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { + result = __FAILURE__; + } + else + { + int get_single_value_result = amqpvalue_get_fields(item_value, message_annotations_value); + if (get_single_value_result != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +int modified_set_message_annotations(MODIFIED_HANDLE modified, fields message_annotations_value) +{ + int result; + + if (modified == NULL) + { + result = __FAILURE__; + } + else + { + MODIFIED_INSTANCE* modified_instance = (MODIFIED_INSTANCE*)modified; + AMQP_VALUE message_annotations_amqp_value = amqpvalue_create_fields(message_annotations_value); + if (message_annotations_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(modified_instance->composite_value, 2, message_annotations_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(message_annotations_amqp_value); + } + } + + return result; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/amqp_frame_codec.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/amqpvalue.h" + +typedef enum AMQP_FRAME_DECODE_STATE_TAG +{ + AMQP_FRAME_DECODE_FRAME, + AMQP_FRAME_DECODE_ERROR +} AMQP_FRAME_DECODE_STATE; + +typedef struct AMQP_FRAME_CODEC_TAG +{ + FRAME_CODEC_HANDLE frame_codec; + + /* decode */ + AMQP_FRAME_RECEIVED_CALLBACK frame_received_callback; + AMQP_EMPTY_FRAME_RECEIVED_CALLBACK empty_frame_received_callback; + AMQP_FRAME_CODEC_ERROR_CALLBACK error_callback; + void* callback_context; + AMQPVALUE_DECODER_HANDLE decoder; + AMQP_FRAME_DECODE_STATE decode_state; + AMQP_VALUE decoded_performative; +} AMQP_FRAME_CODEC; + +static void amqp_value_decoded(void* context, AMQP_VALUE decoded_value) +{ + AMQP_FRAME_CODEC_HANDLE amqp_frame_codec = (AMQP_FRAME_CODEC_HANDLE)context; + uint64_t performative_descriptor_ulong; + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(decoded_value); + + /* Codes_SRS_AMQP_FRAME_CODEC_01_060: [If any error occurs while decoding a frame, the decoder shall switch to an error state where decoding shall not be possible anymore.] */ + if ((descriptor == NULL) || + (amqpvalue_get_ulong(descriptor, &performative_descriptor_ulong) != 0) || + /* Codes_SRS_AMQP_FRAME_CODEC_01_003: [The performative MUST be one of those defined in section 2.7 and is encoded as a described type in the AMQP type system.] */ + (performative_descriptor_ulong < AMQP_OPEN) || + (performative_descriptor_ulong > AMQP_CLOSE)) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_060: [If any error occurs while decoding a frame, the decoder shall switch to an error state where decoding shall not be possible anymore.] */ + amqp_frame_codec->decode_state = AMQP_FRAME_DECODE_ERROR; + } + else + { + amqp_frame_codec->decoded_performative = decoded_value; + } +} + +static void frame_received(void* context, const unsigned char* type_specific, uint32_t type_specific_size, const unsigned char* frame_body, uint32_t frame_body_size) +{ + AMQP_FRAME_CODEC_HANDLE amqp_frame_codec = (AMQP_FRAME_CODEC_HANDLE)context; + uint16_t channel; + + switch (amqp_frame_codec->decode_state) + { + default: + /* Codes_SRS_AMQP_FRAME_CODEC_01_050: [All subsequent decoding shall fail and no AMQP frames shall be indicated from that point on to the consumers of amqp_frame_codec.] */ + case AMQP_FRAME_DECODE_ERROR: + break; + + case AMQP_FRAME_DECODE_FRAME: + /* Codes_SRS_AMQP_FRAME_CODEC_01_049: [If not enough type specific bytes are received to decode the channel number, the decoding shall stop with an error.] */ + if (type_specific_size < 2) + { + amqp_frame_codec->decode_state = AMQP_FRAME_DECODE_ERROR; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_069: [If any error occurs while decoding a frame, the decoder shall indicate the error by calling the amqp_frame_codec_error_callback and passing to it the callback context argument that was given in amqp_frame_codec_create.] */ + amqp_frame_codec->error_callback(amqp_frame_codec->callback_context); + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_001: [Bytes 6 and 7 of an AMQP frame contain the channel number ] */ + channel = ((uint16_t)type_specific[0]) << 8; + channel += type_specific[1]; + + if (frame_body_size == 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_048: [When a frame header is received from frame_codec and the frame payload size is 0, empty_frame_received_callback shall be invoked, while passing the channel number as argument.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_007: [An AMQP frame with no body MAY be used to generate artificial traffic as needed to satisfy any negotiated idle timeout interval ] */ + amqp_frame_codec->empty_frame_received_callback(amqp_frame_codec->callback_context, channel); + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_051: [If the frame payload is greater than 0, amqp_frame_codec shall decode the performative as a described AMQP type.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_002: [The frame body is defined as a performative followed by an opaque payload.] */ + amqp_frame_codec->decoded_performative = NULL; + + while ((frame_body_size > 0) && + (amqp_frame_codec->decoded_performative == NULL) && + (amqp_frame_codec->decode_state != AMQP_FRAME_DECODE_ERROR)) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_052: [Decoding the performative shall be done by feeding the bytes to the decoder create in amqp_frame_codec_create.] */ + if (amqpvalue_decode_bytes(amqp_frame_codec->decoder, frame_body, 1) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_060: [If any error occurs while decoding a frame, the decoder shall switch to an error state where decoding shall not be possible anymore.] */ + amqp_frame_codec->decode_state = AMQP_FRAME_DECODE_ERROR; + } + else + { + frame_body_size--; + frame_body++; + } + } + + if (amqp_frame_codec->decode_state == AMQP_FRAME_DECODE_ERROR) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_069: [If any error occurs while decoding a frame, the decoder shall indicate the error by calling the amqp_frame_codec_error_callback and passing to it the callback context argument that was given in amqp_frame_codec_create.] */ + amqp_frame_codec->error_callback(amqp_frame_codec->callback_context); + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_004: [The remaining bytes in the frame body form the payload for that frame.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_067: [When the performative is decoded, the rest of the frame_bytes shall not be given to the AMQP decoder, but they shall be buffered so that later they are given to the frame_received callback.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_054: [Once the performative is decoded and all frame payload bytes are received, the callback frame_received_callback shall be called.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_068: [A pointer to all the payload bytes shall also be passed to frame_received_callback.] */ + amqp_frame_codec->frame_received_callback(amqp_frame_codec->callback_context, channel, amqp_frame_codec->decoded_performative, frame_body, frame_body_size); + } + } + } + break; + } +} + +static int encode_bytes(void* context, const unsigned char* bytes, size_t length) +{ + PAYLOAD* payload = (PAYLOAD*)context; + (void)memcpy((unsigned char*)payload->bytes + payload->length, bytes, length); + payload->length += length; + return 0; +} + +/* Codes_SRS_AMQP_FRAME_CODEC_01_011: [amqp_frame_codec_create shall create an instance of an amqp_frame_codec and return a non-NULL handle to it.] */ +AMQP_FRAME_CODEC_HANDLE amqp_frame_codec_create(FRAME_CODEC_HANDLE frame_codec, AMQP_FRAME_RECEIVED_CALLBACK frame_received_callback, + AMQP_EMPTY_FRAME_RECEIVED_CALLBACK empty_frame_received_callback, AMQP_FRAME_CODEC_ERROR_CALLBACK amqp_frame_codec_error_callback, void* callback_context) +{ + AMQP_FRAME_CODEC_HANDLE result; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_012: [If any of the arguments frame_codec, frame_received_callback, amqp_frame_codec_error_callback or empty_frame_received_callback is NULL, amqp_frame_codec_create shall return NULL.] */ + if ((frame_codec == NULL) || + (frame_received_callback == NULL) || + (empty_frame_received_callback == NULL) || + (amqp_frame_codec_error_callback == NULL)) + { + LogError("Bad arguments: frame_codec = %p, frame_received_callback = %p, empty_frame_received_callback = %p, amqp_frame_codec_error_callback = %p", + frame_codec, frame_received_callback, empty_frame_received_callback, amqp_frame_codec_error_callback); + result = NULL; + } + else + { + result = (AMQP_FRAME_CODEC_HANDLE)malloc(sizeof(AMQP_FRAME_CODEC)); + /* Codes_SRS_AMQP_FRAME_CODEC_01_020: [If allocating memory for the new amqp_frame_codec fails, then amqp_frame_codec_create shall fail and return NULL.] */ + if (result == NULL) + { + LogError("Could not allocate memory for AMQP frame codec"); + } + else + { + result->frame_codec = frame_codec; + result->frame_received_callback = frame_received_callback; + result->empty_frame_received_callback = empty_frame_received_callback; + result->error_callback = amqp_frame_codec_error_callback; + result->callback_context = callback_context; + result->decode_state = AMQP_FRAME_DECODE_FRAME; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_018: [amqp_frame_codec_create shall create a decoder to be used for decoding AMQP values.] */ + result->decoder = amqpvalue_decoder_create(amqp_value_decoded, result); + if (result->decoder == NULL) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_019: [If creating the decoder fails, amqp_frame_codec_create shall fail and return NULL.] */ + LogError("Could not create AMQP decoder"); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_013: [amqp_frame_codec_create shall subscribe for AMQP frames with the given frame_codec.] */ + if (frame_codec_subscribe(frame_codec, FRAME_TYPE_AMQP, frame_received, result) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_014: [If subscribing for AMQP frames fails, amqp_frame_codec_create shall fail and return NULL.] */ + LogError("Could not subscribe for received AMQP frames"); + amqpvalue_decoder_destroy(result->decoder); + free(result); + result = NULL; + } + } + } + } + + return result; +} + +void amqp_frame_codec_destroy(AMQP_FRAME_CODEC_HANDLE amqp_frame_codec) +{ + if (amqp_frame_codec == NULL) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_016: [If amqp_frame_codec is NULL, amqp_frame_codec_destroy shall do nothing.] */ + LogError("NULL amqp_frame_codec"); + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_017: [amqp_frame_codec_destroy shall unsubscribe from receiving AMQP frames from the frame_codec that was passed to amqp_frame_codec_create.] */ + (void)frame_codec_unsubscribe(amqp_frame_codec->frame_codec, FRAME_TYPE_AMQP); + + /* Codes_SRS_AMQP_FRAME_CODEC_01_021: [The decoder created in amqp_frame_codec_create shall be destroyed by amqp_frame_codec_destroy.] */ + amqpvalue_decoder_destroy(amqp_frame_codec->decoder); + + /* Codes_SRS_AMQP_FRAME_CODEC_01_015: [amqp_frame_codec_destroy shall free all resources associated with the amqp_frame_codec instance.] */ + free(amqp_frame_codec); + } +} + +int amqp_frame_codec_encode_frame(AMQP_FRAME_CODEC_HANDLE amqp_frame_codec, uint16_t channel, AMQP_VALUE performative, const PAYLOAD* payloads, size_t payload_count, ON_BYTES_ENCODED on_bytes_encoded, void* callback_context) +{ + int result; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_024: [If frame_codec, performative or on_bytes_encoded is NULL, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + if ((amqp_frame_codec == NULL) || + (performative == NULL) || + (on_bytes_encoded == NULL)) + { + LogError("Bad arguments: amqp_frame_codec = %p, performative = %p, on_bytes_encoded = %p", + amqp_frame_codec, performative, on_bytes_encoded); + result = __FAILURE__; + } + else + { + AMQP_VALUE descriptor; + uint64_t performative_ulong; + size_t encoded_size; + + if ((descriptor = amqpvalue_get_inplace_descriptor(performative)) == NULL) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_029: [If any error occurs during encoding, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Getting the descriptor failed"); + result = __FAILURE__; + } + else if (amqpvalue_get_ulong(descriptor, &performative_ulong) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_029: [If any error occurs during encoding, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Getting the descriptor ulong failed"); + result = __FAILURE__; + } + /* Codes_SRS_AMQP_FRAME_CODEC_01_008: [The performative MUST be one of those defined in section 2.7 and is encoded as a described type in the AMQP type system.] */ + else if ((performative_ulong < AMQP_OPEN) || + (performative_ulong > AMQP_CLOSE)) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_029: [If any error occurs during encoding, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Bad arguments: amqp_frame_codec = %p, performative = %p, on_bytes_encoded = %p", + amqp_frame_codec, performative, on_bytes_encoded); + result = __FAILURE__; + } + /* Codes_SRS_AMQP_FRAME_CODEC_01_027: [The encoded size of the performative and its fields shall be obtained by calling amqpvalue_get_encoded_size.] */ + else if (amqpvalue_get_encoded_size(performative, &encoded_size) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_029: [If any error occurs during encoding, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Getting the encoded size failed"); + result = __FAILURE__; + } + else + { + unsigned char* amqp_performative_bytes = (unsigned char*)malloc(encoded_size); + if (amqp_performative_bytes == NULL) + { + LogError("Could not allocate performative bytes"); + result = __FAILURE__; + } + else + { + PAYLOAD* new_payloads = (PAYLOAD*)malloc(sizeof(PAYLOAD) * (payload_count + 1)); + if (new_payloads == NULL) + { + LogError("Could not allocate frame payloads"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_070: [The payloads argument for frame_codec_encode_frame shall be made of the payload for the encoded performative and the payloads passed to amqp_frame_codec_encode_frame.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_028: [The encode result for the performative shall be placed in a PAYLOAD structure.] */ + new_payloads[0].bytes = amqp_performative_bytes; + new_payloads[0].length = 0; + + if (payload_count > 0) + { + (void)memcpy(new_payloads + 1, payloads, sizeof(PAYLOAD) * payload_count); + } + + if (amqpvalue_encode(performative, encode_bytes, &new_payloads[0]) != 0) + { + LogError("amqpvalue_encode failed"); + result = __FAILURE__; + } + else + { + unsigned char channel_bytes[2]; + + channel_bytes[0] = channel >> 8; + channel_bytes[1] = channel & 0xFF; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_005: [Bytes 6 and 7 of an AMQP frame contain the channel number ] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_025: [amqp_frame_codec_encode_frame shall encode the frame header by using frame_codec_encode_frame.] */ + /* Codes_SRS_AMQP_FRAME_CODEC_01_006: [The frame body is defined as a performative followed by an opaque payload.] */ + if (frame_codec_encode_frame(amqp_frame_codec->frame_codec, FRAME_TYPE_AMQP, new_payloads, payload_count + 1, channel_bytes, sizeof(channel_bytes), on_bytes_encoded, callback_context) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_029: [If any error occurs during encoding, amqp_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("frame_codec_encode_frame failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_022: [amqp_frame_codec_begin_encode_frame shall encode the frame header and AMQP performative in an AMQP frame and on success it shall return 0.] */ + result = 0; + } + } + + free(new_payloads); + } + + free(amqp_performative_bytes); + } + } + } + + return result; +} + +/* Codes_SRS_AMQP_FRAME_CODEC_01_042: [amqp_frame_codec_encode_empty_frame shall encode a frame with no payload.] */ +/* Codes_SRS_AMQP_FRAME_CODEC_01_010: [An AMQP frame with no body MAY be used to generate artificial traffic as needed to satisfy any negotiated idle timeout interval ] */ +int amqp_frame_codec_encode_empty_frame(AMQP_FRAME_CODEC_HANDLE amqp_frame_codec, uint16_t channel, ON_BYTES_ENCODED on_bytes_encoded, void* callback_context) +{ + int result; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_045: [If amqp_frame_codec is NULL, amqp_frame_codec_encode_empty_frame shall fail and return a non-zero value.] */ + if (amqp_frame_codec == NULL) + { + LogError("NULL amqp_frame_codec"); + result = __FAILURE__; + } + else + { + unsigned char channel_bytes[2]; + + channel_bytes[0] = channel >> 8; + channel_bytes[1] = channel & 0xFF; + + /* Codes_SRS_AMQP_FRAME_CODEC_01_044: [amqp_frame_codec_encode_empty_frame shall use frame_codec_encode_frame to encode the frame.] */ + if (frame_codec_encode_frame(amqp_frame_codec->frame_codec, FRAME_TYPE_AMQP, NULL, 0, channel_bytes, sizeof(channel_bytes), on_bytes_encoded, callback_context) != 0) + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_046: [If encoding fails in any way, amqp_frame_codec_encode_empty_frame shall fail and return a non-zero value.] */ + LogError("frame_codec_encode_frame failed when encoding empty frame"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_FRAME_CODEC_01_043: [On success, amqp_frame_codec_encode_empty_frame shall return 0.] */ + result = 0; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/amqp_management.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1327 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_uamqp_c/amqp_management.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/messaging.h" +#include "azure_uamqp_c/amqp_definitions.h" + +static const char sender_suffix[] = "-sender"; +static const char receiver_suffix[] = "-receiver"; + +#define COUNT_CHARS(str) (sizeof(str) / sizeof((str)[0]) - 1) + +typedef struct OPERATION_MESSAGE_INSTANCE_TAG +{ + ON_AMQP_MANAGEMENT_EXECUTE_OPERATION_COMPLETE on_execute_operation_complete; + void* callback_context; + uint64_t message_id; + AMQP_MANAGEMENT_HANDLE amqp_management; +} OPERATION_MESSAGE_INSTANCE; + +typedef enum AMQP_MANAGEMENT_STATE_TAG +{ + AMQP_MANAGEMENT_STATE_IDLE, + AMQP_MANAGEMENT_STATE_OPENING, + AMQP_MANAGEMENT_STATE_CLOSING, + AMQP_MANAGEMENT_STATE_OPEN, + AMQP_MANAGEMENT_STATE_ERROR +} AMQP_MANAGEMENT_STATE; + +typedef struct AMQP_MANAGEMENT_INSTANCE_TAG +{ + LINK_HANDLE sender_link; + LINK_HANDLE receiver_link; + MESSAGE_SENDER_HANDLE message_sender; + MESSAGE_RECEIVER_HANDLE message_receiver; + SINGLYLINKEDLIST_HANDLE pending_operations; + uint64_t next_message_id; + ON_AMQP_MANAGEMENT_OPEN_COMPLETE on_amqp_management_open_complete; + void* on_amqp_management_open_complete_context; + ON_AMQP_MANAGEMENT_ERROR on_amqp_management_error; + void* on_amqp_management_error_context; + AMQP_MANAGEMENT_STATE amqp_management_state; + char* status_code_key_name; + char* status_description_key_name; + unsigned int sender_connected : 1; + unsigned int receiver_connected : 1; +} AMQP_MANAGEMENT_INSTANCE; + +static AMQP_VALUE on_message_received(const void* context, MESSAGE_HANDLE message) +{ + AMQP_VALUE result; + + if (context == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_108: [ When `on_message_received` is called with a NULL context, it shall do nothing. ]*/ + LogError("NULL context in on_message_received"); + result = NULL; + } + else + { + AMQP_MANAGEMENT_HANDLE amqp_management = (AMQP_MANAGEMENT_HANDLE)context; + AMQP_VALUE application_properties; + + /* Codes_SRS_AMQP_MANAGEMENT_01_109: [ `on_message_received` shall obtain the application properties from the message by calling `message_get_application_properties`. ]*/ + if (message_get_application_properties(message, &application_properties) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_113: [ If obtaining the application properties or message properties fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve application properties"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not get application properties on AMQP management response."); + } + else + { + PROPERTIES_HANDLE response_properties; + + /* Codes_SRS_AMQP_MANAGEMENT_01_110: [ `on_message_received` shall obtain the message properties from the message by calling `message_get_properties`. ]*/ + if (message_get_properties(message, &response_properties) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_113: [ If obtaining the application properties or message properties fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve message properties"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not get message properties on AMQP management response."); + } + else + { + AMQP_VALUE key; + AMQP_VALUE value; + AMQP_VALUE desc_key; + AMQP_VALUE desc_value; + AMQP_VALUE map; + AMQP_VALUE correlation_id_value; + uint64_t correlation_id; + + /* Codes_SRS_AMQP_MANAGEMENT_01_111: [ `on_message_received` shall obtain the correlation Id from the message properties by using `properties_get_correlation_id`. ]*/ + if (properties_get_correlation_id(response_properties, &correlation_id_value) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_114: [ If obtaining the correlation Id fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ] */ + LogError("Could not retrieve correlation Id"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not get correlation Id from AMQP management response."); + } + else + { + if (amqpvalue_get_ulong(correlation_id_value, &correlation_id) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_132: [ If any functions manipulating AMQP values, application properties, etc., fail, an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve correlation Id ulong value"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not get correlation Id from AMQP management response."); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_119: [ `on_message_received` shall obtain the application properties map by calling `amqpvalue_get_inplace_described_value`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_070: [ Response messages have the following application-properties: ]*/ + map = amqpvalue_get_inplace_described_value(application_properties); + if (map == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_132: [ If any functions manipulating AMQP values, application properties, etc., fail, an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve application property map"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not get application property map from the application properties in the AMQP management response."); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_120: [ An AMQP value used to lookup the status code shall be created by calling `amqpvalue_create_string` with the status code key name (`statusCode`) as argument. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_071: [ statusCode integer Yes HTTP response code [RFC2616] ]*/ + key = amqpvalue_create_string(amqp_management->status_code_key_name); + if (key == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_132: [ If any functions manipulating AMQP values, application properties, etc., fail, an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not create status-code amqp value"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_135: [ When an error occurs in creating AMQP values (for status code, etc.) `on_message_received` shall call `messaging_delivery_released` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_released(); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_121: [ The status code shall be looked up in the application properties by using `amqpvalue_get_map_value`. ]*/ + value = amqpvalue_get_map_value(map, key); + if (value == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_122: [ If status code is not found an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve status code from application properties"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not retrieve status code from the application properties in the AMQP management response."); + } + else + { + int32_t status_code; + /* Codes_SRS_AMQP_MANAGEMENT_01_133: [ The status code value shall be extracted from the value found in the map by using `amqpvalue_get_int`. ]*/ + if (amqpvalue_get_int(value, &status_code) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_132: [ If any functions manipulating AMQP values, application properties, etc., fail, an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not retrieve status code int value"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_136: [ When `on_message_received` fails due to errors in parsing the response message `on_message_received` shall call `messaging_delivery_rejected` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not retrieve status code value from the application properties in the AMQP management response."); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_123: [ An AMQP value used to lookup the status description shall be created by calling `amqpvalue_create_string` with the status description key name (`statusDescription`) as argument. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_072: [ statusDescription string No Description of the status. ]*/ + desc_key = amqpvalue_create_string(amqp_management->status_description_key_name); + if (desc_key == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_132: [ If any functions manipulating AMQP values, application properties, etc., fail, an error shall be indicated to the consumer by calling the `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not create status-description amqp value"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_135: [ When an error occurs in creating AMQP values (for status code, etc.) `on_message_received` shall call `messaging_delivery_released` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_released(); + } + else + { + const char* status_description = NULL; + LIST_ITEM_HANDLE list_item_handle; + bool found = false; + bool is_error = false; + + /* Codes_SRS_AMQP_MANAGEMENT_01_124: [ The status description shall be looked up in the application properties by using `amqpvalue_get_map_value`. ]*/ + desc_value = amqpvalue_get_map_value(map, desc_key); + if (desc_value != NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_134: [ The status description value shall be extracted from the value found in the map by using `amqpvalue_get_string`. ]*/ + if (amqpvalue_get_string(desc_value, &status_description) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_125: [ If status description is not found, NULL shall be passed to the user callback as `status_description` argument. ]*/ + status_description = NULL; + } + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_125: [ If status description is not found, NULL shall be passed to the user callback as `status_description` argument. ]*/ + status_description = NULL; + } + + list_item_handle = singlylinkedlist_get_head_item(amqp_management->pending_operations); + while (list_item_handle != NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_116: [ Each pending operation item value shall be obtained by calling `singlylinkedlist_item_get_value`. ]*/ + OPERATION_MESSAGE_INSTANCE* operation_message = (OPERATION_MESSAGE_INSTANCE*)singlylinkedlist_item_get_value(list_item_handle); + if (operation_message == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_117: [ If iterating through the pending operations list fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not create status-description amqp value"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_135: [ When an error occurs in creating AMQP values (for status code, etc.) `on_message_received` shall call `messaging_delivery_released` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_released(); + break; + } + else + { + AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT execute_operation_result; + + /* Codes_SRS_AMQP_MANAGEMENT_01_112: [ `on_message_received` shall check if the correlation Id matches the stored message Id of any pending operation. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_068: [ The correlation-id of the response message MUST be the correlation-id from the request message (if present) ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_069: [ else the message-id from the request message. ]*/ + if (correlation_id == operation_message->message_id) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_074: [ Successful operations MUST result in a statusCode in the 2xx range as defined in Section 10.2 of [RFC2616]. ]*/ + if ((status_code < 200) || (status_code > 299)) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_128: [ If the status indicates that the operation failed, the result callback argument shall be `AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_075: [ Unsuccessful operations MUST NOT result in a statusCode in the 2xx range as defined in Section 10.2 of [RFC2616]. ]*/ + execute_operation_result = AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_127: [ If the operation succeeded the result callback argument shall be `AMQP_MANAGEMENT_EXECUTE_OPERATION_OK`. ]*/ + execute_operation_result = AMQP_MANAGEMENT_EXECUTE_OPERATION_OK; + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_126: [ If a corresponding correlation Id is found in the pending operations list, the callback associated with the pending operation shall be called. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_166: [ The `message` shall be passed as argument to the callback. ]*/ + operation_message->on_execute_operation_complete(operation_message->callback_context, execute_operation_result, status_code, status_description, message); + + free(operation_message); + + /* Codes_SRS_AMQP_MANAGEMENT_01_129: [ After calling the callback, the pending operation shall be removed from the pending operations list by calling `singlylinkedlist_remove`. ]*/ + if (singlylinkedlist_remove(amqp_management->pending_operations, list_item_handle) != 0) + { + LogError("Cannot remove pending operation"); + is_error = true; + break; + } + else + { + found = true; + } + + break; + } + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_115: [ Iterating through the pending operations shall be done by using `singlylinkedlist_get_head_item` and `singlylinkedlist_get_next_item` until the enm of the pending operations singly linked list is reached. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_117: [ If iterating through the pending operations list fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + list_item_handle = singlylinkedlist_get_next_item(list_item_handle); + } + + if (is_error) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_117: [ If iterating through the pending operations list fails, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_135: [ When an error occurs in creating AMQP values (for status code, etc.) `on_message_received` shall call `messaging_delivery_released` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_released(); + } + else + { + if (!found) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_118: [ If no pending operation is found matching the correlation Id, an error shall be indicated by calling `on_amqp_management_error` and passing the `on_amqp_management_error_context` to it. ]*/ + LogError("Could not match AMQP management response to request"); + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + /* Codes_SRS_AMQP_MANAGEMENT_01_135: [ When an error occurs in creating AMQP values (for status code, etc.) `on_message_received` shall call `messaging_delivery_released` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_rejected("amqp:internal-error", "Could not match AMQP management response to request"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_130: [ The `on_message_received` shall call `messaging_delivery_accepted` and return the created delivery AMQP value. ]*/ + result = messaging_delivery_accepted(); + } + } + + if (desc_value != NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + amqpvalue_destroy(desc_value); + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + amqpvalue_destroy(desc_key); + } + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + amqpvalue_destroy(value); + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + amqpvalue_destroy(key); + } + } + } + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + properties_destroy(response_properties); + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_131: [ All temporary values like AMQP values used as keys shall be freed before exiting the callback. ]*/ + application_properties_destroy(application_properties); + } + } + + return result; +} + +static void on_message_send_complete(void* context, MESSAGE_SEND_RESULT send_result, AMQP_VALUE delivery_state) +{ + (void)delivery_state; + + if (context == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_167: [ When `on_message_send_complete` is called with a NULL context it shall return. ]*/ + LogError("NULL context"); + } + else + { + if (send_result == MESSAGE_SEND_OK) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_170: [ If `send_result` is `MESSAGE_SEND_OK`, `on_message_send_complete` shall return. ]*/ + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_172: [ If `send_result` is different then `MESSAGE_SEND_OK`: ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_168: [ - `context` shall be used as a LIST_ITEM_HANDLE containing the pending operation. ]*/ + LIST_ITEM_HANDLE pending_operation_list_item_handle = (LIST_ITEM_HANDLE)context; + + /* Codes_SRS_AMQP_MANAGEMENT_01_169: [ - `on_message_send_complete` shall obtain the pending operation by calling `singlylinkedlist_item_get_value`. ]*/ + OPERATION_MESSAGE_INSTANCE* pending_operation_message = (OPERATION_MESSAGE_INSTANCE*)singlylinkedlist_item_get_value(pending_operation_list_item_handle); + AMQP_MANAGEMENT_HANDLE amqp_management = pending_operation_message->amqp_management; + + /* Codes_SRS_AMQP_MANAGEMENT_01_171: [ - `on_message_send_complete` shall removed the pending operation from the pending operations list. ]*/ + if (singlylinkedlist_remove(amqp_management->pending_operations, pending_operation_list_item_handle) != 0) + { + /* Tests_SRS_AMQP_MANAGEMENT_01_174: [ If any error occurs in removing the pending operation from the list `on_amqp_management_error` callback shall be invoked while passing the `on_amqp_management_error_context` as argument. ]*/ + amqp_management->on_amqp_management_error(amqp_management->on_amqp_management_error_context); + LogError("Cannot remove pending operation"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_173: [ - The callback associated with the pending operation shall be called with `AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR`. ]*/ + pending_operation_message->on_execute_operation_complete(pending_operation_message->callback_context, AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR, 0, NULL, NULL); + free(pending_operation_message); + } + } + } +} + +static void on_message_sender_state_changed(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state) +{ + if (context == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_137: [ When `on_message_sender_state_changed` is called with NULL `context`, it shall do nothing. ]*/ + LogError("on_message_sender_state_changed called with NULL context"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_138: [ When `on_message_sender_state_changed` is called and the `new_state` is different than `previous_state`, the following actions shall be taken: ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_148: [ When no state change is detected, `on_message_sender_state_changed` shall do nothing. ]*/ + if (new_state != previous_state) + { + AMQP_MANAGEMENT_INSTANCE* amqp_management_instance = (AMQP_MANAGEMENT_INSTANCE*)context; + switch (amqp_management_instance->amqp_management_state) + { + default: + break; + + /* Codes_SRS_AMQP_MANAGEMENT_01_139: [ For the current state of AMQP management being `OPENING`: ]*/ + case AMQP_MANAGEMENT_STATE_OPENING: + { + switch (new_state) + { + case MESSAGE_SENDER_STATE_OPENING: + /* Codes_SRS_AMQP_MANAGEMENT_01_165: [ - If `new_state` is `MESSAGE_SENDER_STATE_OPEING` the transition shall be ignored. ]*/ + break; + + default: + /* Codes_SRS_AMQP_MANAGEMENT_01_140: [ - If `new_state` is `MESSAGE_SENDER_STATE_IDLE`, `MESSAGE_SENDER_STATE_CLOSING` or `MESSAGE_SENDER_STATE_ERROR`, the `on_amqp_management_open_complete` callback shall be called with `AMQP_MANAGEMENT_OPEN_ERROR`, while also passing the context passed in `amqp_management_open_async`. ]*/ + case MESSAGE_SENDER_STATE_IDLE: + case MESSAGE_SENDER_STATE_CLOSING: + case MESSAGE_SENDER_STATE_ERROR: + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + amqp_management_instance->on_amqp_management_open_complete(amqp_management_instance->on_amqp_management_open_complete_context, AMQP_MANAGEMENT_OPEN_ERROR); + break; + + case MESSAGE_SENDER_STATE_OPEN: + amqp_management_instance->sender_connected = 1; + /* Codes_SRS_AMQP_MANAGEMENT_01_142: [ - If `new_state` is `MESSAGE_SENDER_STATE_OPEN` and the message receiver did not yet indicate its state as `MESSAGE_RECEIVER_STATE_OPEN`, the `on_amqp_management_open_complete` callback shall not be called.]*/ + if (amqp_management_instance->receiver_connected != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_141: [ - If `new_state` is `MESSAGE_SENDER_STATE_OPEN` and the message receiver already indicated its state as `MESSAGE_RECEIVER_STATE_OPEN`, the `on_amqp_management_open_complete` callback shall be called with `AMQP_MANAGEMENT_OPEN_OK`, while also passing the context passed in `amqp_management_open_async`. ]*/ + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_OPEN; + amqp_management_instance->on_amqp_management_open_complete(amqp_management_instance->on_amqp_management_open_complete_context, AMQP_MANAGEMENT_OPEN_OK); + } + break; + } + break; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_144: [ For the current state of AMQP management being `OPEN`: ]*/ + case AMQP_MANAGEMENT_STATE_OPEN: + { + switch (new_state) + { + default: + /* Codes_SRS_AMQP_MANAGEMENT_01_143: [ - If `new_state` is `MESSAGE_SENDER_STATE_IDLE`, `MESSAGE_SENDER_STATE_OPENING`, `MESSAGE_SENDER_STATE_CLOSING` or `MESSAGE_SENDER_STATE_ERROR` the `on_amqp_management_error` callback shall be invoked while passing the `on_amqp_management_error_context` as argument. ]*/ + case MESSAGE_SENDER_STATE_IDLE: + case MESSAGE_SENDER_STATE_CLOSING: + case MESSAGE_SENDER_STATE_ERROR: + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_ERROR; + amqp_management_instance->on_amqp_management_error(amqp_management_instance->on_amqp_management_error_context); + break; + + case MESSAGE_SENDER_STATE_OPEN: + /* Codes_SRS_AMQP_MANAGEMENT_01_145: [ - If `new_state` is `MESSAGE_SENDER_STATE_OPEN`, `on_message_sender_state_changed` shall do nothing. ]*/ + break; + } + break; + } + /* Codes_SRS_AMQP_MANAGEMENT_09_001: [ For the current state of AMQP management being `CLOSING`: ]*/ + case AMQP_MANAGEMENT_STATE_CLOSING: + { + switch (new_state) + { + default: + /* Codes_SRS_AMQP_MANAGEMENT_09_002: [ - If `new_state` is `MESSAGE_SENDER_STATE_OPEN`, `MESSAGE_SENDER_STATE_OPENING`, `MESSAGE_SENDER_STATE_ERROR` the `on_amqp_management_error` callback shall be invoked while passing the `on_amqp_management_error_context` as argument. ]*/ + case MESSAGE_SENDER_STATE_OPEN: + case MESSAGE_SENDER_STATE_OPENING: + case MESSAGE_SENDER_STATE_ERROR: + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_ERROR; + amqp_management_instance->on_amqp_management_error(amqp_management_instance->on_amqp_management_error_context); + break; + case MESSAGE_SENDER_STATE_IDLE: + case MESSAGE_SENDER_STATE_CLOSING: + /* Codes_SRS_AMQP_MANAGEMENT_09_003: [ - If `new_state` is `MESSAGE_SENDER_STATE_CLOSING` or `MESSAGE_SENDER_STATE_IDLE`, `on_message_sender_state_changed` shall do nothing. ]*/ + break; + } + break; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_146: [ For the current state of AMQP management being `ERROR`: ]*/ + case AMQP_MANAGEMENT_STATE_ERROR: + /* Codes_SRS_AMQP_MANAGEMENT_01_147: [ - All state transitions shall be ignored. ]*/ + break; + } + } + } +} + +static void on_message_receiver_state_changed(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state) +{ + if (context == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_149: [ When `on_message_receiver_state_changed` is called with NULL `context`, it shall do nothing. ]*/ + LogError("on_message_receiver_state_changed called with NULL context"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_150: [ When `on_message_receiver_state_changed` is called and the `new_state` is different than `previous_state`, the following actions shall be taken: ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_160: [ When no state change is detected, `on_message_receiver_state_changed` shall do nothing. ]*/ + if (new_state != previous_state) + { + AMQP_MANAGEMENT_INSTANCE* amqp_management_instance = (AMQP_MANAGEMENT_INSTANCE*)context; + switch (amqp_management_instance->amqp_management_state) + { + default: + break; + + /* Codes_SRS_AMQP_MANAGEMENT_01_151: [ For the current state of AMQP management being `OPENING`: ]*/ + case AMQP_MANAGEMENT_STATE_OPENING: + { + switch (new_state) + { + case MESSAGE_RECEIVER_STATE_OPENING: + /* Codes_SRS_AMQP_MANAGEMENT_01_164: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_OPEING` the transition shall be ignored. ]*/ + break; + + default: + /* Codes_SRS_AMQP_MANAGEMENT_01_152: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_IDLE`, `MESSAGE_RECEIVER_STATE_CLOSING` or `MESSAGE_RECEIVER_STATE_ERROR`, the `on_amqp_management_open_complete` callback shall be called with `AMQP_MANAGEMENT_OPEN_ERROR`, while also passing the context passed in `amqp_management_open_async`. ]*/ + case MESSAGE_RECEIVER_STATE_IDLE: + case MESSAGE_RECEIVER_STATE_CLOSING: + case MESSAGE_RECEIVER_STATE_ERROR: + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + amqp_management_instance->on_amqp_management_open_complete(amqp_management_instance->on_amqp_management_open_complete_context, AMQP_MANAGEMENT_OPEN_ERROR); + break; + + case MESSAGE_RECEIVER_STATE_OPEN: + amqp_management_instance->receiver_connected = 1; + /* Codes_SRS_AMQP_MANAGEMENT_01_154: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_OPEN` and the message sender did not yet indicate its state as `MESSAGE_RECEIVER_STATE_OPEN`, the `on_amqp_management_open_complete` callback shall not be called. ]*/ + if (amqp_management_instance->sender_connected != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_153: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_OPEN` and the message sender already indicated its state as `MESSAGE_RECEIVER_STATE_OPEN`, the `on_amqp_management_open_complete` callback shall be called with `AMQP_MANAGEMENT_OPEN_OK`, while also passing the context passed in `amqp_management_open_async`. ]*/ + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_OPEN; + amqp_management_instance->on_amqp_management_open_complete(amqp_management_instance->on_amqp_management_open_complete_context, AMQP_MANAGEMENT_OPEN_OK); + } + break; + } + break; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_155: [ For the current state of AMQP management being `OPEN`: ]*/ + case AMQP_MANAGEMENT_STATE_OPEN: + { + switch (new_state) + { + default: + /* Codes_SRS_AMQP_MANAGEMENT_01_156: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_IDLE`, `MESSAGE_RECEIVER_STATE_OPENING`, `MESSAGE_RECEIVER_STATE_CLOSING` or `MESSAGE_RECEIVER_STATE_ERROR` the `on_amqp_management_error` callback shall be invoked while passing the `on_amqp_management_error_context` as argument. ]*/ + case MESSAGE_RECEIVER_STATE_IDLE: + case MESSAGE_RECEIVER_STATE_CLOSING: + case MESSAGE_RECEIVER_STATE_ERROR: + amqp_management_instance->amqp_management_state = AMQP_MANAGEMENT_STATE_ERROR; + amqp_management_instance->on_amqp_management_error(amqp_management_instance->on_amqp_management_error_context); + break; + + case MESSAGE_RECEIVER_STATE_OPEN: + /* Codes_SRS_AMQP_MANAGEMENT_01_157: [ - If `new_state` is `MESSAGE_RECEIVER_STATE_OPEN`, `on_message_receiver_state_changed` shall do nothing. ]*/ + break; + } + break; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_158: [ For the current state of AMQP management being `ERROR`: ]*/ + case AMQP_MANAGEMENT_STATE_ERROR: + /* Codes_SRS_AMQP_MANAGEMENT_01_159: [ - All state transitions shall be ignored. ]*/ + break; + } + } + } +} + +static int set_message_id(MESSAGE_HANDLE message, uint64_t next_message_id) +{ + int result; + PROPERTIES_HANDLE properties; + + /* Codes_SRS_AMQP_MANAGEMENT_01_094: [ In order to set the message Id on the message, the properties shall be obtained by calling `message_get_properties`. ]*/ + if (message_get_properties(message, &properties) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_098: [ If any API fails while setting the message Id, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not retrieve message properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_099: [ If the properties were not set on the message, a new properties instance shall be created by calling `properties_create`. ]*/ + if (properties == NULL) + { + properties = properties_create(); + } + + if (properties == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_098: [ If any API fails while setting the message Id, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not create message properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_095: [ A message Id with the next ulong value to be used shall be created by calling `amqpvalue_create_message_id_ulong`. ]*/ + AMQP_VALUE message_id = amqpvalue_create_message_id_ulong(next_message_id); + if (message_id == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_098: [ If any API fails while setting the message Id, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not create message id value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_096: [ The message Id value shall be set on the properties by calling `properties_set_message_id`. ]*/ + if (properties_set_message_id(properties, message_id) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_098: [ If any API fails while setting the message Id, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not set message Id on the properties"); + result = __FAILURE__; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_097: [ The properties thus modified to contain the message Id shall be set on the message by calling `message_set_properties`. ]*/ + else if (message_set_properties(message, properties) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_098: [ If any API fails while setting the message Id, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not set message properties"); + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(message_id); + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_100: [ After setting the properties, the properties instance shall be freed by `properties_destroy`. ]*/ + properties_destroy(properties); + } + } + + return result; +} + +static int add_string_key_value_pair_to_map(AMQP_VALUE map, const char* key, const char* value) +{ + int result; + + /* Codes_SRS_AMQP_MANAGEMENT_01_084: [ For each of the arguments `operation`, `type` and `locales` an AMQP value of type string shall be created by calling `amqpvalue_create_string` in order to be used as key in the application properties map. ]*/ + AMQP_VALUE key_value = amqpvalue_create_string(key); + if (key_value == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_090: [ If any APIs used to create and set the application properties on the message fails, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not create key value for %s", key); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_085: [ For each of the arguments `operation`, `type` and `locales` an AMQP value of type string containing the argument value shall be created by calling `amqpvalue_create_string` in order to be used as value in the application properties map. ]*/ + AMQP_VALUE value_value = amqpvalue_create_string(value); + if (value_value == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_090: [ If any APIs used to create and set the application properties on the message fails, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not create value for key %s", key); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_086: [ The key/value pairs for `operation`, `type` and `locales` shall be added to the application properties map by calling `amqpvalue_set_map_value`. ]*/ + if (amqpvalue_set_map_value(map, key_value, value_value) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_090: [ If any APIs used to create and set the application properties on the message fails, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not set the value in the map for key %s", key); + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(value_value); + } + + amqpvalue_destroy(key_value); + } + + return result; +} + +static int internal_set_status_code_key_name(AMQP_MANAGEMENT_HANDLE amqp_management, const char* status_code_key_name) +{ + int result; + char* copied_status_code_key_name; + if (mallocAndStrcpy_s(&copied_status_code_key_name, status_code_key_name) != 0) + { + LogError("Cannot copy status code key name"); + result = __FAILURE__; + } + else + { + if (amqp_management->status_code_key_name != NULL) + { + free(amqp_management->status_code_key_name); + } + + amqp_management->status_code_key_name = copied_status_code_key_name; + result = 0; + } + + return result; +} + +static int internal_set_status_description_key_name(AMQP_MANAGEMENT_HANDLE amqp_management, const char* status_description_key_name) +{ + int result; + char* copied_status_description_key_name; + if (mallocAndStrcpy_s(&copied_status_description_key_name, status_description_key_name) != 0) + { + LogError("Cannot copy status description key name"); + result = __FAILURE__; + } + else + { + if (amqp_management->status_description_key_name != NULL) + { + free(amqp_management->status_description_key_name); + } + + amqp_management->status_description_key_name = copied_status_description_key_name; + result = 0; + } + + return result; +} + +AMQP_MANAGEMENT_HANDLE amqp_management_create(SESSION_HANDLE session, const char* management_node) +{ + AMQP_MANAGEMENT_INSTANCE* amqp_management; + + if ((session == NULL) || + (management_node == NULL)) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_002: [ If `session` or `management_node` is NULL then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Bad arguments: session = %p, management_node = %p", session, management_node); + amqp_management = NULL; + } + else if (strlen(management_node) == 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_030: [ If `management_node` is an empty string, then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Empty string management node"); + amqp_management = NULL; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_001: [ `amqp_management_create` shall create a new CBS instance and on success return a non-NULL handle to it. ]*/ + amqp_management = (AMQP_MANAGEMENT_INSTANCE*)malloc(sizeof(AMQP_MANAGEMENT_INSTANCE)); + if (amqp_management == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_005: [ If allocating memory for the new handle fails, `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for AMQP management handle"); + } + else + { + amqp_management->sender_connected = 0; + amqp_management->receiver_connected = 0; + amqp_management->on_amqp_management_open_complete = NULL; + amqp_management->on_amqp_management_open_complete_context = NULL; + amqp_management->on_amqp_management_error = NULL; + amqp_management->on_amqp_management_error_context = NULL; + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + amqp_management->status_code_key_name = NULL; + amqp_management->status_description_key_name = NULL; + + /* Codes_SRS_AMQP_MANAGEMENT_01_003: [ `amqp_management_create` shall create a singly linked list for pending operations by calling `singlylinkedlist_create`. ]*/ + amqp_management->pending_operations = singlylinkedlist_create(); + if (amqp_management->pending_operations == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_004: [ If `singlylinkedlist_create` fails, `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Cannot create pending operations list"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_181: [ `amqp_management_create` shall set the status code key name to be used for parsing the status code to `statusCode`. ]*/ + if (internal_set_status_code_key_name(amqp_management, "statusCode") != 0) + { + LogError("Cannot set status code key name"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_182: [ `amqp_management_create` shall set the status description key name to be used for parsing the status description to `statusDescription`. ]*/ + if (internal_set_status_description_key_name(amqp_management, "statusDescription") != 0) + { + LogError("Cannot set status description key name"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_010: [ The `source` argument shall be a value created by calling `messaging_create_source` with `management_node` as argument. ]*/ + AMQP_VALUE source = messaging_create_source(management_node); + if (source == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_012: [ If `messaging_create_source` fails then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating source AMQP value"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_011: [ The `target` argument shall be a value created by calling `messaging_create_target` with `management_node` as argument. ]*/ + AMQP_VALUE target = messaging_create_target(management_node); + if (target == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_013: [ If `messaging_create_target` fails then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating target AMQP value"); + } + else + { + size_t management_node_length = strlen(management_node); + + char* sender_link_name = (char*)malloc(management_node_length + COUNT_CHARS(sender_suffix) + 1); + if (sender_link_name == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_033: [ If any other error occurs `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed allocating memory for sender link name"); + } + else + { + char* receiver_link_name; + + (void)memcpy(sender_link_name, management_node, management_node_length); + (void)memcpy(sender_link_name + management_node_length, sender_suffix, COUNT_CHARS(sender_suffix) + 1); + + receiver_link_name = (char*)malloc(management_node_length + COUNT_CHARS(receiver_suffix) + 1); + if (receiver_link_name == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_033: [ If any other error occurs `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed allocating memory for receiver link name"); + } + else + { + (void)memcpy(receiver_link_name, management_node, management_node_length); + (void)memcpy(receiver_link_name + management_node_length, receiver_suffix, COUNT_CHARS(receiver_suffix) + 1); + + /* Codes_SRS_AMQP_MANAGEMENT_01_006: [ `amqp_management_create` shall create a sender link by calling `link_create`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_007: [ The `session` argument shall be set to `session`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_008: [ The `name` argument shall be constructed by concatenating the `management_node` value with `-sender`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_009: [ The `role` argument shall be `role_sender`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_019: [ The `source` argument shall be the value created by calling `messaging_create_source`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_020: [ The `target` argument shall be the value created by calling `messaging_create_target`. ]*/ + amqp_management->sender_link = link_create(session, sender_link_name, role_sender, source, target); + if (amqp_management->sender_link == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_014: [ If `link_create` fails when creating the sender link then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating sender link"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_015: [ `amqp_management_create` shall create a receiver link by calling `link_create`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_016: [ The `session` argument shall be set to `session`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_017: [ The `name` argument shall be constructed by concatenating the `management_node` value with `-receiver`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_018: [ The `role` argument shall be `role_receiver`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_019: [ The `source` argument shall be the value created by calling `messaging_create_source`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_020: [ The `target` argument shall be the value created by calling `messaging_create_target`. ]*/ + amqp_management->receiver_link = link_create(session, receiver_link_name, role_receiver, source, target); + if (amqp_management->receiver_link == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_021: [ If `link_create` fails when creating the receiver link then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating receiver link"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_022: [ `amqp_management_create` shall create a message sender by calling `messagesender_create` and passing to it the sender link handle. ]*/ + amqp_management->message_sender = messagesender_create(amqp_management->sender_link, on_message_sender_state_changed, amqp_management); + if (amqp_management->message_sender == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_031: [ If `messagesender_create` fails then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating message sender"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_023: [ `amqp_management_create` shall create a message receiver by calling `messagereceiver_create` and passing to it the receiver link handle. ]*/ + amqp_management->message_receiver = messagereceiver_create(amqp_management->receiver_link, on_message_receiver_state_changed, amqp_management); + if (amqp_management->message_receiver == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_032: [ If `messagereceiver_create` fails then `amqp_management_create` shall fail and return NULL. ]*/ + LogError("Failed creating message receiver"); + link_destroy(amqp_management->receiver_link); + } + else + { + free(receiver_link_name); + free(sender_link_name); + amqpvalue_destroy(target); + amqpvalue_destroy(source); + + /* Codes_SRS_AMQP_MANAGEMENT_01_106: [ The message Id set on the message properties shall start at 0. ]*/ + amqp_management->next_message_id = 0; + + goto all_ok; + } + + messagesender_destroy(amqp_management->message_sender); + } + + link_destroy(amqp_management->receiver_link); + } + + link_destroy(amqp_management->sender_link); + } + + free(receiver_link_name); + } + + free(sender_link_name); + } + + amqpvalue_destroy(target); + } + + amqpvalue_destroy(source); + } + + free(amqp_management->status_description_key_name); + } + + free(amqp_management->status_code_key_name); + } + + singlylinkedlist_destroy(amqp_management->pending_operations); + } + + free(amqp_management); + amqp_management = NULL; + } + } + +all_ok: + return amqp_management; +} + +void amqp_management_destroy(AMQP_MANAGEMENT_HANDLE amqp_management) +{ + if (amqp_management == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_025: [ If `amqp_management` is NULL, `amqp_management_destroy` shall do nothing. ]*/ + LogError("NULL amqp_management"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_024: [ `amqp_management_destroy` shall free all the resources allocated by `amqp_management_create`. ]*/ + if (amqp_management->amqp_management_state != AMQP_MANAGEMENT_STATE_IDLE) + { + (void)amqp_management_close(amqp_management); + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_028: [ `amqp_management_destroy` shall free the message sender by calling `messagesender_destroy`. ]*/ + messagesender_destroy(amqp_management->message_sender); + /* Codes_SRS_AMQP_MANAGEMENT_01_029: [ `amqp_management_destroy` shall free the message receiver by calling `messagereceiver_destroy`. ]*/ + messagereceiver_destroy(amqp_management->message_receiver); + /* Codes_SRS_AMQP_MANAGEMENT_01_027: [ `amqp_management_destroy` shall free the sender and receiver links by calling `link_destroy`. ]*/ + link_destroy(amqp_management->sender_link); + link_destroy(amqp_management->receiver_link); + free(amqp_management->status_code_key_name); + free(amqp_management->status_description_key_name); + /* Codes_SRS_AMQP_MANAGEMENT_01_026: [ `amqp_management_destroy` shall free the singly linked list by calling `singlylinkedlist_destroy`. ]*/ + singlylinkedlist_destroy(amqp_management->pending_operations); + free(amqp_management); + } +} + +int amqp_management_open_async(AMQP_MANAGEMENT_HANDLE amqp_management, ON_AMQP_MANAGEMENT_OPEN_COMPLETE on_amqp_management_open_complete, void* on_amqp_management_open_complete_context, ON_AMQP_MANAGEMENT_ERROR on_amqp_management_error, void* on_amqp_management_error_context) +{ + int result; + + /* Codes_SRS_AMQP_MANAGEMENT_01_044: [ `on_amqp_management_open_complete_context` and `on_amqp_management_error_context` shall be allowed to be NULL. ]*/ + if ((amqp_management == NULL) || + (on_amqp_management_open_complete == NULL) || + (on_amqp_management_error == NULL)) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_038: [ If `amqp_management`, `on_amqp_management_open_complete` or `on_amqp_management_error` is NULL, `amqp_management_open_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: amqp_management = %p, on_amqp_management_open_complete = %p, on_amqp_management_error = %p", + amqp_management, + on_amqp_management_open_complete, + on_amqp_management_error); + result = __FAILURE__; + } + else if (amqp_management->amqp_management_state != AMQP_MANAGEMENT_STATE_IDLE) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_043: [ If the AMQP management instance is already OPEN or OPENING, `amqp_management_open_async` shall fail and return a non-zero value. ]*/ + LogError("AMQP management instance already OPEN"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_036: [ `amqp_management_open_async` shall start opening the AMQP management instance and save the callbacks so that they can be called when opening is complete. ]*/ + amqp_management->on_amqp_management_open_complete = on_amqp_management_open_complete; + amqp_management->on_amqp_management_open_complete_context = on_amqp_management_open_complete_context; + amqp_management->on_amqp_management_error = on_amqp_management_error; + amqp_management->on_amqp_management_error_context = on_amqp_management_error_context; + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_OPENING; + + /* Codes_SRS_AMQP_MANAGEMENT_01_040: [ `amqp_management_open_async` shall open the message receiver by calling `messagereceiver_open`. ]*/ + if (messagereceiver_open(amqp_management->message_receiver, on_message_received, amqp_management) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_042: [ If `messagereceiver_open` fails, `amqp_management_open_async` shall fail and return a non-zero value. ]*/ + LogError("Failed opening message receiver"); + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_039: [ `amqp_management_open_async` shall open the message sender by calling `messagesender_open`. ]*/ + if (messagesender_open(amqp_management->message_sender) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_041: [ If `messagesender_open` fails, `amqp_management_open_async` shall fail and return a non-zero value. ]*/ + LogError("Failed opening message sender"); + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + (void)messagereceiver_close(amqp_management->message_receiver); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_037: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int amqp_management_close(AMQP_MANAGEMENT_HANDLE amqp_management) +{ + int result; + + if (amqp_management == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_047: [ If `amqp_management` is NULL, `amqp_management_close` shall fail and return a non-zero value. ]*/ + LogError("NULL amqp_management"); + result = __FAILURE__; + } + else if (amqp_management->amqp_management_state == AMQP_MANAGEMENT_STATE_IDLE) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_049: [ `amqp_management_close` on an AMQP management instance that is not OPEN, shall fail and return a non-zero value. ]*/ + LogError("AMQP management instance not open"); + result = __FAILURE__; + } + else + { + AMQP_MANAGEMENT_STATE previous_state = amqp_management->amqp_management_state; + + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_CLOSING; + + if (previous_state == AMQP_MANAGEMENT_STATE_OPENING) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_048: [ `amqp_management_close` on an AMQP management instance that is OPENING shall trigger the `on_amqp_management_open_complete` callback with `AMQP_MANAGEMENT_OPEN_CANCELLED`, while also passing the context passed in `amqp_management_open_async`. ]*/ + amqp_management->on_amqp_management_open_complete(amqp_management->on_amqp_management_open_complete_context, AMQP_MANAGEMENT_OPEN_CANCELLED); + } + + + /* Codes_SRS_AMQP_MANAGEMENT_01_045: [ `amqp_management_close` shall close the AMQP management instance. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_050: [ `amqp_management_close` shall close the message sender by calling `messagesender_close`. ]*/ + if (messagesender_close(amqp_management->message_sender) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_052: [ If `messagesender_close` fails, `amqp_management_close` shall fail and return a non-zero value. ]*/ + LogError("messagesender_close failed"); + result = __FAILURE__; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_051: [ `amqp_management_close` shall close the message receiver by calling `messagereceiver_close`. ]*/ + else if (messagereceiver_close(amqp_management->message_receiver) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_053: [ If `messagereceiver_close` fails, `amqp_management_close` shall fail and return a non-zero value. ]*/ + LogError("messagereceiver_close failed"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE list_item_handle = singlylinkedlist_get_head_item(amqp_management->pending_operations); + while (list_item_handle != NULL) + { + OPERATION_MESSAGE_INSTANCE* operation_message = (OPERATION_MESSAGE_INSTANCE*)singlylinkedlist_item_get_value(list_item_handle); + if (operation_message == NULL) + { + LogError("Cannot obtain pending operation"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_054: [ All pending operations shall be indicated complete with the code `AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED`. ]*/ + operation_message->on_execute_operation_complete(operation_message->callback_context, AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED, 0, NULL, NULL); + free(operation_message); + } + + if (singlylinkedlist_remove(amqp_management->pending_operations, list_item_handle) != 0) + { + LogError("Cannot remove item"); + } + + list_item_handle = singlylinkedlist_get_head_item(amqp_management->pending_operations); + } + + amqp_management->amqp_management_state = AMQP_MANAGEMENT_STATE_IDLE; + + /* Codes_SRS_AMQP_MANAGEMENT_01_046: [ On success it shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int amqp_management_execute_operation_async(AMQP_MANAGEMENT_HANDLE amqp_management, const char* operation, const char* type, const char* locales, MESSAGE_HANDLE message, ON_AMQP_MANAGEMENT_EXECUTE_OPERATION_COMPLETE on_execute_operation_complete, void* on_execute_operation_complete_context) +{ + int result; + + if ((amqp_management == NULL) || + (operation == NULL) || + (type == NULL) || + (on_execute_operation_complete == NULL)) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_057: [ If `amqp_management`, `operation`, `type` or `on_execute_operation_complete` is NULL, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: amqp_management = %p, operation = %p, type = %p", + amqp_management, operation, type); + result = __FAILURE__; + } + /* Codes_SRS_AMQP_MANAGEMENT_01_081: [ If `amqp_management_execute_operation_async` is called when not OPEN, it shall fail and return a non-zero value. ]*/ + else if ((amqp_management->amqp_management_state == AMQP_MANAGEMENT_STATE_IDLE) || + /* Codes_SRS_AMQP_MANAGEMENT_01_104: [ If `amqp_management_execute_operation_async` is called when the AMQP management is in error, it shall fail and return a non-zero value. ]*/ + (amqp_management->amqp_management_state == AMQP_MANAGEMENT_STATE_ERROR)) + { + LogError("amqp_management_execute_operation_async called while not open or in error"); + result = __FAILURE__; + } + else + { + AMQP_VALUE application_properties; + MESSAGE_HANDLE cloned_message; + + if (message == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_102: [ If `message` is NULL, a new message shall be created by calling `message_create`. ]*/ + cloned_message = message_create(); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_103: [ Otherwise the existing message shall be cloned by using `message_clone` before being modified accordingly and used for the pending operation. ]*/ + cloned_message = message_clone(message); + if (cloned_message == NULL) + { + LogError("Could not clone message"); + } + } + + if (cloned_message == NULL) + { + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_055: [ `amqp_management_execute_operation_async` shall start an AMQP management operation. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_082: [ `amqp_management_execute_operation_async` shall obtain the application properties from the message by calling `message_get_application_properties`. ]*/ + if (message_get_application_properties(cloned_message, &application_properties) != 0) + { + LogError("Could not get application properties"); + result = __FAILURE__; + } + else + { + if (application_properties == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_083: [ If no application properties were set on the message, a new application properties instance shall be created by calling `amqpvalue_create_map`; ]*/ + application_properties = amqpvalue_create_map(); + if (application_properties == NULL) + { + LogError("Could not create application properties"); + } + } + + if (application_properties == NULL) + { + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_084: [ For each of the arguments `operation`, `type` and `locales` an AMQP value of type string shall be created by calling `amqpvalue_create_string` in order to be used as key in the application properties map. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_085: [ For each of the arguments `operation`, `type` and `locales` an AMQP value of type string containing the argument value shall be created by calling `amqpvalue_create_string` in order to be used as value in the application properties map. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_058: [ Request messages have the following application-properties: ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_059: [ operation string Yes The management operation to be performed. ] */ + if ((add_string_key_value_pair_to_map(application_properties, "operation", operation) != 0) || + /* Codes_SRS_AMQP_MANAGEMENT_01_061: [ type string Yes The Manageable Entity Type of the Manageable Entity to be managed. ]*/ + (add_string_key_value_pair_to_map(application_properties, "type", type) != 0) || + /* Codes_SRS_AMQP_MANAGEMENT_01_093: [ If `locales` is NULL, no key/value pair shall be added for it in the application properties map. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_063: [ locales string No A list of locales that the sending peer permits for incoming informational text in response messages. ]*/ + ((locales != NULL) && (add_string_key_value_pair_to_map(application_properties, "locales", locales) != 0))) + { + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_087: [ The application properties obtained after adding the key/value pairs shall be set on the message by calling `message_set_application_properties`. ]*/ + if (message_set_application_properties(cloned_message, application_properties) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_090: [ If any APIs used to create and set the application properties on the message fails, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not set application properties"); + result = __FAILURE__; + } + else if (set_message_id(cloned_message, amqp_management->next_message_id) != 0) + { + result = __FAILURE__; + } + else + { + OPERATION_MESSAGE_INSTANCE* pending_operation_message = (OPERATION_MESSAGE_INSTANCE*)malloc(sizeof(OPERATION_MESSAGE_INSTANCE)); + if (pending_operation_message == NULL) + { + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE added_item; + pending_operation_message->callback_context = on_execute_operation_complete_context; + pending_operation_message->on_execute_operation_complete = on_execute_operation_complete; + pending_operation_message->message_id = amqp_management->next_message_id; + pending_operation_message->amqp_management = amqp_management; + + /* Codes_SRS_AMQP_MANAGEMENT_01_091: [ Once the request message has been sent, an entry shall be stored in the pending operations list by calling `singlylinkedlist_add`. ]*/ + added_item = singlylinkedlist_add(amqp_management->pending_operations, pending_operation_message); + if (added_item == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_092: [ If `singlylinkedlist_add` fails then `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not add the operation to the pending operations list."); + free(pending_operation_message); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_088: [ `amqp_management_execute_operation_async` shall send the message by calling `messagesender_send_async`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_166: [ The `on_message_send_complete` callback shall be passed to the `messagesender_send_async` call. ]*/ + if (messagesender_send_async(amqp_management->message_sender, cloned_message, on_message_send_complete, added_item, 0) == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_089: [ If `messagesender_send_async` fails, `amqp_management_execute_operation_async` shall fail and return a non-zero value. ]*/ + LogError("Could not send request message"); + (void)singlylinkedlist_remove(amqp_management->pending_operations, added_item); + free(pending_operation_message); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_107: [ The message Id set on the message properties shall be incremented with each operation. ]*/ + amqp_management->next_message_id++; + + /* Codes_SRS_AMQP_MANAGEMENT_01_056: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + } + } + + /* Codes_SRS_AMQP_MANAGEMENT_01_101: [ After setting the application properties, the application properties instance shall be freed by `amqpvalue_destroy`. ]*/ + amqpvalue_destroy(application_properties); + } + } + + message_destroy(cloned_message); + } + } + return result; +} + +void amqp_management_set_trace(AMQP_MANAGEMENT_HANDLE amqp_management, bool trace_on) +{ + if (amqp_management == NULL) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_163: [ If `amqp_management` is NULL, `amqp_management_set_trace` shal do nothing. ]*/ + LogError("NULL amqp_management"); + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_161: [ `amqp_management_set_trace` shall call `messagesender_set_trace` to enable/disable tracing on the message sender. ]*/ + messagesender_set_trace(amqp_management->message_sender, trace_on); + /* Codes_SRS_AMQP_MANAGEMENT_01_162: [ `amqp_management_set_trace` shall call `messagereceiver_set_trace` to enable/disable tracing on the message receiver. ]*/ + messagereceiver_set_trace(amqp_management->message_receiver, trace_on); + } +} + +int amqp_management_set_override_status_code_key_name(AMQP_MANAGEMENT_HANDLE amqp_management, const char* override_status_code_key_name) +{ + int result; + + /* Codes_SRS_AMQP_MANAGEMENT_01_171: [ If `amqp_management` is NULL, `amqp_management_set_override_status_code_key_name` shall fail and return a non-zero value. ]*/ + if ((amqp_management == NULL) || + /* Codes_SRS_AMQP_MANAGEMENT_01_172: [ If `override_status_code_key_name` is NULL, `amqp_management_set_override_status_code_key_name` shall fail and return a non-zero value. ]*/ + (override_status_code_key_name == NULL)) + { + LogError("Bad arguments: amqp_management = %p, override_status_code_key_name = %s", + amqp_management, P_OR_NULL(override_status_code_key_name)); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_167: [ `amqp_management_set_override_status_code_key_name` shall set the status code key name used to parse the status code from the reply messages to `override_status_code_key_name`. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_168: [ `amqp_management_set_override_status_code_key_name` shall copy the `override_status_code_key_name` string. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_169: [ `amqp_management_set_override_status_code_key_name` shall free any string previously used for the status code key name. ]*/ + if (internal_set_status_code_key_name(amqp_management, override_status_code_key_name) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_173: [ If any error occurs in copying the `override_status_code_key_name` string, `amqp_management_set_override_status_code_key_name` shall fail and return a non-zero value. ]*/ + LogError("Cannot set status code key name"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_170: [ On success, `amqp_management_set_override_status_code_key_name` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int amqp_management_set_override_status_description_key_name(AMQP_MANAGEMENT_HANDLE amqp_management, const char* override_status_description_key_name) +{ + int result; + + /* Codes_SRS_AMQP_MANAGEMENT_01_178: [ If `amqp_management` is NULL, `amqp_management_set_override_status_description_key_name` shall fail and return a non-zero value. ]*/ + if ((amqp_management == NULL) || + /* Tests_SRS_AMQP_MANAGEMENT_01_179: [ If `override_status_description_key_name` is NULL, `amqp_management_set_override_status_description_key_name` shall fail and return a non-zero value. ]*/ + (override_status_description_key_name == NULL)) + { + LogError("Bad arguments: amqp_management = %p, override_status_description_key_name = %s", + amqp_management, P_OR_NULL(override_status_description_key_name)); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQP_MANAGEMENT_01_174: [ `amqp_management_set_override_status_description_key_name` shall set the status description key name used to parse the status description from the reply messages to `over ride_status_description_key_name`.]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_175: [ `amqp_management_set_override_status_description_key_name` shall copy the `override_status_description_key_name` string. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_176: [ `amqp_management_set_override_status_description_key_name` shall free any string previously used for the status description key name. ]*/ + /* Codes_SRS_AMQP_MANAGEMENT_01_177: [ On success, `amqp_management_set_override_status_description_key_name` shall return 0. ]*/ + if (internal_set_status_description_key_name(amqp_management, override_status_description_key_name) != 0) + { + /* Codes_SRS_AMQP_MANAGEMENT_01_180: [ If any error occurs in copying the `override_status_description_key_name` string, `amqp_management_set_override_status_description_key_name` shall fail and return a non-zero value. ]*/ + LogError("Cannot set status description key name"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/amqpvalue.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,5909 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/amqp_types.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/refcount.h" + +/* Requirements satisfied by the current implementation without any code: +Codes_SRS_AMQPVALUE_01_270: [<encoding code="0x56" category="fixed" width="1" label="boolean with the octet 0x00 being false and octet 0x01 being true"/>] +Codes_SRS_AMQPVALUE_01_099: [Represents an approximate point in time using the Unix time t [IEEE1003] encoding of UTC, but with a precision of milliseconds.] +*/ + +typedef struct AMQP_LIST_VALUE_TAG +{ + AMQP_VALUE* items; + uint32_t count; +} AMQP_LIST_VALUE; + +typedef struct AMQP_ARRAY_VALUE_TAG +{ + AMQP_VALUE* items; + uint32_t count; +} AMQP_ARRAY_VALUE; + +typedef struct AMQP_MAP_KEY_VALUE_PAIR_TAG +{ + AMQP_VALUE key; + AMQP_VALUE value; +} AMQP_MAP_KEY_VALUE_PAIR; + +typedef struct AMQP_MAP_VALUE_TAG +{ + AMQP_MAP_KEY_VALUE_PAIR* pairs; + uint32_t pair_count; +} AMQP_MAP_VALUE; + +typedef struct AMQP_STRING_VALUE_TAG +{ + char* chars; +} AMQP_STRING_VALUE; + +typedef struct AMQP_SYMBOL_VALUE_TAG +{ + char* chars; +} AMQP_SYMBOL_VALUE; + +typedef struct AMQP_BINARY_VALUE_TAG +{ + unsigned char* bytes; + uint32_t length; +} AMQP_BINARY_VALUE; + +typedef struct DESCRIBED_VALUE_TAG +{ + AMQP_VALUE descriptor; + AMQP_VALUE value; +} DESCRIBED_VALUE; + +typedef union AMQP_VALUE_UNION_TAG +{ + DESCRIBED_VALUE described_value; + unsigned char ubyte_value; + uint16_t ushort_value; + uint32_t uint_value; + uint64_t ulong_value; + char byte_value; + int16_t short_value; + int32_t int_value; + int64_t long_value; + bool bool_value; + float float_value; + double double_value; + uint32_t char_value; + int64_t timestamp_value; + uuid uuid_value; + AMQP_STRING_VALUE string_value; + amqp_binary binary_value; + AMQP_LIST_VALUE list_value; + AMQP_MAP_VALUE map_value; + AMQP_ARRAY_VALUE array_value; + AMQP_SYMBOL_VALUE symbol_value; +} AMQP_VALUE_UNION; + +typedef enum DECODE_LIST_STEP_TAG +{ + DECODE_LIST_STEP_SIZE, + DECODE_LIST_STEP_COUNT, + DECODE_LIST_STEP_ITEMS +} DECODE_LIST_STEP; + +typedef enum DECODE_ARRAY_STEP_TAG +{ + DECODE_ARRAY_STEP_SIZE, + DECODE_ARRAY_STEP_COUNT, + DECODE_ARRAY_STEP_ITEMS +} DECODE_ARRAY_STEP; + +typedef enum DECODE_DESCRIBED_VALUE_STEP_TAG +{ + DECODE_DESCRIBED_VALUE_STEP_DESCRIPTOR, + DECODE_DESCRIBED_VALUE_STEP_VALUE +} DECODE_DESCRIBED_VALUE_STEP; + +typedef enum DECODE_MAP_STEP_TAG +{ + DECODE_MAP_STEP_SIZE, + DECODE_MAP_STEP_COUNT, + DECODE_MAP_STEP_PAIRS +} DECODE_MAP_STEP; + +typedef struct DECODE_LIST_VALUE_STATE_TAG +{ + DECODE_LIST_STEP list_value_state; + uint32_t item; +} DECODE_LIST_VALUE_STATE; + +typedef struct DECODE_ARRAY_VALUE_STATE_TAG +{ + DECODE_ARRAY_STEP array_value_state; + uint32_t item; + unsigned char constructor_byte; +} DECODE_ARRAY_VALUE_STATE; + +typedef struct DECODE_DESCRIBED_VALUE_STATE_TAG +{ + DECODE_DESCRIBED_VALUE_STEP described_value_state; +} DECODE_DESCRIBED_VALUE_STATE; + +typedef struct DECODE_STRING_VALUE_STATE_TAG +{ + uint32_t length; +} DECODE_STRING_VALUE_STATE; + +typedef struct DECODE_SYMBOL_VALUE_STATE_TAG +{ + uint32_t length; +} DECODE_SYMBOL_VALUE_STATE; + +typedef struct DECODE_MAP_VALUE_STATE_TAG +{ + DECODE_MAP_STEP map_value_state; + uint32_t item; +} DECODE_MAP_VALUE_STATE; + +typedef union DECODE_VALUE_STATE_UNION_TAG +{ + DECODE_LIST_VALUE_STATE list_value_state; + DECODE_ARRAY_VALUE_STATE array_value_state; + DECODE_DESCRIBED_VALUE_STATE described_value_state; + DECODE_STRING_VALUE_STATE string_value_state; + DECODE_SYMBOL_VALUE_STATE symbol_value_state; + DECODE_MAP_VALUE_STATE map_value_state; +} DECODE_VALUE_STATE_UNION; + +typedef struct AMQP_VALUE_DATA_TAG +{ + AMQP_TYPE type; + AMQP_VALUE_UNION value; +} AMQP_VALUE_DATA; + +DEFINE_REFCOUNT_TYPE(AMQP_VALUE_DATA); + +typedef enum DECODER_STATE_TAG +{ + DECODER_STATE_CONSTRUCTOR, + DECODER_STATE_TYPE_DATA, + DECODER_STATE_DONE, + DECODER_STATE_ERROR +} DECODER_STATE; + +typedef struct INTERNAL_DECODER_DATA_TAG* INTERNAL_DECODER_HANDLE; + +typedef struct INTERNAL_DECODER_DATA_TAG +{ + ON_VALUE_DECODED on_value_decoded; + void* on_value_decoded_context; + size_t bytes_decoded; + DECODER_STATE decoder_state; + uint8_t constructor_byte; + AMQP_VALUE_DATA* decode_to_value; + INTERNAL_DECODER_HANDLE inner_decoder; + DECODE_VALUE_STATE_UNION decode_value_state; + bool is_internal; +} INTERNAL_DECODER_DATA; + +typedef struct AMQPVALUE_DECODER_HANDLE_DATA_TAG +{ + INTERNAL_DECODER_DATA* internal_decoder; + AMQP_VALUE_DATA* decode_to_value; +} AMQPVALUE_DECODER_HANDLE_DATA; + +/* Codes_SRS_AMQPVALUE_01_003: [1.6.1 null Indicates an empty value.] */ +AMQP_VALUE amqpvalue_create_null(void) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_002: [If allocating the AMQP_VALUE fails then amqpvalue_create_null shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_001: [amqpvalue_create_null shall return a handle to an AMQP_VALUE that stores a null value.] */ + result->type = AMQP_TYPE_NULL; + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_004: [1.6.2 boolean Represents a true or false value.] */ +AMQP_VALUE amqpvalue_create_boolean(bool value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_007: [If allocating the AMQP_VALUE fails then amqpvalue_create_boolean shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_006: [amqpvalue_create_boolean shall return a handle to an AMQP_VALUE that stores a boolean value.] */ + result->type = AMQP_TYPE_BOOL; + result->value.bool_value = value; + } + + return result; +} + +int amqpvalue_get_boolean(AMQP_VALUE value, bool* bool_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_009: [If any of the arguments is NULL then amqpvalue_get_boolean shall return a non-zero value.] */ + if ((value == NULL) || + (bool_value == NULL)) + { + LogError("Bad arguments: value = %p, bool_value = %p", + value, bool_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_011: [If the type of the value is not Boolean, then amqpvalue_get_boolean shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_BOOL) + { + LogError("Value is not of type bool"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_008: [amqpvalue_get_boolean shall fill in the bool_value argument the Boolean value stored by the AMQP value indicated by the value argument.] */ + *bool_value = value_data->value.bool_value; + + /* Codes_SRS_AMQPVALUE_01_010: [On success amqpvalue_get_boolean shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_005: [1.6.3 ubyte Integer in the range 0 to 28 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_ubyte(unsigned char value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result != NULL) + { + /* Codes_SRS_AMQPVALUE_01_032: [amqpvalue_create_ubyte shall return a handle to an AMQP_VALUE that stores a unsigned char value.] */ + result->type = AMQP_TYPE_UBYTE; + result->value.ubyte_value = value; + } + + return result; +} + +int amqpvalue_get_ubyte(AMQP_VALUE value, unsigned char* ubyte_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_036: [If any of the arguments is NULL then amqpvalue_get_ubyte shall return a non-zero value.] */ + if ((value == NULL) || + (ubyte_value == NULL)) + { + LogError("Bad arguments: value = %p, ubyte_value = %p", + value, ubyte_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_037: [If the type of the value is not ubyte (was not created with amqpvalue_create_ubyte), then amqpvalue_get_ubyte shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_UBYTE) + { + LogError("Value is not of type UBYTE"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_034: [amqpvalue_get_ubyte shall fill in the ubyte_value argument the unsigned char value stored by the AMQP value indicated by the value argument.] */ + *ubyte_value = value_data->value.ubyte_value; + + /* Codes_SRS_AMQPVALUE_01_035: [On success amqpvalue_get_ubyte shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_012: [1.6.4 ushort Integer in the range 0 to 216 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_ushort(uint16_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_039: [If allocating the AMQP_VALUE fails then amqpvalue_create_ushort shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_038: [amqpvalue_create_ushort shall return a handle to an AMQP_VALUE that stores an uint16_t value.] */ + result->type = AMQP_TYPE_USHORT; + result->value.ushort_value = value; + } + + return result; +} + +int amqpvalue_get_ushort(AMQP_VALUE value, uint16_t* ushort_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_042: [If any of the arguments is NULL then amqpvalue_get_ushort shall return a non-zero value.] */ + if ((value == NULL) || + (ushort_value == NULL)) + { + LogError("Bad arguments: value = %p, ushort_value = %p", + value, ushort_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_043: [If the type of the value is not ushort (was not created with amqpvalue_create_ushort), then amqpvalue_get_ushort shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_USHORT) + { + LogError("Value is not of type USHORT"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_040: [amqpvalue_get_ushort shall fill in the ushort_value argument the uint16_t value stored by the AMQP value indicated by the value argument.] */ + *ushort_value = value_data->value.ushort_value; + + /* Codes_SRS_AMQPVALUE_01_041: [On success amqpvalue_get_ushort shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_013: [1.6.5 uint Integer in the range 0 to 232 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_uint(uint32_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_045: [If allocating the AMQP_VALUE fails then amqpvalue_create_uint shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_044: [amqpvalue_create_uint shall return a handle to an AMQP_VALUE that stores an uint32_t value.] */ + result->type = AMQP_TYPE_UINT; + result->value.uint_value = value; + } + + return result; +} + +int amqpvalue_get_uint(AMQP_VALUE value, uint32_t* uint_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_079: [If any of the arguments is NULL then amqpvalue_get_uint shall return a non-zero value.] */ + if ((value == NULL) || + (uint_value == NULL)) + { + LogError("Bad arguments: value = %p, uint_value = %p", + value, uint_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_048: [If the type of the value is not uint (was not created with amqpvalue_create_uint), then amqpvalue_get_uint shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_UINT) + { + LogError("Value is not of type UINT"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_046: [amqpvalue_get_uint shall fill in the uint_value argument the uint32_t value stored by the AMQP value indicated by the value argument.] */ + *uint_value = value_data->value.uint_value; + + /* Codes_SRS_AMQPVALUE_01_047: [On success amqpvalue_get_uint shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_014: [1.6.6 ulong Integer in the range 0 to 264 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_ulong(uint64_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_050: [If allocating the AMQP_VALUE fails then amqpvalue_create_ulong shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_049: [amqpvalue_create_ulong shall return a handle to an AMQP_VALUE that stores an uint64_t value.] */ + result->type = AMQP_TYPE_ULONG; + result->value.ulong_value = value; + } + + return result; +} + +int amqpvalue_get_ulong(AMQP_VALUE value, uint64_t* ulong_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_053: [If any of the arguments is NULL then amqpvalue_get_ulong shall return a non-zero value.] */ + if ((value == NULL) || + (ulong_value == NULL)) + { + LogError("Bad arguments: value = %p, ulong_value = %p", + value, ulong_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_054: [If the type of the value is not ulong (was not created with amqpvalue_create_ulong), then amqpvalue_get_ulong shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_ULONG) + { + LogError("Value is not of type ULONG"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_051: [amqpvalue_get_ulong shall fill in the ulong_value argument the ulong64_t value stored by the AMQP value indicated by the value argument.] */ + *ulong_value = value_data->value.ulong_value; + + /* Codes_SRS_AMQPVALUE_01_052: [On success amqpvalue_get_ulong shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_015: [1.6.7 byte Integer in the range -(27) to 27 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_byte(char value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_056: [If allocating the AMQP_VALUE fails then amqpvalue_create_byte shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_055: [amqpvalue_create_byte shall return a handle to an AMQP_VALUE that stores a char value.] */ + result->type = AMQP_TYPE_BYTE; + result->value.byte_value = value; + } + + return result; +} + +int amqpvalue_get_byte(AMQP_VALUE value, char* byte_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_059: [If any of the arguments is NULL then amqpvalue_get_byte shall return a non-zero value.] */ + if ((value == NULL) || + (byte_value == NULL)) + { + LogError("Bad arguments: value = %p, byte_value = %p", + value, byte_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_060: [If the type of the value is not byte (was not created with amqpvalue_create_byte), then amqpvalue_get_byte shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_BYTE) + { + LogError("Value is not of type BYTE"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_057: [amqpvalue_get_byte shall fill in the byte_value argument the char value stored by the AMQP value indicated by the value argument.] */ + *byte_value = value_data->value.byte_value; + + /* Codes_SRS_AMQPVALUE_01_058: [On success amqpvalue_get_byte shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_016: [1.6.8 short Integer in the range -(215) to 215 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_short(int16_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_062: [If allocating the AMQP_VALUE fails then amqpvalue_create_short shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_061: [amqpvalue_create_short shall return a handle to an AMQP_VALUE that stores an int16_t value.] */ + result->type = AMQP_TYPE_SHORT; + result->value.short_value = value; + } + return result; +} + +int amqpvalue_get_short(AMQP_VALUE value, int16_t* short_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_065: [If any of the arguments is NULL then amqpvalue_get_short shall return a non-zero value.] */ + if ((value == NULL) || + (short_value == NULL)) + { + LogError("Bad arguments: value = %p, short_value = %p", + value, short_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_066: [If the type of the value is not short (was not created with amqpvalue_create_short), then amqpvalue_get_short shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_SHORT) + { + LogError("Value is not of type SHORT"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_063: [amqpvalue_get_short shall fill in the short_value argument the int16_t value stored by the AMQP value indicated by the value argument.] */ + *short_value = value_data->value.short_value; + + /* Codes_SRS_AMQPVALUE_01_064: [On success amqpvalue_get_short shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_017: [1.6.9 int Integer in the range -(231) to 231 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_int(int32_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_068: [If allocating the AMQP_VALUE fails then amqpvalue_create_int shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_067: [amqpvalue_create_int shall return a handle to an AMQP_VALUE that stores an int32_t value.] */ + result->type = AMQP_TYPE_INT; + result->value.int_value = value; + } + + return result; +} + +int amqpvalue_get_int(AMQP_VALUE value, int32_t* int_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_071: [If any of the arguments is NULL then amqpvalue_get_int shall return a non-zero value.] */ + if ((value == NULL) || + (int_value == NULL)) + { + LogError("Bad arguments: value = %p, int_value = %p", + value, int_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_072: [If the type of the value is not int (was not created with amqpvalue_create_int), then amqpvalue_get_int shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_INT) + { + LogError("Value is not of type INT"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_069: [amqpvalue_get_int shall fill in the int_value argument the int32_t value stored by the AMQP value indicated by the value argument.] */ + *int_value = value_data->value.int_value; + + /* Codes_SRS_AMQPVALUE_01_070: [On success amqpvalue_get_int shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_018: [1.6.10 long Integer in the range -(263) to 263 - 1 inclusive.] */ +AMQP_VALUE amqpvalue_create_long(int64_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_074: [If allocating the AMQP_VALUE fails then amqpvalue_create_long shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_073: [amqpvalue_create_long shall return a handle to an AMQP_VALUE that stores an int64_t value.] */ + result->type = AMQP_TYPE_LONG; + result->value.long_value = value; + } + + return result; +} + +int amqpvalue_get_long(AMQP_VALUE value, int64_t* long_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_077: [If any of the arguments is NULL then amqpvalue_get_long shall return a non-zero value.] */ + if ((value == NULL) || + (long_value == NULL)) + { + LogError("Bad arguments: value = %p, long_value = %p", + value, long_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_078: [If the type of the value is not long (was not created with amqpvalue_create_long), then amqpvalue_get_long shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_LONG) + { + LogError("Value is not of type LONG"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_075: [amqpvalue_get_long shall fill in the long_value argument the int64_t value stored by the AMQP value indicated by the value argument.] */ + *long_value = value_data->value.long_value; + + /* Codes_SRS_AMQPVALUE_01_076: [On success amqpvalue_get_long shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_019: [1.6.11 float 32-bit floating point number (IEEE 754-2008 binary32).] */ +AMQP_VALUE amqpvalue_create_float(float value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_081: [If allocating the AMQP_VALUE fails then amqpvalue_create_float shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_080: [amqpvalue_create_float shall return a handle to an AMQP_VALUE that stores a float value.] */ + result->type = AMQP_TYPE_FLOAT; + result->value.float_value = value; + } + + return result; +} + +int amqpvalue_get_float(AMQP_VALUE value, float* float_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_084: [If any of the arguments is NULL then amqpvalue_get_float shall return a non-zero value.] */ + if ((value == NULL) || + (float_value == NULL)) + { + LogError("Bad arguments: value = %p, float_value = %p", + value, float_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_085: [If the type of the value is not float (was not created with amqpvalue_create_float), then amqpvalue_get_float shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_FLOAT) + { + LogError("Value is not of type FLOAT"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_082: [amqpvalue_get_float shall fill in the float_value argument the float value stored by the AMQP value indicated by the value argument.] */ + *float_value = value_data->value.float_value; + + /* Codes_SRS_AMQPVALUE_01_083: [On success amqpvalue_get_float shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_020: [1.6.12 double 64-bit floating point number (IEEE 754-2008 binary64).] */ +AMQP_VALUE amqpvalue_create_double(double value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_087: [If allocating the AMQP_VALUE fails then amqpvalue_create_double shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_086: [amqpvalue_create_double shall return a handle to an AMQP_VALUE that stores a double value.] */ + result->type = AMQP_TYPE_DOUBLE; + result->value.double_value = value; + } + + return result; +} + +int amqpvalue_get_double(AMQP_VALUE value, double* double_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_090: [If any of the arguments is NULL then amqpvalue_get_double shall return a non-zero value.] */ + if ((value == NULL) || + (double_value == NULL)) + { + LogError("Bad arguments: value = %p, double_value = %p", + value, double_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_091: [If the type of the value is not double (was not created with amqpvalue_create_double), then amqpvalue_get_double shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_DOUBLE) + { + LogError("Value is not of type DOUBLE"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_088: [amqpvalue_get_double shall fill in the double_value argument the double value stored by the AMQP value indicated by the value argument.] */ + *double_value = value_data->value.double_value; + + /* Codes_SRS_AMQPVALUE_01_089: [On success amqpvalue_get_double shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_024: [1.6.16 char A single Unicode character.] */ +AMQP_VALUE amqpvalue_create_char(uint32_t value) +{ + AMQP_VALUE result; + + /* Codes_SRS_AMQPVALUE_01_098: [If the code point value is outside of the allowed range [0, 0x10FFFF] then amqpvalue_create_char shall return NULL.] */ + if (value > 0x10FFFF) + { + LogError("Invalid value for a Unicode char"); + result = NULL; + } + else + { + result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_093: [If allocating the AMQP_VALUE fails then amqpvalue_create_char shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_092: [amqpvalue_create_char shall return a handle to an AMQP_VALUE that stores a single UTF-32 character value.] */ + result->type = AMQP_TYPE_CHAR; + result->value.char_value = value; + } + } + + return result; +} + +int amqpvalue_get_char(AMQP_VALUE value, uint32_t* char_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_096: [If any of the arguments is NULL then amqpvalue_get_char shall return a non-zero value.] */ + if ((value == NULL) || + (char_value == NULL)) + { + LogError("Bad arguments: value = %p, double_value = %p", + value, char_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_097: [If the type of the value is not char (was not created with amqpvalue_create_char), then amqpvalue_get_char shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_CHAR) + { + LogError("Value is not of type CHAR"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_094: [amqpvalue_get_char shall fill in the char_value argument the UTF32 char value stored by the AMQP value indicated by the value argument.] */ + *char_value = value_data->value.char_value; + + /* Codes_SRS_AMQPVALUE_01_095: [On success amqpvalue_get_char shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_025: [1.6.17 timestamp An absolute point in time.] */ +AMQP_VALUE amqpvalue_create_timestamp(int64_t value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_108: [If allocating the AMQP_VALUE fails then amqpvalue_create_timestamp shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_107: [amqpvalue_create_timestamp shall return a handle to an AMQP_VALUE that stores an uint64_t value that represents a millisecond precision Unix time.] */ + result->type = AMQP_TYPE_TIMESTAMP; + result->value.timestamp_value = value; + } + + return result; +} + +int amqpvalue_get_timestamp(AMQP_VALUE value, int64_t* timestamp_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_111: [If any of the arguments is NULL then amqpvalue_get_timestamp shall return a non-zero value.] */ + if ((value == NULL) || + (timestamp_value == NULL)) + { + LogError("Bad arguments: value = %p, timestamp_value = %p", + value, timestamp_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_112: [If the type of the value is not timestamp (was not created with amqpvalue_create_timestamp), then amqpvalue_get_timestamp shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_TIMESTAMP) + { + LogError("Value is not of type TIMESTAMP"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_109: [amqpvalue_get_timestamp shall fill in the timestamp_value argument the timestamp value stored by the AMQP value indicated by the value argument.] */ + *timestamp_value = value_data->value.timestamp_value; + + /* Codes_SRS_AMQPVALUE_01_110: [On success amqpvalue_get_timestamp shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_026: [1.6.18 uuid A universally unique identifier as defined by RFC-4122 section 4.1.2 .] */ +AMQP_VALUE amqpvalue_create_uuid(uuid value) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_114: [If allocating the AMQP_VALUE fails then amqpvalue_create_uuid shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_113: [amqpvalue_create_uuid shall return a handle to an AMQP_VALUE that stores an uuid value that represents a unique identifier per RFC-4122 section 4.1.2.] */ + result->type = AMQP_TYPE_UUID; + (void)memcpy(&result->value.uuid_value, value, 16); + } + + return result; +} + +int amqpvalue_get_uuid(AMQP_VALUE value, uuid* uuid_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_117: [If any of the arguments is NULL then amqpvalue_get_uuid shall return a non-zero value.] */ + if ((value == NULL) || + (uuid_value == NULL)) + { + LogError("Bad arguments: value = %p, uuid_value = %p", + value, uuid_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_118: [If the type of the value is not uuid (was not created with amqpvalue_create_uuid), then amqpvalue_get_uuid shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_UUID) + { + LogError("Value is not of type UUID"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_115: [amqpvalue_get_uuid shall fill in the uuid_value argument the uuid value stored by the AMQP value indicated by the value argument.] */ + (void)memcpy(*uuid_value, value_data->value.uuid_value, 16); + + /* Codes_SRS_AMQPVALUE_01_116: [On success amqpvalue_get_uuid shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_027: [1.6.19 binary A sequence of octets.] */ +AMQP_VALUE amqpvalue_create_binary(amqp_binary value) +{ + AMQP_VALUE result; + if ((value.bytes == NULL) && + (value.length > 0)) + { + /* Codes_SRS_AMQPVALUE_01_129: [If value.data is NULL and value.length is positive then amqpvalue_create_binary shall return NULL.] */ + LogError("NULL bytes with non-zero length"); + result = NULL; + } + else + { + result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_128: [If allocating the AMQP_VALUE fails then amqpvalue_create_binary shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_127: [amqpvalue_create_binary shall return a handle to an AMQP_VALUE that stores a sequence of bytes.] */ + result->type = AMQP_TYPE_BINARY; + if (value.length > 0) + { + result->value.binary_value.bytes = malloc(value.length); + } + else + { + result->value.binary_value.bytes = NULL; + } + + result->value.binary_value.length = value.length; + + if ((result->value.binary_value.bytes == NULL) && (value.length > 0)) + { + /* Codes_SRS_AMQPVALUE_01_128: [If allocating the AMQP_VALUE fails then amqpvalue_create_binary shall return NULL.] */ + LogError("Could not allocate memory for binary payload of AMQP value"); + free(result); + result = NULL; + } + else + { + if (value.length > 0) + { + (void)memcpy((void*)result->value.binary_value.bytes, value.bytes, value.length); + } + } + } + } + + return result; +} + +int amqpvalue_get_binary(AMQP_VALUE value, amqp_binary* binary_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_132: [If any of the arguments is NULL then amqpvalue_get_binary shall return NULL.] */ + if ((value == NULL) || + (binary_value == NULL)) + { + LogError("Bad arguments: value = %p, binary_value = %p", + value, binary_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + /* Codes_SRS_AMQPVALUE_01_133: [If the type of the value is not binary (was not created with amqpvalue_create_binary), then amqpvalue_get_binary shall return NULL.] */ + if (value_data->type != AMQP_TYPE_BINARY) + { + LogError("Value is not of type BINARY"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_131: [amqpvalue_get_binary shall yield a pointer to the sequence of bytes held by the AMQP_VALUE in binary_value.data and fill in the binary_value.length argument the number of bytes held in the binary value.] */ + binary_value->length = value_data->value.binary_value.length; + binary_value->bytes = value_data->value.binary_value.bytes; + + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_135: [amqpvalue_create_string shall return a handle to an AMQP_VALUE that stores a sequence of Unicode characters.] */ +/* Codes_SRS_AMQPVALUE_01_028: [1.6.20 string A sequence of Unicode characters.] */ +AMQP_VALUE amqpvalue_create_string(const char* value) +{ + AMQP_VALUE result; + if (value == NULL) + { + LogError("NULL argument value"); + result = NULL; + } + else + { + size_t length = strlen(value); + + result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_136: [If allocating the AMQP_VALUE fails then amqpvalue_create_string shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + result->type = AMQP_TYPE_STRING; + result->value.string_value.chars = (char*)malloc(length + 1); + if (result->value.string_value.chars == NULL) + { + /* Codes_SRS_AMQPVALUE_01_136: [If allocating the AMQP_VALUE fails then amqpvalue_create_string shall return NULL.] */ + LogError("Could not allocate memory for string AMQP value"); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->value.string_value.chars, value, length + 1); + } + } + } + + return result; +} + +int amqpvalue_get_string(AMQP_VALUE value, const char** string_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_139: [If any of the arguments is NULL then amqpvalue_get_string shall return a non-zero value.] */ + if ((value == NULL) || + (string_value == NULL)) + { + LogError("Bad arguments: value = %p, string_value = %p", + value, string_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_140: [If the type of the value is not string (was not created with amqpvalue_create_string), then amqpvalue_get_string shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_STRING) + { + LogError("Value is not of type STRING"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_138: [amqpvalue_get_string shall yield a pointer to the sequence of bytes held by the AMQP_VALUE in string_value.] */ + *string_value = value_data->value.string_value.chars; + + /* Codes_SRS_AMQPVALUE_01_141: [On success, amqpvalue_get_string shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_029: [1.6.21 symbol Symbolic values from a constrained domain.] */ +AMQP_VALUE amqpvalue_create_symbol(const char* value) +{ + AMQP_VALUE result; + if (value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_400: [If value is NULL, amqpvalue_create_symbol shall fail and return NULL.] */ + LogError("NULL argument"); + result = NULL; + } + else + { + size_t length = strlen(value); + if (length > UINT32_MAX) + { + /* Codes_SRS_AMQPVALUE_01_401: [ If the string pointed to by value is longer than 2^32-1 then amqpvalue_create_symbol shall return NULL. ]*/ + LogError("string too long to be represented as a symbol"); + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_143: [If allocating the AMQP_VALUE fails then amqpvalue_create_symbol shall return NULL.] */ + result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + LogError("Cannot allocate memory for AMQP value"); + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_142: [amqpvalue_create_symbol shall return a handle to an AMQP_VALUE that stores a symbol (ASCII string) value.] */ + result->type = AMQP_TYPE_SYMBOL; + result->value.symbol_value.chars = (char*)malloc(length + 1); + if (result->value.symbol_value.chars == NULL) + { + LogError("Cannot allocate memory for symbol string"); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->value.symbol_value.chars, value, length + 1); + } + } + } + } + + return result; +} + +int amqpvalue_get_symbol(AMQP_VALUE value, const char** symbol_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_147: [If any of the arguments is NULL then amqpvalue_get_symbol shall return a non-zero value.] */ + if ((value == NULL) || + (symbol_value == NULL)) + { + LogError("Bad arguments: value = %p, symbol_value = %p", + value, symbol_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_148: [If the type of the value is not symbol (was not created with amqpvalue_create_symbol), then amqpvalue_get_symbol shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_SYMBOL) + { + LogError("Value is not of type SYMBOL"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_145: [amqpvalue_get_symbol shall fill in the symbol_value the symbol value string held by the AMQP_VALUE.] */ + *symbol_value = value_data->value.symbol_value.chars; + + /* Codes_SRS_AMQPVALUE_01_146: [On success, amqpvalue_get_symbol shall return 0.] */ + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_030: [1.6.22 list A sequence of polymorphic values.] */ +AMQP_VALUE amqpvalue_create_list(void) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_150: [If allocating the AMQP_VALUE fails then amqpvalue_create_list shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_149: [amqpvalue_create_list shall return a handle to an AMQP_VALUE that stores a list.] */ + result->type = AMQP_TYPE_LIST; + + /* Codes_SRS_AMQPVALUE_01_151: [The list shall have an initial size of zero.] */ + result->value.list_value.count = 0; + result->value.list_value.items = NULL; + } + + return result; +} + +int amqpvalue_set_list_item_count(AMQP_VALUE value, uint32_t list_size) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_155: [If the value argument is NULL, amqpvalue_set_list_item_count shall return a non-zero value.] */ + if (value == NULL) + { + LogError("NULL list value"); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if (value_data->type != AMQP_TYPE_LIST) + { + /* Codes_SRS_AMQPVALUE_01_156: [If the value is not of type list, then amqpvalue_set_list_item_count shall return a non-zero value.] */ + LogError("Value is not of type LIST"); + result = __FAILURE__; + } + else + { + if (value_data->value.list_value.count < list_size) + { + AMQP_VALUE* new_list; + + /* Codes_SRS_AMQPVALUE_01_152: [amqpvalue_set_list_item_count shall resize an AMQP list.] */ + new_list = (AMQP_VALUE*)realloc(value_data->value.list_value.items, list_size * sizeof(AMQP_VALUE)); + if (new_list == NULL) + { + /* Codes_SRS_AMQPVALUE_01_154: [If allocating memory for the list according to the new size fails, then amqpvalue_set_list_item_count shall return a non-zero value, while preserving the existing list contents.] */ + LogError("Could not reallocate list memory"); + result = __FAILURE__; + } + else + { + uint32_t i; + value_data->value.list_value.items = new_list; + + /* Codes_SRS_AMQPVALUE_01_162: [When a list is grown a null AMQP_VALUE shall be inserted as new list items to fill the list up to the new size.] */ + for (i = value_data->value.list_value.count; i < list_size; i++) + { + new_list[i] = amqpvalue_create_null(); + if (new_list[i] == NULL) + { + LogError("Could not create NULL AMQP value to be inserted in list"); + break; + } + } + + if (i < list_size) + { + /* Codes_SRS_AMQPVALUE_01_154: [If allocating memory for the list according to the new size fails, then amqpvalue_set_list_item_count shall return a non-zero value, while preserving the existing list contents.] */ + uint32_t j; + for (j = value_data->value.list_value.count; j < i; j++) + { + amqpvalue_destroy(new_list[j]); + } + + result = __FAILURE__; + } + else + { + value_data->value.list_value.count = list_size; + + /* Codes_SRS_AMQPVALUE_01_153: [On success amqpvalue_set_list_item_count shall return 0.] */ + result = 0; + } + } + } + else if (value_data->value.list_value.count > list_size) + { + uint32_t i; + + /* Codes_SRS_AMQPVALUE_01_161: [When the list is shrunk, the extra items shall be freed by using amqp_value_destroy.] */ + for (i = list_size; i < value_data->value.list_value.count; i++) + { + amqpvalue_destroy(value_data->value.list_value.items[i]); + } + + value_data->value.list_value.count = list_size; + + /* Codes_SRS_AMQPVALUE_01_153: [On success amqpvalue_set_list_item_count shall return 0.] */ + result = 0; + } + else + { + /* Codes_SRS_AMQPVALUE_01_153: [On success amqpvalue_set_list_item_count shall return 0.] */ + result = 0; + } + } + } + + return result; +} + +int amqpvalue_get_list_item_count(AMQP_VALUE value, uint32_t* size) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_159: [If any of the arguments are NULL, amqpvalue_get_list_item_count shall return a non-zero value.] */ + if ((value == NULL) || + (size == NULL)) + { + LogError("Bad arguments: value = %p, size = %p", + value, size); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_160: [If the AMQP_VALUE is not a list then amqpvalue_get_list_item_count shall return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_LIST) + { + LogError("Value is not of type LIST"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_157: [amqpvalue_get_list_item_count shall fill in the size argument the number of items held by the AMQP list.] */ + *size = value_data->value.list_value.count; + + /* Codes_SRS_AMQPVALUE_01_158: [On success amqpvalue_get_list_item_count shall return 0.] */ + result = 0; + } + } + + return result; +} + +int amqpvalue_set_list_item(AMQP_VALUE value, uint32_t index, AMQP_VALUE list_item_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_165: [If value or list_item_value is NULL, amqpvalue_set_list_item shall fail and return a non-zero value.] */ + if (value == NULL) + { + LogError("NULL list value"); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if (value_data->type != AMQP_TYPE_LIST) + { + LogError("Value is not of type LIST"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_168: [The item stored at the index-th position in the list shall be a clone of list_item_value.] */ + AMQP_VALUE cloned_item = amqpvalue_clone(list_item_value); + if (cloned_item == NULL) + { + /* Codes_SRS_AMQPVALUE_01_170: [When amqpvalue_set_list_item fails due to not being able to clone the item or grow the list, the list shall not be altered.] */ + /* Codes_SRS_AMQPVALUE_01_169: [If cloning the item fails, amqpvalue_set_list_item shall fail and return a non-zero value.] */ + LogError("Could not clone list item"); + result = __FAILURE__; + } + else + { + if (index >= value_data->value.list_value.count) + { + AMQP_VALUE* new_list = (AMQP_VALUE*)realloc(value_data->value.list_value.items, (index + 1) * sizeof(AMQP_VALUE)); + if (new_list == NULL) + { + /* Codes_SRS_AMQPVALUE_01_170: [When amqpvalue_set_list_item fails due to not being able to clone the item or grow the list, the list shall not be altered.] */ + LogError("Could not reallocate list storage"); + amqpvalue_destroy(cloned_item); + result = __FAILURE__; + } + else + { + uint32_t i; + + value_data->value.list_value.items = new_list; + + for (i = value_data->value.list_value.count; i < index; i++) + { + new_list[i] = amqpvalue_create_null(); + if (new_list[i] == NULL) + { + LogError("Could not allocate NULL value for list entries"); + break; + } + } + + if (i < index) + { + /* Codes_SRS_AMQPVALUE_01_170: [When amqpvalue_set_list_item fails due to not being able to clone the item or grow the list, the list shall not be altered.] */ + uint32_t j; + + for (j = value_data->value.list_value.count; j < i; j++) + { + amqpvalue_destroy(new_list[j]); + } + + amqpvalue_destroy(cloned_item); + + /* Codes_SRS_AMQPVALUE_01_172: [If growing the list fails, then amqpvalue_set_list_item shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + else + { + value_data->value.list_value.count = index + 1; + value_data->value.list_value.items[index] = cloned_item; + + /* Codes_SRS_AMQPVALUE_01_164: [On success amqpvalue_set_list_item shall return 0.] */ + result = 0; + } + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_167: [Any previous value stored at the position index in the list shall be freed by using amqpvalue_destroy.] */ + amqpvalue_destroy(value_data->value.list_value.items[index]); + + /* Codes_SRS_AMQPVALUE_01_163: [amqpvalue_set_list_item shall replace the item at the 0 based index-th position in the list identified by the value argument with the AMQP_VALUE specified by list_item_value.] */ + value_data->value.list_value.items[index] = cloned_item; + + /* Codes_SRS_AMQPVALUE_01_164: [On success amqpvalue_set_list_item shall return 0.] */ + result = 0; + } + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_list_item(AMQP_VALUE value, size_t index) +{ + AMQP_VALUE result; + + if (value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_174: [If the value argument is NULL, amqpvalue_get_list_item shall fail and return NULL.] */ + LogError("NULL list value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_177: [If value is not a list then amqpvalue_get_list_item shall fail and return NULL.] */ + if (value_data->type != AMQP_TYPE_LIST) + { + LogError("Value is not of type LIST"); + result = NULL; + } + /* Codes_SRS_AMQPVALUE_01_175: [If index is greater or equal to the number of items in the list then amqpvalue_get_list_item shall fail and return NULL.] */ + else if (value_data->value.list_value.count <= index) + { + LogError("Bad index value %u", (unsigned int)index); + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_173: [amqpvalue_get_list_item shall return a copy of the AMQP_VALUE stored at the 0 based position index in the list identified by value.] */ + /* Codes_SRS_AMQPVALUE_01_176: [If cloning the item at position index fails, then amqpvalue_get_list_item shall fail and return NULL.] */ + result = amqpvalue_clone(value_data->value.list_value.items[index]); + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_178: [amqpvalue_create_map shall create an AMQP value that holds a map and return a handle to it.] */ +/* Codes_SRS_AMQPVALUE_01_031: [1.6.23 map A polymorphic mapping from distinct keys to values.] */ +AMQP_VALUE amqpvalue_create_map(void) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_179: [If allocating memory for the map fails, then amqpvalue_create_map shall return NULL.] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + result->type = AMQP_TYPE_MAP; + + /* Codes_SRS_AMQPVALUE_01_180: [The number of key/value pairs in the newly created map shall be zero.] */ + result->value.map_value.pairs = NULL; + result->value.map_value.pair_count = 0; + } + + return result; +} + +int amqpvalue_set_map_value(AMQP_VALUE map, AMQP_VALUE key, AMQP_VALUE value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_183: [If any of the arguments are NULL, amqpvalue_set_map_value shall fail and return a non-zero value.] */ + if ((map == NULL) || + (key == NULL) || + (value == NULL)) + { + LogError("Bad arguments: map = %p, key = %p, value = %p", + map, key, value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)map; + + /* Codes_SRS_AMQPVALUE_01_196: [If the map argument is not an AMQP value created with the amqpvalue_create_map function than amqpvalue_set_map_value shall fail and return a non-zero value.] */ + if (value_data->type != AMQP_TYPE_MAP) + { + LogError("Value is not of type MAP"); + result = __FAILURE__; + } + else + { + AMQP_VALUE cloned_value; + + /* Codes_SRS_AMQPVALUE_01_185: [When storing the key or value, their contents shall be cloned.] */ + cloned_value = amqpvalue_clone(value); + if (cloned_value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_188: [If cloning the value fails, amqpvalue_set_map_value shall fail and return a non-zero value.] */ + LogError("Could not clone value to set in the map"); + result = __FAILURE__; + } + else + { + uint32_t i; + AMQP_VALUE cloned_key; + + for (i = 0; i < value_data->value.map_value.pair_count; i++) + { + if (amqpvalue_are_equal(value_data->value.map_value.pairs[i].key, key)) + { + LogError("Could not allocate NULL value for map entries"); + break; + } + } + + if (i < value_data->value.map_value.pair_count) + { + /* Codes_SRS_AMQPVALUE_01_184: [If the key already exists in the map, its value shall be replaced with the value provided by the value argument.] */ + /* Codes_SRS_AMQPVALUE_01_125: [A map in which there exist two identical key values is invalid.] */ + amqpvalue_destroy(value_data->value.map_value.pairs[i].value); + value_data->value.map_value.pairs[i].value = cloned_value; + + /* Codes_SRS_AMQPVALUE_01_182: [On success amqpvalue_set_map_value shall return 0.] */ + result = 0; + } + else + { + /* Codes_SRS_AMQPVALUE_01_185: [When storing the key or value, their contents shall be cloned.] */ + cloned_key = amqpvalue_clone(key); + if (cloned_key == NULL) + { + /* Codes_SRS_AMQPVALUE_01_187: [If cloning the key fails, amqpvalue_set_map_value shall fail and return a non-zero value.] */ + amqpvalue_destroy(cloned_value); + LogError("Could not clone key for map"); + result = __FAILURE__; + } + else + { + AMQP_MAP_KEY_VALUE_PAIR* new_pairs = (AMQP_MAP_KEY_VALUE_PAIR*)realloc(value_data->value.map_value.pairs, (value_data->value.map_value.pair_count + 1) * sizeof(AMQP_MAP_KEY_VALUE_PAIR)); + if (new_pairs == NULL) + { + /* Codes_SRS_AMQPVALUE_01_186: [If allocating memory to hold a new key/value pair fails, amqpvalue_set_map_value shall fail and return a non-zero value.] */ + amqpvalue_destroy(cloned_key); + amqpvalue_destroy(cloned_value); + LogError("Could not reallocate memory for map"); + result = __FAILURE__; + } + else + { + value_data->value.map_value.pairs = new_pairs; + + /* Codes_SRS_AMQPVALUE_01_181: [amqpvalue_set_map_value shall set the value in the map identified by the map argument for a key/value pair identified by the key argument.] */ + value_data->value.map_value.pairs[value_data->value.map_value.pair_count].key = cloned_key; + value_data->value.map_value.pairs[value_data->value.map_value.pair_count].value = cloned_value; + value_data->value.map_value.pair_count++; + + /* Codes_SRS_AMQPVALUE_01_182: [On success amqpvalue_set_map_value shall return 0.] */ + result = 0; + } + } + } + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_map_value(AMQP_VALUE map, AMQP_VALUE key) +{ + AMQP_VALUE result; + + /* Codes_SRS_AMQPVALUE_01_190: [If any argument is NULL, amqpvalue_get_map_value shall return NULL.] */ + if ((map == NULL) || + (key == NULL)) + { + LogError("Bad arguments: map = %p, key = %p", + map, key); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)map; + + /* Codes_SRS_AMQPVALUE_01_197: [If the map argument is not an AMQP value created with the amqpvalue_create_map function than amqpvalue_get_map_value shall return NULL.] */ + if (value_data->type != AMQP_TYPE_MAP) + { + LogError("Value is not of type MAP"); + result = NULL; + } + else + { + uint32_t i; + + for (i = 0; i < value_data->value.map_value.pair_count; i++) + { + if (amqpvalue_are_equal(value_data->value.map_value.pairs[i].key, key)) + { + break; + } + } + + if (i == value_data->value.map_value.pair_count) + { + /* Codes_SRS_AMQPVALUE_01_191: [If the key cannot be found, amqpvalue_get_map_value shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_189: [amqpvalue_get_map_value shall return the value whose key is identified by the key argument.] */ + /* Codes_SRS_AMQPVALUE_01_192: [The returned value shall be a clone of the actual value stored in the map.] */ + result = amqpvalue_clone(value_data->value.map_value.pairs[i].value); + } + } + } + + return result; +} + +int amqpvalue_get_map_pair_count(AMQP_VALUE map, uint32_t* pair_count) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_195: [If any of the arguments is NULL, amqpvalue_get_map_pair_count shall fail and return a non-zero value.] */ + if ((map == NULL) || + (pair_count == NULL)) + { + LogError("Bad arguments: map = %p, pair_count = %p", + map, pair_count); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)map; + + if (value_data->type != AMQP_TYPE_MAP) + { + /* Codes_SRS_AMQPVALUE_01_198: [If the map argument is not an AMQP value created with the amqpvalue_create_map function then amqpvalue_get_map_pair_count shall fail and return a non-zero value.] */ + LogError("Value is not of type MAP"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_193: [amqpvalue_get_map_pair_count shall fill in the number of key/value pairs in the map in the pair_count argument.] */ + *pair_count = value_data->value.map_value.pair_count; + + /* Codes_SRS_AMQPVALUE_01_194: [On success amqpvalue_get_map_pair_count shall return 0.] */ + result = 0; + } + } + + return result; +} + +int amqpvalue_get_map_key_value_pair(AMQP_VALUE map, uint32_t index, AMQP_VALUE* key, AMQP_VALUE* value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_201: [If any of the map, key or value arguments is NULL, amqpvalue_get_map_key_value_pair shall fail and return a non-zero value.] */ + if ((map == NULL) || + (key == NULL) || + (value == NULL)) + { + LogError("Bad arguments: map = %p, key = %p, value = %p", + map, key, value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)map; + + if (value_data->type != AMQP_TYPE_MAP) + { + /* Codes_SRS_AMQPVALUE_01_205: [If the map argument is not an AMQP value created with the amqpvalue_create_map function then amqpvalue_get_map_key_value_pair shall fail and return a non-zero value.] */ + LogError("Value is not of type MAP"); + result = __FAILURE__; + } + else if (value_data->value.map_value.pair_count <= index) + { + /* Codes_SRS_AMQPVALUE_01_204: [If the index argument is greater or equal to the number of key/value pairs in the map then amqpvalue_get_map_key_value_pair shall fail and return a non-zero value.] */ + LogError("Index out of range: %u", (unsigned int)index); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_199: [amqpvalue_get_map_key_value_pair shall fill in the key and value arguments copies of the key/value pair on the 0 based position index in a map.] */ + *key = amqpvalue_clone(value_data->value.map_value.pairs[index].key); + if (*key == NULL) + { + /* Codes_SRS_AMQPVALUE_01_202: [If cloning the key fails, amqpvalue_get_map_key_value_pair shall fail and return a non-zero value.] */ + LogError("Could not clone index %u key", (unsigned int)index); + result = __FAILURE__; + } + else + { + *value = amqpvalue_clone(value_data->value.map_value.pairs[index].value); + if (*value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_203: [If cloning the value fails, amqpvalue_get_map_key_value_pair shall fail and return a non-zero value.] */ + amqpvalue_destroy(*key); + LogError("Could not clone index %u value", (unsigned int)index); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_200: [On success amqpvalue_get_map_key_value_pair shall return 0.] */ + result = 0; + } + } + } + } + + return result; +} + +int amqpvalue_get_map(AMQP_VALUE value, AMQP_VALUE* map_value) +{ + int result; + + if ((value == NULL) || + (map_value == NULL)) + { + LogError("Bad arguments: value = %p, map_value = %p", + value, map_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if (value_data->type != AMQP_TYPE_MAP) + { + LogError("Value is not of type MAP"); + result = __FAILURE__; + } + else + { + *map_value = value; + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_397: [1.6.24 array A sequence of values of a single type.] */ +AMQP_VALUE amqpvalue_create_array(void) +{ + AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + /* Codes_SRS_AMQPVALUE_01_405: [ If allocating memory for the array fails, then `amqpvalue_create_array` shall return NULL. ] */ + LogError("Could not allocate memory for AMQP value"); + } + else + { + /* Codes_SRS_AMQPVALUE_01_404: [ `amqpvalue_create_array` shall return a handle to an AMQP_VALUE that stores an array. ] */ + result->type = AMQP_TYPE_ARRAY; + + /* Codes_SRS_AMQPVALUE_01_406: [ The array shall have an initial size of zero. ] */ + result->value.array_value.items = NULL; + result->value.array_value.count = 0; + } + + return result; +} + +int amqpvalue_get_array_item_count(AMQP_VALUE value, uint32_t* count) +{ + int result; + + /* Tests_SRS_AMQPVALUE_01_421: [ If any of the arguments is NULL, `amqpvalue_get_array_item_count` shall fail and return a non-zero value. ]*/ + if ((value == NULL) || + (count == NULL)) + { + LogError("Bad arguments: value = %p, count = %p", + value, count); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_422: [ If the array argument is not an AMQP value created with the `amqpvalue_create_array` function then `amqpvalue_get_array_item_count` shall fail and return a non-zero value. ]*/ + if (value_data->type != AMQP_TYPE_ARRAY) + { + LogError("Value is not of type ARRAY"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_419: [ `amqpvalue_get_array_item_count` shall return in `count` the number of items in the array. ]*/ + *count = value_data->value.array_value.count; + + /* Codes_SRS_AMQPVALUE_01_420: [ On success `amqpvalue_get_array_item_count` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int amqpvalue_add_array_item(AMQP_VALUE value, AMQP_VALUE array_item_value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_409: [ If `value` or `array_item_value` is NULL, amqpvalue_add_array_item shall fail and return a non-zero value. ]*/ + if (value == NULL) + { + LogError("NULL value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_413: [ If the `value` argument is not an AMQP array created with the `amqpvalue_create_array` function than `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */ + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if (value_data->type != AMQP_TYPE_ARRAY) + { + LogError("Value is not of type ARRAY"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_425: [ If the type of `array_item_value` does not match that of items already in the array then `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */ + AMQP_VALUE_DATA* array_item_value_data = (AMQP_VALUE_DATA*)array_item_value; + if ((value_data->value.array_value.count > 0) && + (array_item_value_data->type != value_data->value.array_value.items[0]->type)) + { + LogError("Cannot put different types in the same array"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_410: [ The item stored at the n-th position in the array shall be a clone of `array_item_value`. ] */ + AMQP_VALUE cloned_item = amqpvalue_clone(array_item_value); + if (cloned_item == NULL) + { + /* Codes_SRS_AMQPVALUE_01_423: [ When `amqpvalue_add_array_item` fails due to not being able to clone the item or grow the array, the array shall not be altered. ] */ + /* Codes_SRS_AMQPVALUE_01_412: [ If cloning the item fails, `amqpvalue_add_array_item` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone value to put in the array"); + result = __FAILURE__; + } + else + { + AMQP_VALUE* new_array = (AMQP_VALUE*)realloc(value_data->value.array_value.items, (value_data->value.array_value.count + 1) * sizeof(AMQP_VALUE)); + if (new_array == NULL) + { + /* Codes_SRS_AMQPVALUE_01_423: [ When `amqpvalue_add_array_item` fails due to not being able to clone the item or grow the array, the array shall not be altered. ] */ + /* Codes_SRS_AMQPVALUE_01_424: [ If growing the array fails, then `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */ + amqpvalue_destroy(cloned_item); + LogError("Cannot resize array"); + result = __FAILURE__; + } + else + { + value_data->value.array_value.items = new_array; + + /* Codes_SRS_AMQPVALUE_01_407: [ `amqpvalue_add_array_item` shall add the AMQP_VALUE specified by `array_item_value` at the 0 based n-th position in the array. ]*/ + value_data->value.array_value.items[value_data->value.array_value.count] = cloned_item; + value_data->value.array_value.count++; + + /* Codes_SRS_AMQPVALUE_01_408: [ On success `amqpvalue_add_array_item` shall return 0. ]*/ + result = 0; + } + } + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_array_item(AMQP_VALUE value, uint32_t index) +{ + AMQP_VALUE result; + + if (value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_416: [ If the `value` argument is NULL, `amqpvalue_get_array_item` shall fail and return NULL. ] */ + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + /* Codes_SRS_AMQPVALUE_01_418: [ If value is not an array then `amqpvalue_get_array_item` shall fail and return NULL. ] */ + if (value_data->type != AMQP_TYPE_ARRAY) + { + LogError("Value is not of type ARRAY"); + result = NULL; + } + /* Codes_SRS_AMQPVALUE_01_417: [ If `index` is greater or equal to the number of items in the array then `amqpvalue_get_array_item` shall fail and return NULL. ] */ + else if (value_data->value.array_value.count <= index) + { + LogError("Index out of range: %u", (unsigned int)index); + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_414: [ `amqpvalue_get_array_item` shall return a copy of the AMQP_VALUE stored at the 0 based position `index` in the array identified by `value`. ] */ + /* Codes_SRS_AMQPVALUE_01_426: [ If cloning the item at position `index` fails, then `amqpvalue_get_array_item` shall fail and return NULL. ] */ + result = amqpvalue_clone(value_data->value.array_value.items[index]); + } + } + + return result; +} + +int amqpvalue_get_array(AMQP_VALUE value, AMQP_VALUE* array_value) +{ + int result; + + if ((value == NULL) || + (array_value == NULL)) + { + LogError("Bad arguments: value = %p, array_value = %p", + value, array_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if (value_data->type != AMQP_TYPE_ARRAY) + { + LogError("Value is not of type ARRAY"); + result = __FAILURE__; + } + else + { + *array_value = value; + result = 0; + } + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_206: [amqpvalue_are_equal shall return true if the contents of value1 and value2 are equal.] */ +bool amqpvalue_are_equal(AMQP_VALUE value1, AMQP_VALUE value2) +{ + bool result; + + /* Codes_SRS_AMQPVALUE_01_207: [If value1 and value2 are NULL, amqpvalue_are_equal shall return true.] */ + if ((value1 == NULL) && + (value2 == NULL)) + { + LogError("Bad arguments: value1 = %p, value2 = %p", + value1, value2); + result = true; + } + /* Codes_SRS_AMQPVALUE_01_208: [If one of the arguments is NULL and the other is not, amqpvalue_are_equal shall return false.] */ + else if ((value1 != value2) && ((value1 == NULL) || (value2 == NULL))) + { + result = false; + } + else + { + AMQP_VALUE_DATA* value1_data = (AMQP_VALUE_DATA*)value1; + AMQP_VALUE_DATA* value2_data = (AMQP_VALUE_DATA*)value2; + + /* Codes_SRS_AMQPVALUE_01_209: [If the types for value1 and value2 are different amqpvalue_are_equal shall return false.] */ +#if _MSC_VER +#pragma warning(suppress: 28182) /* The compiler states that value2_data can be NULL, but it cannot. And there are tests for it. */ +#endif + if (value1_data->type != value2_data->type) + { + result = false; + } + else + { + switch (value1_data->type) + { + default: + result = false; + break; + + case AMQP_TYPE_NULL: + /* Codes_SRS_AMQPVALUE_01_210: [- null: always equal.] */ + result = true; + break; + + case AMQP_TYPE_BOOL: + /* Codes_SRS_AMQPVALUE_01_211: [- boolean: compare the bool content.] */ + result = (value1_data->value.bool_value == value2_data->value.bool_value); + break; + + case AMQP_TYPE_UBYTE: + /* Codes_SRS_AMQPVALUE_01_212: [- ubyte: compare the unsigned char content.] */ + result = (value1_data->value.ubyte_value == value2_data->value.ubyte_value); + break; + + case AMQP_TYPE_USHORT: + /* Codes_SRS_AMQPVALUE_01_213: [- ushort: compare the uint16_t content.] */ + result = (value1_data->value.ushort_value == value2_data->value.ushort_value); + break; + + case AMQP_TYPE_UINT: + /* Codes_SRS_AMQPVALUE_01_214: [- uint: compare the uint32_t content.] */ + result = (value1_data->value.uint_value == value2_data->value.uint_value); + break; + + case AMQP_TYPE_ULONG: + /* Codes_SRS_AMQPVALUE_01_215: [- ulong: compare the uint64_t content.] */ + result = (value1_data->value.ulong_value == value2_data->value.ulong_value); + break; + + case AMQP_TYPE_BYTE: + /* Codes_SRS_AMQPVALUE_01_216: [- byte: compare the char content.] */ + result = (value1_data->value.byte_value == value2_data->value.byte_value); + break; + + case AMQP_TYPE_SHORT: + /* Codes_SRS_AMQPVALUE_01_217: [- short: compare the int16_t content.] */ + result = (value1_data->value.short_value == value2_data->value.short_value); + break; + + case AMQP_TYPE_INT: + /* Codes_SRS_AMQPVALUE_01_218: [- int: compare the int32_t content.] */ + result = (value1_data->value.int_value == value2_data->value.int_value); + break; + + case AMQP_TYPE_LONG: + /* Codes_SRS_AMQPVALUE_01_219: [- long: compare the int64_t content.] */ + result = (value1_data->value.long_value == value2_data->value.long_value); + break; + + case AMQP_TYPE_FLOAT: + /* Codes_SRS_AMQPVALUE_01_224: [- float: compare the float content.] */ + result = (value1_data->value.float_value == value2_data->value.float_value); + break; + + case AMQP_TYPE_DOUBLE: + /* Codes_SRS_AMQPVALUE_01_225: [- double: compare the double content.] */ + result = (value1_data->value.double_value == value2_data->value.double_value); + break; + + case AMQP_TYPE_CHAR: + /* Codes_SRS_AMQPVALUE_01_226: [- char: compare the UNICODE character.] */ + result = (value1_data->value.char_value == value2_data->value.char_value); + break; + + case AMQP_TYPE_TIMESTAMP: + /* Codes_SRS_AMQPVALUE_01_227: [- timestamp: compare the underlying 64 bit integer.] */ + result = (value1_data->value.timestamp_value == value2_data->value.timestamp_value); + break; + + case AMQP_TYPE_UUID: + /* Codes_SRS_AMQPVALUE_01_228: [- uuid: compare all uuid bytes.] */ + result = (memcmp(value1_data->value.uuid_value, value2_data->value.uuid_value, sizeof(value1_data->value.uuid_value)) == 0); + break; + + case AMQP_TYPE_BINARY: + /* Codes_SRS_AMQPVALUE_01_229: [- binary: compare all binary bytes.] */ + result = (value1_data->value.binary_value.length == value2_data->value.binary_value.length) && + (memcmp(value1_data->value.binary_value.bytes, value2_data->value.binary_value.bytes, value1_data->value.binary_value.length) == 0); + break; + + case AMQP_TYPE_STRING: + /* Codes_SRS_AMQPVALUE_01_230: [- string: compare all string characters.] */ + result = (strcmp(value1_data->value.string_value.chars, value2_data->value.string_value.chars) == 0); + break; + + case AMQP_TYPE_SYMBOL: + /* Codes_SRS_AMQPVALUE_01_263: [- symbol: compare all symbol characters.] */ + result = (strcmp(value1_data->value.symbol_value.chars, value2_data->value.symbol_value.chars) == 0); + break; + + case AMQP_TYPE_LIST: + { + /* Codes_SRS_AMQPVALUE_01_231: [- list: compare list item count and each element.] */ + if (value1_data->value.list_value.count != value2_data->value.list_value.count) + { + result = false; + } + else + { + uint32_t i; + + for (i = 0; i < value1_data->value.list_value.count; i++) + { + /* Codes_SRS_AMQPVALUE_01_232: [Nesting shall be considered in comparison.] */ + if (!amqpvalue_are_equal(value1_data->value.list_value.items[i], value2_data->value.list_value.items[i])) + { + break; + } + } + + result = (i == value1_data->value.list_value.count); + } + + break; + } + case AMQP_TYPE_ARRAY: + { + /* Codes_SRS_AMQPVALUE_01_427: [- array: compare array item count and each element. ] */ + if (value1_data->value.array_value.count != value2_data->value.array_value.count) + { + result = false; + } + else + { + uint32_t i; + + for (i = 0; i < value1_data->value.array_value.count; i++) + { + /* Codes_SRS_AMQPVALUE_01_428: [ Nesting shall be considered in comparison. ] */ + if (!amqpvalue_are_equal(value1_data->value.array_value.items[i], value2_data->value.array_value.items[i])) + { + break; + } + } + + result = (i == value1_data->value.array_value.count); + } + + break; + } + case AMQP_TYPE_MAP: + { + /* Codes_SRS_AMQPVALUE_01_233: [- map: compare map pair count and each key/value pair.] */ + if (value1_data->value.map_value.pair_count != value2_data->value.map_value.pair_count) + { + result = false; + } + else + { + uint32_t i; + + /* Codes_SRS_AMQPVALUE_01_126: [Unless known to be otherwise, maps MUST be considered to be ordered, that is, the order of the key-value pairs is semantically important and two maps which are different only in the order in which their key-value pairs are encoded are not equal.] */ + for (i = 0; i < value1_data->value.map_value.pair_count; i++) + { + /* Codes_SRS_AMQPVALUE_01_234: [Nesting shall be considered in comparison.] */ + if ((!amqpvalue_are_equal(value1_data->value.map_value.pairs[i].key, value2_data->value.map_value.pairs[i].key)) || + (!amqpvalue_are_equal(value1_data->value.map_value.pairs[i].value, value2_data->value.map_value.pairs[i].value))) + { + break; + } + } + + result = (i == value1_data->value.map_value.pair_count); + } + + break; + } + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_clone(AMQP_VALUE value) +{ + AMQP_VALUE result; + + if (value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_402: [ If `value` is NULL, `amqpvalue_clone` shall return NULL. ]*/ + LogError("NULL value"); + result = NULL; + } + else + { + /* Codes_SRS_AMQPVALUE_01_235: [amqpvalue_clone shall clone the value passed as argument and return a new non-NULL handle to the cloned AMQP value.] */ + INC_REF(AMQP_VALUE_DATA, value); + result = value; + } + + return result; +} + +AMQP_TYPE amqpvalue_get_type(AMQP_VALUE value) +{ + AMQP_VALUE_DATA* amqpvalue_data = (AMQP_VALUE_DATA*)value; + return amqpvalue_data->type; +} + +static int output_byte(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, unsigned char b) +{ + int result; + + if (encoder_output != NULL) + { + /* Codes_SRS_AMQPVALUE_01_267: [amqpvalue_encode shall pass the encoded bytes to the encoder_output function.] */ + /* Codes_SRS_AMQPVALUE_01_268: [On each call to the encoder_output function, amqpvalue_encode shall also pass the context argument.] */ + result = encoder_output(context, &b, 1); + } + else + { + result = 0; + } + + return result; +} + +static int output_bytes(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, const void* bytes, size_t length) +{ + int result; + + if (encoder_output != NULL) + { + /* Codes_SRS_AMQPVALUE_01_267: [amqpvalue_encode shall pass the encoded bytes to the encoder_output function.] */ + /* Codes_SRS_AMQPVALUE_01_268: [On each call to the encoder_output function, amqpvalue_encode shall also pass the context argument.] */ + result = encoder_output(context, (const unsigned char*)bytes, length); + } + else + { + result = 0; + } + + return result; +} + +static int encode_boolean(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, bool value) +{ + int result; + + if (value == false) + { + /* Codes_SRS_AMQPVALUE_01_273: [<encoding name="false" code="0x42" category="fixed" width="0" label="the boolean value false"/>] */ + if (output_byte(encoder_output, context, 0x42) != 0) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding boolean"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_272: [<encoding name="true" code="0x41" category="fixed" width="0" label="the boolean value true"/>] */ + if (output_byte(encoder_output, context, 0x41) != 0) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding boolean"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +static int encode_ubyte(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, unsigned char value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_275: [<encoding code="0x50" category="fixed" width="1" label="8-bit unsigned integer"/>] */ + if ((output_byte(encoder_output, context, 0x50) != 0) || + (output_byte(encoder_output, context, value) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding ubyte"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_ushort(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint16_t value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_276: [<encoding code="0x60" category="fixed" width="2" label="16-bit unsigned integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x60) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value & 0xFF)) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding ushort"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_uint(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint32_t value) +{ + int result; + + if (value == 0) + { + /* uint0 */ + /* Codes_SRS_AMQPVALUE_01_279: [<encoding name="uint0" code="0x43" category="fixed" width="0" label="the uint value 0"/>] */ + if (output_byte(encoder_output, context, 0x43) != 0) + { + LogError("Failed encoding uint"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else if (value <= 255) + { + /* smalluint */ + /* Codes_SRS_AMQPVALUE_01_278: [<encoding name="smalluint" code="0x52" category="fixed" width="1" label="unsigned integer value in the range 0 to 255 inclusive"/>] */ + if ((output_byte(encoder_output, context, 0x52) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding uint"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_277: [<encoding code="0x70" category="fixed" width="4" label="32-bit unsigned integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x70) != 0) || + (output_byte(encoder_output, context, (value >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding uint"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_ulong(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint64_t value) +{ + int result; + if (value == 0) + { + /* ulong0 */ + /* Codes_SRS_AMQPVALUE_01_282: [<encoding name="ulong0" code="0x44" category="fixed" width="0" label="the ulong value 0"/>] */ + if (output_byte(encoder_output, context, 0x44) != 0) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding ulong"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else if (value <= 255) + { + /* smallulong */ + /* Codes_SRS_AMQPVALUE_01_281: [<encoding name="smallulong" code="0x53" category="fixed" width="1" label="unsigned long value in the range 0 to 255 inclusive"/>] */ + if ((output_byte(encoder_output, context, 0x53) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding ulong"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_280: [<encoding code="0x80" category="fixed" width="8" label="64-bit unsigned integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x80) != 0) || + (output_byte(encoder_output, context, (value >> 56) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 48) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 40) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 32) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding ulong"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_byte(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, char value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_283: [<encoding code="0x51" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + if ((output_byte(encoder_output, context, 0x51) != 0) || + (output_byte(encoder_output, context, value) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding byte"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_short(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, int16_t value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_284: [<encoding code="0x61" category="fixed" width="2" label="16-bit two's-complement integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x61) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value & 0xFF)) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding short"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_int(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, int32_t value) +{ + int result; + + if ((value <= 127) && (value >= -128)) + { + /* Codes_SRS_AMQPVALUE_01_286: [<encoding name="smallint" code="0x54" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + if ((output_byte(encoder_output, context, 0x54) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding int"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_285: [<encoding code="0x71" category="fixed" width="4" label="32-bit two's-complement integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x71) != 0) || + (output_byte(encoder_output, context, (value >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding int"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_long(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, int64_t value) +{ + int result; + + if ((value <= 127) && (value >= -128)) + { + /* Codes_SRS_AMQPVALUE_01_288: [<encoding name="smalllong" code="0x55" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + if ((output_byte(encoder_output, context, 0x55) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding long"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_287: [<encoding code="0x81" category="fixed" width="8" label="64-bit two's-complement integer in network byte order"/>] */ + if ((output_byte(encoder_output, context, 0x81) != 0) || + (output_byte(encoder_output, context, (value >> 56) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 48) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 40) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 32) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding long"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_float(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, float value) +{ + int result; + + uint32_t value_as_uint32 = *((uint32_t*)(void*)&value); + /* Codes_SRS_AMQPVALUE_01_289: [\<encoding name="ieee-754" code="0x72" category="fixed" width="4" label="IEEE 754-2008 binary32"/>] */ + if ((output_byte(encoder_output, context, 0x72) != 0) || + (output_byte(encoder_output, context, (value_as_uint32 >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint32 >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint32 >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint32) & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failure encoding bytes for float"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_double(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, double value) +{ + int result; + + uint64_t value_as_uint64 = *((uint64_t*)(void*)&value); + /* Codes_SRS_AMQPVALUE_01_290: [\<encoding name="ieee-754" code="0x82" category="fixed" width="8" label="IEEE 754-2008 binary64"/>] */ + if ((output_byte(encoder_output, context, 0x82) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 56) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 48) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 40) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 32) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64 >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value_as_uint64) & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failure encoding bytes for double"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_timestamp(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, int64_t value) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_295: [<encoding name="ms64" code="0x83" category="fixed" width="8" label="64-bit two's-complement integer representing milliseconds since the unix epoch"/>] */ + if ((output_byte(encoder_output, context, 0x83) != 0) || + (output_byte(encoder_output, context, (value >> 56) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 48) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 40) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 32) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (value >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, value & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding timestamp"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_uuid(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uuid uuid) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_296: [<encoding code="0x98" category="fixed" width="16" label="UUID as defined in section 4.1.2 of RFC-4122"/>] */ + if ((output_byte(encoder_output, context, 0x98) != 0) || + (output_bytes(encoder_output, context, uuid, 16) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding uuid"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + + return result; +} + +static int encode_binary(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, const unsigned char* value, uint32_t length) +{ + int result; + if (length <= 255) + { + /* Codes_SRS_AMQPVALUE_01_297: [<encoding name="vbin8" code="0xa0" category="variable" width="1" label="up to 2^8 - 1 octets of binary data"/>] */ + if ((output_byte(encoder_output, context, 0xA0) != 0) || + (output_byte(encoder_output, context, (unsigned char)length) != 0) || + ((length > 0) && (output_bytes(encoder_output, context, value, length) != 0))) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding binary"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_298: [<encoding name="vbin32" code="0xb0" category="variable" width="4" label="up to 2^32 - 1 octets of binary data"/>] */ + if ((output_byte(encoder_output, context, 0xB0) != 0) || + (output_byte(encoder_output, context, (length >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, length & 0xFF) != 0) || + (output_bytes(encoder_output, context, value, length) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding binary"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_string(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, const char* value) +{ + int result; + size_t length = strlen(value); + + if (length <= 255) + { + /* Codes_SRS_AMQPVALUE_01_299: [<encoding name="str8-utf8" code="0xa1" category="variable" width="1" label="up to 2^8 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + if ((output_byte(encoder_output, context, (unsigned char)0xA1) != 0) || + (output_byte(encoder_output, context, (unsigned char)length) != 0) || + (output_bytes(encoder_output, context, value, length) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding string"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_300: [<encoding name="str32-utf8" code="0xb1" category="variable" width="4" label="up to 2^32 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + if ((output_byte(encoder_output, context, 0xB1) != 0) || + (output_byte(encoder_output, context, (length >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, length & 0xFF) != 0) || + (output_bytes(encoder_output, context, value, length) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding string"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_symbol(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, const char* value) +{ + int result; + size_t length = strlen(value); + + if (length <= 255) + { + /* Codes_SRS_AMQPVALUE_01_301: [<encoding name="sym8" code="0xa3" category="variable" width="1" label="up to 2^8 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + if ((output_byte(encoder_output, context, (unsigned char)0xA3) != 0) || + (output_byte(encoder_output, context, (unsigned char)length) != 0) || + (output_bytes(encoder_output, context, value, length) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding symbol"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + /* Codes_SRS_AMQPVALUE_01_302: [<encoding name="sym32" code="0xb3" category="variable" width="4" label="up to 2^32 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + if ((output_byte(encoder_output, context, 0xB3) != 0) || + (output_byte(encoder_output, context, (length >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (length >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, length & 0xFF) != 0) || + /* Codes_SRS_AMQPVALUE_01_122: [Symbols are encoded as ASCII characters [ASCII].] */ + (output_bytes(encoder_output, context, value, length) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding symbol"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + return result; +} + +static int encode_list(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint32_t count, AMQP_VALUE* items) +{ + size_t i; + int result; + + if (count == 0) + { + /* Codes_SRS_AMQPVALUE_01_303: [<encoding name="list0" code="0x45" category="fixed" width="0" label="the empty list (i.e. the list with no elements)"/>] */ + if (output_byte(encoder_output, context, 0x45) != 0) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Could not output list constructor byte"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + uint32_t size = 0; + + /* get the size of all items in the list */ + for (i = 0; i < count; i++) + { + size_t item_size; + if (amqpvalue_get_encoded_size(items[i], &item_size) != 0) + { + LogError("Could not get encoded size for element %u of the list", (unsigned int)i); + break; + } + + if ((item_size > UINT32_MAX) || + size + (uint32_t)item_size < size) + { + LogError("Overflow in list size computation"); + break; + } + + size = (uint32_t)(size + item_size); + } + + if (i < count) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + else + { + if ((count <= 255) && (size < 255)) + { + size++; + + /* Codes_SRS_AMQPVALUE_01_304: [<encoding name="list8" code="0xc0" category="compound" width="1" label="up to 2^8 - 1 list elements with total size less than 2^8 octets"/>] */ + if ((output_byte(encoder_output, context, 0xC0) != 0) || + /* size */ + (output_byte(encoder_output, context, (size & 0xFF)) != 0) || + /* count */ + (output_byte(encoder_output, context, (count & 0xFF)) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + size += 4; + + /* Codes_SRS_AMQPVALUE_01_305: [<encoding name="list32" code="0xd0" category="compound" width="4" label="up to 2^32 - 1 list elements with total size less than 2^32 octets"/>] */ + if ((output_byte(encoder_output, context, 0xD0) != 0) || + /* size */ + (output_byte(encoder_output, context, (size >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, size & 0xFF) != 0) || + /* count */ + (output_byte(encoder_output, context, (count >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (count >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (count >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, count & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Failed encoding list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + if (result == 0) + { + for (i = 0; i < count; i++) + { + if (amqpvalue_encode(items[i], encoder_output, context) != 0) + { + break; + } + } + + if (i < count) + { + LogError("Failed encoding element %u of the list", (unsigned int)i); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + + return result; +} + +static int encode_map(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint32_t count, AMQP_MAP_KEY_VALUE_PAIR* pairs) +{ + size_t i; + int result; + + uint32_t size = 0; + + /* Codes_SRS_AMQPVALUE_01_124: [Map encodings MUST contain an even number of items (i.e. an equal number of keys and values).] */ + uint32_t elements = count * 2; + + /* get the size of all items in the list */ + for (i = 0; i < count; i++) + { + size_t item_size; + if (amqpvalue_get_encoded_size(pairs[i].key, &item_size) != 0) + { + LogError("Could not get encoded size for key element %u of the map", (unsigned int)i); + break; + } + + if ((item_size > UINT32_MAX) || + size + (uint32_t)item_size < size) + { + LogError("Encoded data is more than the max size for a map"); + break; + } + + size = (uint32_t)(size + item_size); + + if (amqpvalue_get_encoded_size(pairs[i].value, &item_size) != 0) + { + LogError("Could not get encoded size for value element %u of the map", (unsigned int)i); + break; + } + + if ((item_size > UINT32_MAX) || + size + (uint32_t)item_size < size) + { + LogError("Encoded data is more than the max size for a map"); + break; + } + + size = (uint32_t)(size + item_size); + } + + if (i < count) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + else + { + if ((elements <= 255) && (size < 255)) + { + size++; + + /* Codes_SRS_AMQPVALUE_01_306: [<encoding name="map8" code="0xc1" category="compound" width="1" label="up to 2^8 - 1 octets of encoded map data"/>] */ + if ((output_byte(encoder_output, context, 0xC1) != 0) || + /* size */ + (output_byte(encoder_output, context, (size & 0xFF)) != 0) || + /* count */ + (output_byte(encoder_output, context, (elements & 0xFF)) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Could not encode map header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + size += 4; + + /* Codes_SRS_AMQPVALUE_01_307: [<encoding name="map32" code="0xd1" category="compound" width="4" label="up to 2^32 - 1 octets of encoded map data"/>] */ + if ((output_byte(encoder_output, context, 0xD1) != 0) || + /* size */ + (output_byte(encoder_output, context, (size >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, size & 0xFF) != 0) || + /* count */ + (output_byte(encoder_output, context, (elements >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (elements >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (elements >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, elements & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Could not encode map header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + if (result == 0) + { + /* Codes_SRS_AMQPVALUE_01_123: [A map is encoded as a compound value where the constituent elements form alternating key value pairs.] */ + for (i = 0; i < count; i++) + { + if ((amqpvalue_encode(pairs[i].key, encoder_output, context) != 0) || + (amqpvalue_encode(pairs[i].value, encoder_output, context) != 0)) + { + LogError("Failed encoding map element %u", (unsigned int)i); + break; + } + } + + if (i < count) + { + LogError("Could not encode map"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +static int encode_array(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint32_t count, AMQP_VALUE* items) +{ + size_t i; + int result; + + uint32_t size = 0; + + /* get the size of all items in the array */ + for (i = 0; i < count; i++) + { + size_t item_size; + if (amqpvalue_get_encoded_size(items[i], &item_size) != 0) + { + LogError("Could not get encoded size for element %u of the array", (unsigned int)i); + break; + } + + if ((item_size > UINT32_MAX) || + size + (uint32_t)item_size < size) + { + LogError("Overflow in array size computation"); + break; + } + + size = (uint32_t)(size + item_size); + + } + + if (i < count) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + result = __FAILURE__; + } + else + { + if ((count <= 255) && (size < 255)) + { + size++; + + /* Codes_SRS_AMQPVALUE_01_306: [<encoding name="map8" code="0xE0" category="compound" width="1" label="up to 2^8 - 1 octets of encoded map data"/>] */ + if ((output_byte(encoder_output, context, 0xE0) != 0) || + /* size */ + (output_byte(encoder_output, context, (size & 0xFF)) != 0) || + /* count */ + (output_byte(encoder_output, context, (count & 0xFF)) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Could not encode map header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + else + { + size += 4; + + /* Codes_SRS_AMQPVALUE_01_307: [<encoding name="map32" code="0xF0" category="compound" width="4" label="up to 2^32 - 1 octets of encoded map data"/>] */ + if ((output_byte(encoder_output, context, 0xF0) != 0) || + /* size */ + (output_byte(encoder_output, context, (size >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (size >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, size & 0xFF) != 0) || + /* count */ + (output_byte(encoder_output, context, (count >> 24) & 0xFF) != 0) || + (output_byte(encoder_output, context, (count >> 16) & 0xFF) != 0) || + (output_byte(encoder_output, context, (count >> 8) & 0xFF) != 0) || + (output_byte(encoder_output, context, count & 0xFF) != 0)) + { + /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */ + LogError("Could not encode array"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = 0; + } + } + + if (result == 0) + { + for (i = 0; i < count; i++) + { + if (amqpvalue_encode(items[i], encoder_output, context) != 0) + { + break; + } + } + + if (i < count) + { + LogError("Failed encoding element %u of the array", (unsigned int)i); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +static int encode_descriptor_header(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context) +{ + int result; + + if (output_byte(encoder_output, context, 0x00) != 0) + { + LogError("Failed encoding descriptor header"); + result = __FAILURE__; + } + else + { + result = 0; + } + + return result; +} + +/* Codes_SRS_AMQPVALUE_01_265: [amqpvalue_encode shall encode the value per the ISO.] */ +int amqpvalue_encode(AMQP_VALUE value, AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_269: [If value or encoder_output are NULL, amqpvalue_encode shall fail and return a non-zero value.] */ + if ((value == NULL) || + (encoder_output == NULL)) + { + LogError("Bad arguments: value = %p, encoder_output = %p", + value, encoder_output); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + switch (value_data->type) + { + default: + /* Codes_SRS_AMQPVALUE_01_271: [If encoding fails due to any error not specifically mentioned here, it shall return a non-zero value.] */ + LogError("Invalid type: %d", (int)value_data->type); + result = __FAILURE__; + break; + + case AMQP_TYPE_NULL: + /* Codes_SRS_AMQPVALUE_01_264: [<encoding code="0x40" category="fixed" width="0" label="the null value"/>] */ + /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */ + result = output_byte(encoder_output, context, (unsigned char)0x40); + break; + + case AMQP_TYPE_BOOL: + result = encode_boolean(encoder_output, context, value_data->value.bool_value); + break; + + case AMQP_TYPE_UBYTE: + result = encode_ubyte(encoder_output, context, value_data->value.ubyte_value); + break; + + case AMQP_TYPE_USHORT: + result = encode_ushort(encoder_output, context, value_data->value.ushort_value); + break; + + case AMQP_TYPE_UINT: + result = encode_uint(encoder_output, context, value_data->value.uint_value); + break; + + case AMQP_TYPE_ULONG: + result = encode_ulong(encoder_output, context, value_data->value.ulong_value); + break; + + case AMQP_TYPE_BYTE: + result = encode_byte(encoder_output, context, value_data->value.byte_value); + break; + + case AMQP_TYPE_SHORT: + result = encode_short(encoder_output, context, value_data->value.short_value); + break; + + case AMQP_TYPE_INT: + result = encode_int(encoder_output, context, value_data->value.int_value); + break; + + case AMQP_TYPE_LONG: + result = encode_long(encoder_output, context, value_data->value.long_value); + break; + + case AMQP_TYPE_FLOAT: + result = encode_float(encoder_output, context, value_data->value.float_value); + break; + + case AMQP_TYPE_DOUBLE: + result = encode_double(encoder_output, context, value_data->value.double_value); + break; + + case AMQP_TYPE_TIMESTAMP: + result = encode_timestamp(encoder_output, context, value_data->value.timestamp_value); + break; + + case AMQP_TYPE_UUID: + result = encode_uuid(encoder_output, context, value_data->value.uuid_value); + break; + + case AMQP_TYPE_BINARY: + result = encode_binary(encoder_output, context, (const unsigned char*)value_data->value.binary_value.bytes, value_data->value.binary_value.length); + break; + + case AMQP_TYPE_STRING: + result = encode_string(encoder_output, context, value_data->value.string_value.chars); + break; + + case AMQP_TYPE_SYMBOL: + result = encode_symbol(encoder_output, context, value_data->value.symbol_value.chars); + break; + + case AMQP_TYPE_LIST: + result = encode_list(encoder_output, context, value_data->value.list_value.count, value_data->value.list_value.items); + break; + + case AMQP_TYPE_ARRAY: + result = encode_array(encoder_output, context, value_data->value.array_value.count, value_data->value.array_value.items); + break; + + case AMQP_TYPE_MAP: + result = encode_map(encoder_output, context, value_data->value.map_value.pair_count, value_data->value.map_value.pairs); + break; + + case AMQP_TYPE_COMPOSITE: + case AMQP_TYPE_DESCRIBED: + { + if ((encode_descriptor_header(encoder_output, context) != 0) || + (amqpvalue_encode(value_data->value.described_value.descriptor, encoder_output, context) != 0) || + (amqpvalue_encode(value_data->value.described_value.value, encoder_output, context) != 0)) + { + LogError("Failed encoding described or composite type"); + result = __FAILURE__; + } + else + { + result = 0; + } + + break; + } + } + } + + return result; +} + +static int count_bytes(void* context, const unsigned char* bytes, size_t length) +{ + size_t* byte_count; + (void)bytes; + + byte_count = (size_t*)context; + *byte_count += length; + + return 0; +} + +int amqpvalue_get_encoded_size(AMQP_VALUE value, size_t* encoded_size) +{ + int result; + + /* Codes_SRS_AMQPVALUE_01_309: [If any argument is NULL, amqpvalue_get_encoded_size shall return a non-zero value.] */ + if ((value == NULL) || + (encoded_size == NULL)) + { + LogError("Bad arguments: value = %p, encoded_size = %p", + value, encoded_size); + result = __FAILURE__; + } + else + { + *encoded_size = 0; + result = amqpvalue_encode(value, count_bytes, encoded_size); + } + + return result; +} + +static void amqpvalue_clear(AMQP_VALUE_DATA* value_data) +{ + switch (value_data->type) + { + default: + break; + + case AMQP_TYPE_BINARY: + if (value_data->value.binary_value.bytes != NULL) + { + free((void*)value_data->value.binary_value.bytes); + } + break; + case AMQP_TYPE_STRING: + if (value_data->value.string_value.chars != NULL) + { + free(value_data->value.string_value.chars); + } + break; + case AMQP_TYPE_SYMBOL: + if (value_data->value.symbol_value.chars != NULL) + { + free(value_data->value.symbol_value.chars); + } + break; + case AMQP_TYPE_LIST: + { + size_t i; + for (i = 0; i < value_data->value.list_value.count; i++) + { + amqpvalue_destroy(value_data->value.list_value.items[i]); + } + + free(value_data->value.list_value.items); + value_data->value.list_value.items = NULL; + break; + } + case AMQP_TYPE_MAP: + { + size_t i; + for (i = 0; i < value_data->value.map_value.pair_count; i++) + { + amqpvalue_destroy(value_data->value.map_value.pairs[i].key); + amqpvalue_destroy(value_data->value.map_value.pairs[i].value); + } + + free(value_data->value.map_value.pairs); + value_data->value.map_value.pairs = NULL; + break; + } + case AMQP_TYPE_ARRAY: + { + size_t i; + for (i = 0; i < value_data->value.array_value.count; i++) + { + amqpvalue_destroy(value_data->value.array_value.items[i]); + } + + free(value_data->value.array_value.items); + value_data->value.array_value.items = NULL; + break; + } + case AMQP_TYPE_COMPOSITE: + case AMQP_TYPE_DESCRIBED: + amqpvalue_destroy(value_data->value.described_value.descriptor); + amqpvalue_destroy(value_data->value.described_value.value); + break; + } + + value_data->type = AMQP_TYPE_UNKNOWN; +} + +void amqpvalue_destroy(AMQP_VALUE value) +{ + /* Codes_SRS_AMQPVALUE_01_315: [If the value argument is NULL, amqpvalue_destroy shall do nothing.] */ + if (value == NULL) + { + LogError("NULL value"); + } + else + { + if (DEC_REF(AMQP_VALUE_DATA, value) == DEC_RETURN_ZERO) + { + /* Codes_SRS_AMQPVALUE_01_314: [amqpvalue_destroy shall free all resources allocated by any of the amqpvalue_create_xxx functions or amqpvalue_clone.] */ + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + amqpvalue_clear(value_data); + free(value); + } + } +} + +static INTERNAL_DECODER_DATA* internal_decoder_create(ON_VALUE_DECODED on_value_decoded, void* callback_context, AMQP_VALUE_DATA* value_data, bool is_internal) +{ + INTERNAL_DECODER_DATA* internal_decoder_data = (INTERNAL_DECODER_DATA*)malloc(sizeof(INTERNAL_DECODER_DATA)); + if (internal_decoder_data == NULL) + { + LogError("Cannot allocate memory for internal decoder structure"); + } + else + { + internal_decoder_data->is_internal = is_internal; + internal_decoder_data->on_value_decoded = on_value_decoded; + internal_decoder_data->on_value_decoded_context = callback_context; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_to_value = value_data; + } + + return internal_decoder_data; +} + +static void internal_decoder_destroy(INTERNAL_DECODER_DATA* internal_decoder) +{ + if (internal_decoder != NULL) + { + internal_decoder_destroy(internal_decoder->inner_decoder); + free(internal_decoder); + } +} + +static void inner_decoder_callback(void* context, AMQP_VALUE decoded_value) +{ + /* API issue: the decoded_value should be removed completely: + TODO: uAMQP: inner_decoder_callback in amqpvalue.c could probably do without the decoded_value ... */ + INTERNAL_DECODER_DATA* internal_decoder_data = (INTERNAL_DECODER_DATA*)context; + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + (void)decoded_value; + inner_decoder->decoder_state = DECODER_STATE_DONE; +} + +static int internal_decoder_decode_bytes(INTERNAL_DECODER_DATA* internal_decoder_data, const unsigned char* buffer, size_t size, size_t* used_bytes) +{ + int result; + size_t initial_size = size; + + if (internal_decoder_data == NULL) + { + /* TODO: investigate if this check is even needed */ + LogError("NULL internal_decoder_data"); + result = __FAILURE__; + } + else + { + result = 0; + /* Codes_SRS_AMQPVALUE_01_322: [amqpvalue_decode_bytes shall process the bytes byte by byte, as a stream.] */ + while ((size > 0) && (internal_decoder_data->decoder_state != DECODER_STATE_DONE)) + { + switch (internal_decoder_data->decoder_state) + { + default: + LogError("Invalid decoder state: %d", (int)internal_decoder_data->decoder_state); + result = __FAILURE__; + break; + + case DECODER_STATE_CONSTRUCTOR: + { + if ((internal_decoder_data->decode_to_value != NULL) && (!internal_decoder_data->is_internal)) + { + amqpvalue_destroy(internal_decoder_data->decode_to_value); + internal_decoder_data->decode_to_value = NULL; + } + + if (internal_decoder_data->decode_to_value == NULL) + { + internal_decoder_data->decode_to_value = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + } + + if (internal_decoder_data->decode_to_value == NULL) + { + LogError("Cannot allocate decode value"); + result = __FAILURE__; + break; + } + + internal_decoder_data->constructor_byte = buffer[0]; + buffer++; + size--; + + switch (internal_decoder_data->constructor_byte) + { + default: + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Invalid constructor byte: 0x%02x", internal_decoder_data->constructor_byte); + result = __FAILURE__; + break; + + case 0x00: /* descriptor */ + { + AMQP_VALUE_DATA* descriptor; + internal_decoder_data->decode_to_value->type = AMQP_TYPE_DESCRIBED; + descriptor = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (descriptor == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for descriptor"); + result = __FAILURE__; + } + else + { + descriptor->type = AMQP_TYPE_UNKNOWN; + internal_decoder_data->decode_to_value->value.described_value.descriptor = descriptor; + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, descriptor, true); + if (internal_decoder_data->inner_decoder == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not create inner decoder for descriptor"); + result = __FAILURE__; + } + else + { + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_value_state.described_value_state.described_value_state = DECODE_DESCRIBED_VALUE_STEP_DESCRIPTOR; + result = 0; + } + } + + break; + } + + /* Codes_SRS_AMQPVALUE_01_329: [<encoding code="0x40" category="fixed" width="0" label="the null value"/>] */ + case 0x40: + { + /* Codes_SRS_AMQPVALUE_01_328: [1.6.1 null Indicates an empty value.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_NULL; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + + break; + } + + /* Codes_SRS_AMQPVALUE_01_331: [<encoding code="0x56" category="fixed" width="1" label="boolean with the octet 0x00 being false and octet 0x01 being true"/>] */ + case 0x56: + { + /* Codes_SRS_AMQPVALUE_01_330: [1.6.2 boolean Represents a true or false value.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_BOOL; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_332: [<encoding name="true" code="0x41" category="fixed" width="0" label="the boolean value true"/>] */ + case 0x41: + { + /* Codes_SRS_AMQPVALUE_01_330: [1.6.2 boolean Represents a true or false value.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_BOOL; + internal_decoder_data->decode_to_value->value.bool_value = true; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_333: [<encoding name="false" code="0x42" category="fixed" width="0" label="the boolean value false"/>] */ + case 0x42: + { + /* Codes_SRS_AMQPVALUE_01_330: [1.6.2 boolean Represents a true or false value.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_BOOL; + internal_decoder_data->decode_to_value->value.bool_value = false; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_335: [<encoding code="0x50" category="fixed" width="1" label="8-bit unsigned integer"/>] */ + case 0x50: + { + /* Codes_SRS_AMQPVALUE_01_334: [1.6.3 ubyte Integer in the range 0 to 28 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_UBYTE; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.ubyte_value = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_337: [<encoding code="0x60" category="fixed" width="2" label="16-bit unsigned integer in network byte order"/>] */ + case 0x60: + { + /* Codes_SRS_AMQPVALUE_01_336: [1.6.4 ushort Integer in the range 0 to 216 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_USHORT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.ushort_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_339: [<encoding code="0x70" category="fixed" width="4" label="32-bit unsigned integer in network byte order"/>] */ + case 0x70: + /* Codes_SRS_AMQPVALUE_01_340: [<encoding name="smalluint" code="0x52" category="fixed" width="1" label="unsigned integer value in the range 0 to 255 inclusive"/>] */ + case 0x52: + { + internal_decoder_data->decode_to_value->type = AMQP_TYPE_UINT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.uint_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_341: [<encoding name="uint0" code="0x43" category="fixed" width="0" label="the uint value 0"/>] */ + case 0x43: + { + /* Codes_SRS_AMQPVALUE_01_338: [1.6.5 uint Integer in the range 0 to 232 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_UINT; + internal_decoder_data->decode_to_value->value.uint_value = 0; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_343: [<encoding code="0x80" category="fixed" width="8" label="64-bit unsigned integer in network byte order"/>] */ + case 0x80: + { + /* Codes_SRS_AMQPVALUE_01_342: [1.6.6 ulong Integer in the range 0 to 264 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_ULONG; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.ulong_value = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + internal_decoder_data->bytes_decoded = 0; + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_344: [<encoding name="smallulong" code="0x53" category="fixed" width="1" label="unsigned long value in the range 0 to 255 inclusive"/>] */ + case 0x53: + { + internal_decoder_data->decode_to_value->type = AMQP_TYPE_ULONG; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.ulong_value = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + internal_decoder_data->bytes_decoded = 0; + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_345: [<encoding name="ulong0" code="0x44" category="fixed" width="0" label="the ulong value 0"/>] */ + case 0x44: + { + /* Codes_SRS_AMQPVALUE_01_342: [1.6.6 ulong Integer in the range 0 to 264 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_ULONG; + internal_decoder_data->decode_to_value->value.ulong_value = 0; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_347: [<encoding code="0x51" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + case 0x51: + { + /* Codes_SRS_AMQPVALUE_01_346: [1.6.7 byte Integer in the range -(27) to 27 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_BYTE; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.byte_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_349: [<encoding code="0x61" category="fixed" width="2" label="16-bit two's-complement integer in network byte order"/>] */ + case 0x61: + { + /* Codes_SRS_AMQPVALUE_01_348: [1.6.8 short Integer in the range -(215) to 215 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_SHORT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.short_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_351: [<encoding code="0x71" category="fixed" width="4" label="32-bit two's-complement integer in network byte order"/>] */ + case 0x71: + { + /* Codes_SRS_AMQPVALUE_01_350: [1.6.9 int Integer in the range -(231) to 231 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_INT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.int_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_352: [<encoding name="smallint" code="0x54" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + case 0x54: + { + /* Codes_SRS_AMQPVALUE_01_350: [1.6.9 int Integer in the range -(231) to 231 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_INT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.int_value = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_354: [<encoding code="0x81" category="fixed" width="8" label="64-bit two's-complement integer in network byte order"/>] */ + case 0x81: + { + /* Codes_SRS_AMQPVALUE_01_353: [1.6.10 long Integer in the range -(263) to 263 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_LONG; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.long_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_355: [<encoding name="smalllong" code="0x55" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + case 0x55: + { + /* Codes_SRS_AMQPVALUE_01_353: [1.6.10 long Integer in the range -(263) to 263 - 1 inclusive.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_LONG; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.long_value = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_289: [\<encoding name="ieee-754" code="0x72" category="fixed" width="4" label="IEEE 754-2008 binary32"/>] */ + case 0x72: + { + /* Codes_SRS_AMQPVALUE_01_019: [1.6.11 float 32-bit floating point number (IEEE 754-2008 binary32).] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_FLOAT; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->bytes_decoded = 0; + *((uint32_t*)&internal_decoder_data->decode_to_value->value.float_value) = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_290: [\<encoding name="ieee-754" code="0x82" category="fixed" width="8" label="IEEE 754-2008 binary64"/>] */ + case 0x82: + { + /* Codes_SRS_AMQPVALUE_01_020: [1.6.12 double 64-bit floating point number (IEEE 754-2008 binary64).] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_DOUBLE; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->bytes_decoded = 0; + *((uint64_t*)&internal_decoder_data->decode_to_value->value.double_value) = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_369: [<encoding name="ms64" code="0x83" category="fixed" width="8" label="64-bit two's-complement integer representing milliseconds since the unix epoch"/>] */ + case 0x83: + { + /* Codes_SRS_AMQPVALUE_01_368: [1.6.17 timestamp An absolute point in time.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_TIMESTAMP; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.timestamp_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_371: [<encoding code="0x98" category="fixed" width="16" label="UUID as defined in section 4.1.2 of RFC-4122"/>] */ + case 0x98: + { + /* Codes_SRS_AMQPVALUE_01_370: [1.6.18 uuid A universally unique identifier as defined by RFC-4122 section 4.1.2 .] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_UUID; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.timestamp_value = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_373: [<encoding name="vbin8" code="0xa0" category="variable" width="1" label="up to 2^8 - 1 octets of binary data"/>] */ + case 0xA0: + /* Codes_SRS_AMQPVALUE_01_374: [<encoding name="vbin32" code="0xb0" category="variable" width="4" label="up to 2^32 - 1 octets of binary data"/>] */ + case 0xB0: + { + /* Codes_SRS_AMQPVALUE_01_372: [1.6.19 binary A sequence of octets.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_BINARY; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.binary_value.length = 0; + internal_decoder_data->decode_to_value->value.binary_value.bytes = NULL; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_376: [<encoding name="str8-utf8" code="0xa1" category="variable" width="1" label="up to 2^8 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + case 0xA1: + /* Codes_SRS_AMQPVALUE_01_377: [<encoding name="str32-utf8" code="0xb1" category="variable" width="4" label="up to 2^32 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + case 0xB1: + { + /* Codes_SRS_AMQPVALUE_01_375: [1.6.20 string A sequence of Unicode characters.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_STRING; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.string_value.chars = NULL; + internal_decoder_data->decode_value_state.string_value_state.length = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_379: [<encoding name="sym8" code="0xa3" category="variable" width="1" label="up to 2^8 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + case 0xA3: + /* Codes_SRS_AMQPVALUE_01_380: [<encoding name="sym32" code="0xb3" category="variable" width="4" label="up to 2^32 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + case 0xB3: + { + /* Codes_SRS_AMQPVALUE_01_378: [1.6.21 symbol Symbolic values from a constrained domain.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_SYMBOL; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.symbol_value.chars = NULL; + internal_decoder_data->decode_value_state.symbol_value_state.length = 0; + internal_decoder_data->bytes_decoded = 0; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_384: [<encoding name="list0" code="0x45" category="fixed" width="0" label="the empty list (i.e. the list with no elements)"/>] */ + case 0x45: + /* Codes_SRS_AMQPVALUE_01_383: [1.6.22 list A sequence of polymorphic values.] */ + internal_decoder_data->decode_to_value->type = AMQP_TYPE_LIST; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->decode_to_value->value.list_value.count = 0; + internal_decoder_data->decode_to_value->value.list_value.items = NULL; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + + /* Codes_SRS_AMQPVALUE_01_385: [<encoding name="list8" code="0xc0" category="compound" width="1" label="up to 2^8 - 1 list elements with total size less than 2^8 octets"/>] */ + case 0xC0: + case 0xD0: + internal_decoder_data->decode_to_value->type = AMQP_TYPE_LIST; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.list_value.count = 0; + internal_decoder_data->decode_to_value->value.list_value.items = NULL; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_value_state.list_value_state.list_value_state = DECODE_LIST_STEP_SIZE; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + result = 0; + break; + + case 0xC1: + case 0xD1: + internal_decoder_data->decode_to_value->type = AMQP_TYPE_MAP; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.map_value.pair_count = 0; + internal_decoder_data->decode_to_value->value.map_value.pairs = NULL; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_value_state.map_value_state.map_value_state = DECODE_MAP_STEP_SIZE; + + result = 0; + break; + + case 0xE0: + case 0xF0: + internal_decoder_data->decode_to_value->type = AMQP_TYPE_ARRAY; + internal_decoder_data->decoder_state = DECODER_STATE_TYPE_DATA; + internal_decoder_data->decode_to_value->value.array_value.count = 0; + internal_decoder_data->decode_to_value->value.array_value.items = NULL; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_value_state.array_value_state.array_value_state = DECODE_ARRAY_STEP_SIZE; + + result = 0; + break; + } + break; + } + + case DECODER_STATE_TYPE_DATA: + { + switch (internal_decoder_data->constructor_byte) + { + default: + LogError("Invalid constructor byte: 0x%02x", internal_decoder_data->constructor_byte); + result = __FAILURE__; + break; + + case 0x00: /* descriptor */ + { + DECODE_DESCRIBED_VALUE_STEP step = internal_decoder_data->decode_value_state.described_value_state.described_value_state; + switch (step) + { + default: + LogError("Invalid described value decode step: %d", step); + result = __FAILURE__; + break; + + case DECODE_DESCRIBED_VALUE_STEP_DESCRIPTOR: + { + size_t inner_used_bytes; + if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, buffer, size, &inner_used_bytes) != 0) + { + LogError("Decoding bytes for described value failed"); + result = __FAILURE__; + } + else + { + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + buffer += inner_used_bytes; + size -= inner_used_bytes; + + if (inner_decoder->decoder_state == DECODER_STATE_DONE) + { + AMQP_VALUE described_value; + internal_decoder_destroy(inner_decoder); + + described_value = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (described_value == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for AMQP value"); + result = __FAILURE__; + } + else + { + described_value->type = AMQP_TYPE_UNKNOWN; + internal_decoder_data->decode_to_value->value.described_value.value = (AMQP_VALUE)described_value; + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, described_value, true); + if (internal_decoder_data->inner_decoder == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not create inner decoder"); + result = __FAILURE__; + } + else + { + internal_decoder_data->decode_value_state.described_value_state.described_value_state = DECODE_DESCRIBED_VALUE_STEP_VALUE; + result = 0; + } + } + } + else + { + result = 0; + } + } + break; + } + case DECODE_DESCRIBED_VALUE_STEP_VALUE: + { + size_t inner_used_bytes; + if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, buffer, size, &inner_used_bytes) != 0) + { + LogError("Decoding bytes for described value failed"); + result = __FAILURE__; + } + else + { + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + buffer += inner_used_bytes; + size -= inner_used_bytes; + + if (inner_decoder->decoder_state == DECODER_STATE_DONE) + { + internal_decoder_destroy(inner_decoder); + internal_decoder_data->inner_decoder = NULL; + + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + break; + } + } + break; + } + case 0x56: + { + /* Codes_SRS_AMQPVALUE_01_331: [<encoding code="0x56" category="fixed" width="1" label="boolean with the octet 0x00 being false and octet 0x01 being true"/>] */ + if (buffer[0] >= 2) + { + LogError("Bad boolean value: %02X", buffer[0]); + result = __FAILURE__; + } + else + { + internal_decoder_data->decode_to_value->value.bool_value = (buffer[0] == 0) ? false : true; + + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + + break; + } + /* Codes_SRS_AMQPVALUE_01_335: [<encoding code="0x50" category="fixed" width="1" label="8-bit unsigned integer"/>] */ + case 0x50: + { + internal_decoder_data->decode_to_value->value.ubyte_value = buffer[0]; + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_337: [<encoding code="0x60" category="fixed" width="2" label="16-bit unsigned integer in network byte order"/>] */ + case 0x60: + { + internal_decoder_data->decode_to_value->value.ushort_value += ((uint16_t)buffer[0]) << ((1 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + if (internal_decoder_data->bytes_decoded == 2) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_339: [<encoding code="0x70" category="fixed" width="4" label="32-bit unsigned integer in network byte order"/>] */ + case 0x70: + { + internal_decoder_data->decode_to_value->value.uint_value += ((uint32_t)buffer[0]) << ((3 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_340: [<encoding name="smalluint" code="0x52" category="fixed" width="1" label="unsigned integer value in the range 0 to 255 inclusive"/>] */ + case 0x52: + { + internal_decoder_data->decode_to_value->value.uint_value = buffer[0]; + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_343: [<encoding code="0x80" category="fixed" width="8" label="64-bit unsigned integer in network byte order"/>] */ + case 0x80: + { + internal_decoder_data->decode_to_value->value.ulong_value += ((uint64_t)buffer[0]) << ((7 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 8) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_344: [<encoding name="smallulong" code="0x53" category="fixed" width="1" label="unsigned long value in the range 0 to 255 inclusive"/>] */ + case 0x53: + { + internal_decoder_data->decode_to_value->value.ulong_value = buffer[0]; + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_347: [<encoding code="0x51" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + case 0x51: + { + internal_decoder_data->decode_to_value->value.byte_value = buffer[0]; + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_349: [<encoding code="0x61" category="fixed" width="2" label="16-bit two's-complement integer in network byte order"/>] */ + case 0x61: + { + internal_decoder_data->decode_to_value->value.short_value = (int16_t)((uint16_t)internal_decoder_data->decode_to_value->value.short_value + (((uint16_t)buffer[0]) << ((1 - internal_decoder_data->bytes_decoded) * 8))); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 2) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_351: [<encoding code="0x71" category="fixed" width="4" label="32-bit two's-complement integer in network byte order"/>] */ + case 0x71: + { + internal_decoder_data->decode_to_value->value.int_value = (int32_t)((uint32_t)internal_decoder_data->decode_to_value->value.int_value + (((uint32_t)buffer[0]) << ((3 - internal_decoder_data->bytes_decoded) * 8))); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_351: [<encoding code="0x71" category="fixed" width="4" label="32-bit two's-complement integer in network byte order"/>] */ + case 0x54: + { + internal_decoder_data->decode_to_value->value.int_value = (int32_t)((int8_t)(buffer[0])); + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_354: [<encoding code="0x81" category="fixed" width="8" label="64-bit two's-complement integer in network byte order"/>] */ + case 0x81: + { + internal_decoder_data->decode_to_value->value.long_value = (int64_t)((uint64_t)internal_decoder_data->decode_to_value->value.long_value + (((uint64_t)buffer[0]) << ((7 - internal_decoder_data->bytes_decoded) * 8))); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 8) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_355: [<encoding name="smalllong" code="0x55" category="fixed" width="1" label="8-bit two's-complement integer"/>] */ + case 0x55: + { + internal_decoder_data->decode_to_value->value.long_value = (int64_t)((int8_t)buffer[0]); + buffer++; + size--; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_289: [\<encoding name="ieee-754" code="0x72" category="fixed" width="4" label="IEEE 754-2008 binary32"/>] */ + case 0x72: + { + *((uint32_t*)&internal_decoder_data->decode_to_value->value.float_value) += ((uint32_t)buffer[0]) << ((3 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_290: [\<encoding name="ieee-754" code="0x82" category="fixed" width="8" label="IEEE 754-2008 binary64"/>]*/ + case 0x82: + { + *((uint64_t*)&internal_decoder_data->decode_to_value->value.double_value) += ((uint64_t)buffer[0]) << ((7 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 8) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_369: [<encoding name="ms64" code="0x83" category="fixed" width="8" label="64-bit two's-complement integer representing milliseconds since the unix epoch"/>] */ + case 0x83: + { + internal_decoder_data->decode_to_value->value.timestamp_value = (int64_t)((uint64_t)internal_decoder_data->decode_to_value->value.timestamp_value + (((uint64_t)buffer[0]) << ((7 - internal_decoder_data->bytes_decoded) * 8))); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 8) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_369: [<encoding name="ms64" code="0x83" category="fixed" width="8" label="64-bit two's-complement integer representing milliseconds since the unix epoch"/>] */ + case 0x98: + { + size_t to_copy = 16 - internal_decoder_data->bytes_decoded; + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(&internal_decoder_data->decode_to_value->value.uuid_value[internal_decoder_data->bytes_decoded], buffer, to_copy); + internal_decoder_data->bytes_decoded += to_copy; + buffer += to_copy; + size -= to_copy; + + /* Codes_SRS_AMQPVALUE_01_327: [If not enough bytes have accumulated to decode a value, the on_value_decoded shall not be called.] */ + if (internal_decoder_data->bytes_decoded == 16) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + break; + } + /* Codes_SRS_AMQPVALUE_01_373: [<encoding name="vbin8" code="0xa0" category="variable" width="1" label="up to 2^8 - 1 octets of binary data"/>] */ + case 0xA0: + { + if (internal_decoder_data->bytes_decoded == 0) + { + internal_decoder_data->decode_to_value->value.binary_value.length = buffer[0]; + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->decode_to_value->value.binary_value.length == 0) + { + internal_decoder_data->decode_to_value->value.binary_value.bytes = NULL; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + internal_decoder_data->decode_to_value->value.binary_value.bytes = (unsigned char*)malloc(internal_decoder_data->decode_to_value->value.binary_value.length); + if (internal_decoder_data->decode_to_value->value.binary_value.bytes == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + LogError("Cannot allocate memory for decoded binary value"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + else + { + size_t to_copy = internal_decoder_data->decode_to_value->value.binary_value.length - (internal_decoder_data->bytes_decoded - 1); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy((unsigned char*)(internal_decoder_data->decode_to_value->value.binary_value.bytes) + (internal_decoder_data->bytes_decoded - 1), buffer, to_copy); + + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_to_value->value.binary_value.length + 1) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + + break; + } + /* Codes_SRS_AMQPVALUE_01_374: [<encoding name="vbin32" code="0xb0" category="variable" width="4" label="up to 2^32 - 1 octets of binary data"/>] */ + case 0xB0: + { + if (internal_decoder_data->bytes_decoded < 4) + { + internal_decoder_data->decode_to_value->value.binary_value.length += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->bytes_decoded == 4) + { + if (internal_decoder_data->decode_to_value->value.binary_value.length == 0) + { + internal_decoder_data->decode_to_value->value.binary_value.bytes = NULL; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + internal_decoder_data->decode_to_value->value.binary_value.bytes = (unsigned char*)malloc(internal_decoder_data->decode_to_value->value.binary_value.length + 1); + if (internal_decoder_data->decode_to_value->value.binary_value.bytes == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Cannot allocate memory for decoded binary value"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + else + { + result = 0; + } + } + else + { + size_t to_copy = internal_decoder_data->decode_to_value->value.binary_value.length - (internal_decoder_data->bytes_decoded - 4); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy((unsigned char*)(internal_decoder_data->decode_to_value->value.binary_value.bytes) + (internal_decoder_data->bytes_decoded - 4), buffer, to_copy); + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_to_value->value.binary_value.length + 4) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + + break; + } + /* Codes_SRS_AMQPVALUE_01_376: [<encoding name="str8-utf8" code="0xa1" category="variable" width="1" label="up to 2^8 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + case 0xA1: + { + if (internal_decoder_data->bytes_decoded == 0) + { + internal_decoder_data->decode_value_state.string_value_state.length = buffer[0]; + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + internal_decoder_data->decode_to_value->value.string_value.chars = (char*)malloc(internal_decoder_data->decode_value_state.string_value_state.length + 1); + if (internal_decoder_data->decode_to_value->value.string_value.chars == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for decoded string value"); + result = __FAILURE__; + } + else + { + if (internal_decoder_data->decode_value_state.string_value_state.length == 0) + { + internal_decoder_data->decode_to_value->value.string_value.chars[0] = '\0'; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + } + else + { + size_t to_copy = internal_decoder_data->decode_value_state.string_value_state.length - (internal_decoder_data->bytes_decoded - 1); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(internal_decoder_data->decode_to_value->value.string_value.chars + (internal_decoder_data->bytes_decoded - 1), buffer, to_copy); + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_value_state.string_value_state.length + 1) + { + internal_decoder_data->decode_to_value->value.string_value.chars[internal_decoder_data->decode_value_state.string_value_state.length] = 0; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + break; + } + /* Codes_SRS_AMQPVALUE_01_377: [<encoding name="str32-utf8" code="0xb1" category="variable" width="4" label="up to 2^32 - 1 octets worth of UTF-8 Unicode (with no byte order mark)"/>] */ + case 0xB1: + { + if (internal_decoder_data->bytes_decoded < 4) + { + internal_decoder_data->decode_value_state.string_value_state.length += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decode_to_value->value.string_value.chars = (char*)malloc(internal_decoder_data->decode_value_state.string_value_state.length + 1); + if (internal_decoder_data->decode_to_value->value.string_value.chars == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for decoded string value"); + result = __FAILURE__; + } + else + { + if (internal_decoder_data->decode_value_state.string_value_state.length == 0) + { + internal_decoder_data->decode_to_value->value.string_value.chars[0] = '\0'; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + } + else + { + result = 0; + } + } + else + { + size_t to_copy = internal_decoder_data->decode_value_state.string_value_state.length - (internal_decoder_data->bytes_decoded - 4); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(internal_decoder_data->decode_to_value->value.string_value.chars + (internal_decoder_data->bytes_decoded - 4), buffer, to_copy); + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_value_state.string_value_state.length + 4) + { + internal_decoder_data->decode_to_value->value.string_value.chars[internal_decoder_data->decode_value_state.string_value_state.length] = '\0'; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + break; + } + /* Codes_SRS_AMQPVALUE_01_379: [<encoding name="sym8" code="0xa3" category="variable" width="1" label="up to 2^8 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + case 0xA3: + { + if (internal_decoder_data->bytes_decoded == 0) + { + internal_decoder_data->decode_value_state.symbol_value_state.length = buffer[0]; + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + internal_decoder_data->decode_to_value->value.symbol_value.chars = (char*)malloc(internal_decoder_data->decode_value_state.symbol_value_state.length + 1); + if (internal_decoder_data->decode_to_value->value.symbol_value.chars == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for decoded symbol value"); + result = __FAILURE__; + } + else + { + if (internal_decoder_data->decode_value_state.symbol_value_state.length == 0) + { + internal_decoder_data->decode_to_value->value.symbol_value.chars[0] = '\0'; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + } + else + { + size_t to_copy = internal_decoder_data->decode_value_state.symbol_value_state.length - (internal_decoder_data->bytes_decoded - 1); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(internal_decoder_data->decode_to_value->value.symbol_value.chars + (internal_decoder_data->bytes_decoded - 1), buffer, to_copy); + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_value_state.symbol_value_state.length + 1) + { + internal_decoder_data->decode_to_value->value.symbol_value.chars[internal_decoder_data->decode_value_state.symbol_value_state.length] = 0; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + break; + } + /* Codes_SRS_AMQPVALUE_01_380: [<encoding name="sym32" code="0xb3" category="variable" width="4" label="up to 2^32 - 1 seven bit ASCII characters representing a symbolic value"/>] */ + case 0xB3: + { + if (internal_decoder_data->bytes_decoded < 4) + { + internal_decoder_data->decode_value_state.symbol_value_state.length += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decode_to_value->value.symbol_value.chars = (char*)malloc(internal_decoder_data->decode_value_state.symbol_value_state.length + 1); + if (internal_decoder_data->decode_to_value->value.symbol_value.chars == NULL) + { + /* Codes_SRS_AMQPVALUE_01_326: [If any allocation failure occurs during decoding, amqpvalue_decode_bytes shall fail and return a non-zero value.] */ + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not allocate memory for decoded symbol value"); + result = __FAILURE__; + } + else + { + if (internal_decoder_data->decode_value_state.symbol_value_state.length == 0) + { + internal_decoder_data->decode_to_value->value.symbol_value.chars[0] = '\0'; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + } + else + { + result = 0; + } + } + else + { + size_t to_copy = internal_decoder_data->decode_value_state.symbol_value_state.length - (internal_decoder_data->bytes_decoded - 4); + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(internal_decoder_data->decode_to_value->value.symbol_value.chars + (internal_decoder_data->bytes_decoded - 4), buffer, to_copy); + buffer += to_copy; + size -= to_copy; + internal_decoder_data->bytes_decoded += to_copy; + + if (internal_decoder_data->bytes_decoded == internal_decoder_data->decode_value_state.symbol_value_state.length + 4) + { + internal_decoder_data->decode_to_value->value.symbol_value.chars[internal_decoder_data->decode_value_state.symbol_value_state.length] = '\0'; + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + result = 0; + } + break; + } + /* Codes_SRS_AMQPVALUE_01_385: [<encoding name="list8" code="0xc0" category="compound" width="1" label="up to 2^8 - 1 list elements with total size less than 2^8 octets"/>] */ + case 0xC0: + /* Codes_SRS_AMQPVALUE_01_386: [<encoding name="list32" code="0xd0" category="compound" width="4" label="up to 2^32 - 1 list elements with total size less than 2^32 octets"/>] */ + case 0xD0: + { + DECODE_LIST_STEP step = internal_decoder_data->decode_value_state.list_value_state.list_value_state; + + switch (step) + { + default: + LogError("Invalid step in decoding list value: %d", step); + result = __FAILURE__; + break; + + case DECODE_LIST_STEP_SIZE: + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xC0) + { + internal_decoder_data->decode_value_state.list_value_state.list_value_state = DECODE_LIST_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.list_value.count = 0; + result = 0; + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decode_value_state.list_value_state.list_value_state = DECODE_LIST_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.list_value.count = 0; + } + + result = 0; + } + + break; + + case DECODE_LIST_STEP_COUNT: + if (internal_decoder_data->constructor_byte == 0xC0) + { + internal_decoder_data->decode_to_value->value.list_value.count = buffer[0]; + } + else + { + internal_decoder_data->decode_to_value->value.list_value.count += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + } + + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xC0) + { + if (internal_decoder_data->decode_to_value->value.list_value.count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + internal_decoder_data->decode_to_value->value.list_value.items = (AMQP_VALUE*)malloc(sizeof(AMQP_VALUE) * internal_decoder_data->decode_to_value->value.list_value.count); + if (internal_decoder_data->decode_to_value->value.list_value.items == NULL) + { + LogError("Could not allocate memory for decoded list value"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.list_value.count; i++) + { + internal_decoder_data->decode_to_value->value.list_value.items[i] = NULL; + } + + internal_decoder_data->decode_value_state.list_value_state.list_value_state = DECODE_LIST_STEP_ITEMS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.list_value_state.item = 0; + result = 0; + } + } + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + if (internal_decoder_data->decode_to_value->value.list_value.count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + + internal_decoder_data->decode_to_value->value.list_value.items = (AMQP_VALUE*)malloc(sizeof(AMQP_VALUE) * internal_decoder_data->decode_to_value->value.list_value.count); + if (internal_decoder_data->decode_to_value->value.list_value.items == NULL) + { + LogError("Could not allocate memory for decoded list value"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.list_value.count; i++) + { + internal_decoder_data->decode_to_value->value.list_value.items[i] = NULL; + } + + internal_decoder_data->decode_value_state.list_value_state.list_value_state = DECODE_LIST_STEP_ITEMS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.list_value_state.item = 0; + result = 0; + } + } + } + else + { + result = 0; + } + } + break; + + case DECODE_LIST_STEP_ITEMS: + { + size_t inner_used_bytes; + + if (internal_decoder_data->bytes_decoded == 0) + { + AMQP_VALUE_DATA* list_item = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (list_item == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + list_item->type = AMQP_TYPE_UNKNOWN; + internal_decoder_data->decode_to_value->value.list_value.items[internal_decoder_data->decode_value_state.list_value_state.item] = list_item; + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, list_item, true); + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("Could not create inner decoder for list items"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("NULL inner decoder. This should not happen under normal circumstances"); + result = __FAILURE__; + } + else if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, buffer, size, &inner_used_bytes) != 0) + { + LogError("Decoding list items failed"); + result = __FAILURE__; + } + else + { + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + internal_decoder_data->bytes_decoded += inner_used_bytes; + buffer += inner_used_bytes; + size -= inner_used_bytes; + + if (inner_decoder->decoder_state == DECODER_STATE_DONE) + { + internal_decoder_destroy(inner_decoder); + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->bytes_decoded = 0; + + internal_decoder_data->decode_value_state.list_value_state.item++; + if (internal_decoder_data->decode_value_state.list_value_state.item == internal_decoder_data->decode_to_value->value.list_value.count) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + /* Codes_SRS_AMQPVALUE_01_323: [When enough bytes have been processed for a valid amqp value, the on_value_decoded passed in amqpvalue_decoder_create shall be called.] */ + /* Codes_SRS_AMQPVALUE_01_324: [The decoded amqp value shall be passed to on_value_decoded.] */ + /* Codes_SRS_AMQPVALUE_01_325: [Also the context stored in amqpvalue_decoder_create shall be passed to the on_value_decoded callback.] */ + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + + } + + result = 0; + } + + break; + } + } + + break; + } + case 0xC1: + case 0xD1: + { + DECODE_MAP_STEP step = internal_decoder_data->decode_value_state.map_value_state.map_value_state; + + switch (step) + { + default: + LogError("Invalid step in decoding map value: %d", step); + result = __FAILURE__; + break; + + case DECODE_MAP_STEP_SIZE: + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xC1) + { + internal_decoder_data->decode_value_state.map_value_state.map_value_state = DECODE_MAP_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.map_value.pair_count = 0; + result = 0; + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decode_value_state.map_value_state.map_value_state = DECODE_MAP_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.map_value.pair_count = 0; + } + result = 0; + } + + break; + + case DECODE_MAP_STEP_COUNT: + if (internal_decoder_data->constructor_byte == 0xC1) + { + internal_decoder_data->decode_to_value->value.map_value.pair_count = buffer[0]; + } + else + { + internal_decoder_data->decode_to_value->value.map_value.pair_count += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + } + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xC1) + { + if (internal_decoder_data->decode_to_value->value.map_value.pair_count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + + internal_decoder_data->decode_to_value->value.map_value.pair_count /= 2; + + internal_decoder_data->decode_to_value->value.map_value.pairs = (AMQP_MAP_KEY_VALUE_PAIR*)malloc(sizeof(AMQP_MAP_KEY_VALUE_PAIR) * (internal_decoder_data->decode_to_value->value.map_value.pair_count * 2)); + if (internal_decoder_data->decode_to_value->value.map_value.pairs == NULL) + { + LogError("Could not allocate memory for map value items"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.map_value.pair_count; i++) + { + internal_decoder_data->decode_to_value->value.map_value.pairs[i].key = NULL; + internal_decoder_data->decode_to_value->value.map_value.pairs[i].value = NULL; + } + + internal_decoder_data->decode_value_state.map_value_state.map_value_state = DECODE_MAP_STEP_PAIRS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.map_value_state.item = 0; + result = 0; + } + } + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + if (internal_decoder_data->decode_to_value->value.map_value.pair_count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + + internal_decoder_data->decode_to_value->value.map_value.pair_count /= 2; + + internal_decoder_data->decode_to_value->value.map_value.pairs = (AMQP_MAP_KEY_VALUE_PAIR*)malloc(sizeof(AMQP_MAP_KEY_VALUE_PAIR) * (internal_decoder_data->decode_to_value->value.map_value.pair_count * 2)); + if (internal_decoder_data->decode_to_value->value.map_value.pairs == NULL) + { + LogError("Could not allocate memory for map value items"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.map_value.pair_count; i++) + { + internal_decoder_data->decode_to_value->value.map_value.pairs[i].key = NULL; + internal_decoder_data->decode_to_value->value.map_value.pairs[i].value = NULL; + } + + internal_decoder_data->decode_value_state.map_value_state.map_value_state = DECODE_MAP_STEP_PAIRS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.map_value_state.item = 0; + result = 0; + } + } + } + else + { + result = 0; + } + } + break; + + case DECODE_MAP_STEP_PAIRS: + { + size_t inner_used_bytes; + + if (internal_decoder_data->bytes_decoded == 0) + { + AMQP_VALUE_DATA* map_item = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (map_item == NULL) + { + LogError("Could not allocate memory for map item"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + map_item->type = AMQP_TYPE_UNKNOWN; + if (internal_decoder_data->decode_to_value->value.map_value.pairs[internal_decoder_data->decode_value_state.map_value_state.item].key == NULL) + { + internal_decoder_data->decode_to_value->value.map_value.pairs[internal_decoder_data->decode_value_state.map_value_state.item].key = map_item; + } + else + { + internal_decoder_data->decode_to_value->value.map_value.pairs[internal_decoder_data->decode_value_state.map_value_state.item].value = map_item; + } + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, map_item, true); + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("Could not create inner decoder for map item"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("NULL inner decoder. This should not happen under normal circumstances"); + result = __FAILURE__; + } + else if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, buffer, size, &inner_used_bytes) != 0) + { + LogError("Could not decode map item"); + result = __FAILURE__; + } + else + { + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + internal_decoder_data->bytes_decoded += inner_used_bytes; + buffer += inner_used_bytes; + size -= inner_used_bytes; + + if (inner_decoder->decoder_state == DECODER_STATE_DONE) + { + internal_decoder_destroy(inner_decoder); + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->bytes_decoded = 0; + + if (internal_decoder_data->decode_to_value->value.map_value.pairs[internal_decoder_data->decode_value_state.map_value_state.item].value != NULL) + { + internal_decoder_data->decode_value_state.map_value_state.item++; + if (internal_decoder_data->decode_value_state.map_value_state.item == internal_decoder_data->decode_to_value->value.map_value.pair_count) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + } + } + } + + result = 0; + } + + break; + } + } + + break; + } + case 0xE0: + case 0xF0: + { + DECODE_ARRAY_STEP step = internal_decoder_data->decode_value_state.array_value_state.array_value_state; + + switch (step) + { + default: + LogError("Invalid step in decoding array value: %d", step); + result = __FAILURE__; + break; + + case DECODE_ARRAY_STEP_SIZE: + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xE0) + { + internal_decoder_data->decode_value_state.array_value_state.array_value_state = DECODE_ARRAY_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.array_value.count = 0; + result = 0; + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + internal_decoder_data->decode_value_state.array_value_state.array_value_state = DECODE_ARRAY_STEP_COUNT; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->decode_to_value->value.array_value.count = 0; + } + result = 0; + } + + break; + + case DECODE_ARRAY_STEP_COUNT: + if (internal_decoder_data->constructor_byte == 0xE0) + { + internal_decoder_data->decode_to_value->value.array_value.count = buffer[0]; + } + else + { + internal_decoder_data->decode_to_value->value.array_value.count += buffer[0] << ((3 - internal_decoder_data->bytes_decoded) * 8); + } + + internal_decoder_data->bytes_decoded++; + buffer++; + size--; + + if (internal_decoder_data->constructor_byte == 0xE0) + { + if (internal_decoder_data->decode_to_value->value.array_value.count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + internal_decoder_data->decode_to_value->value.array_value.items = (AMQP_VALUE*)malloc(sizeof(AMQP_VALUE) * internal_decoder_data->decode_to_value->value.array_value.count); + if (internal_decoder_data->decode_to_value->value.array_value.items == NULL) + { + LogError("Could not allocate memory for array items"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.array_value.count; i++) + { + internal_decoder_data->decode_to_value->value.array_value.items[i] = NULL; + } + + internal_decoder_data->decode_value_state.array_value_state.array_value_state = DECODE_ARRAY_STEP_ITEMS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.array_value_state.item = 0; + result = 0; + } + } + } + else + { + if (internal_decoder_data->bytes_decoded == 4) + { + if (internal_decoder_data->decode_to_value->value.array_value.count == 0) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + result = 0; + } + else + { + uint32_t i; + internal_decoder_data->decode_to_value->value.array_value.items = (AMQP_VALUE*)malloc(sizeof(AMQP_VALUE) * internal_decoder_data->decode_to_value->value.array_value.count); + if (internal_decoder_data->decode_to_value->value.array_value.items == NULL) + { + LogError("Could not allocate memory for array items"); + result = __FAILURE__; + } + else + { + for (i = 0; i < internal_decoder_data->decode_to_value->value.array_value.count; i++) + { + internal_decoder_data->decode_to_value->value.array_value.items[i] = NULL; + } + internal_decoder_data->decode_value_state.array_value_state.array_value_state = DECODE_ARRAY_STEP_ITEMS; + internal_decoder_data->bytes_decoded = 0; + internal_decoder_data->inner_decoder = NULL; + internal_decoder_data->decode_value_state.array_value_state.item = 0; + result = 0; + } + } + } + else + { + result = 0; + } + } + break; + + case DECODE_ARRAY_STEP_ITEMS: + { + size_t inner_used_bytes; + + if (internal_decoder_data->bytes_decoded == 0) + { + AMQP_VALUE_DATA* array_item; + internal_decoder_data->decode_value_state.array_value_state.constructor_byte = buffer[0]; + + array_item = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (array_item == NULL) + { + LogError("Could not allocate memory for array item to be decoded"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + array_item->type = AMQP_TYPE_UNKNOWN; + internal_decoder_data->decode_to_value->value.array_value.items[internal_decoder_data->decode_value_state.array_value_state.item] = array_item; + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, array_item, true); + if (internal_decoder_data->inner_decoder == NULL) + { + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + LogError("Could not create inner decoder for array items"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("NULL inner decoder. This should not happen under normal circumstances"); + result = __FAILURE__; + } + else if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, buffer, size, &inner_used_bytes) != 0) + { + LogError("Could not decode array item"); + result = __FAILURE__; + } + else + { + INTERNAL_DECODER_DATA* inner_decoder = (INTERNAL_DECODER_DATA*)internal_decoder_data->inner_decoder; + internal_decoder_data->bytes_decoded += inner_used_bytes; + buffer += inner_used_bytes; + size -= inner_used_bytes; + + if (inner_decoder->decoder_state == DECODER_STATE_DONE) + { + internal_decoder_destroy(inner_decoder); + internal_decoder_data->inner_decoder = NULL; + + internal_decoder_data->decode_value_state.array_value_state.item++; + if (internal_decoder_data->decode_value_state.array_value_state.item == internal_decoder_data->decode_to_value->value.array_value.count) + { + internal_decoder_data->decoder_state = DECODER_STATE_CONSTRUCTOR; + internal_decoder_data->on_value_decoded(internal_decoder_data->on_value_decoded_context, internal_decoder_data->decode_to_value); + + result = 0; + } + else + { + AMQP_VALUE_DATA* array_item = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (array_item == NULL) + { + LogError("Could not allocate memory for array item"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + array_item->type = AMQP_TYPE_UNKNOWN; + internal_decoder_data->decode_to_value->value.array_value.items[internal_decoder_data->decode_value_state.array_value_state.item] = array_item; + internal_decoder_data->inner_decoder = internal_decoder_create(inner_decoder_callback, internal_decoder_data, array_item, true); + if (internal_decoder_data->inner_decoder == NULL) + { + LogError("Could not create inner decoder for array item"); + internal_decoder_data->decoder_state = DECODER_STATE_ERROR; + result = __FAILURE__; + } + else + { + if (internal_decoder_decode_bytes(internal_decoder_data->inner_decoder, &internal_decoder_data->decode_value_state.array_value_state.constructor_byte, 1, NULL) != 0) + { + LogError("Could not decode array item data"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + else + { + result = 0; + } + } + + break; + } + } + + break; + } + } + break; + } + } + + if (result != 0) + { + break; + } + } + } + + if (used_bytes != NULL) + { + *used_bytes = initial_size - size; + } + + return result; +} + +AMQPVALUE_DECODER_HANDLE amqpvalue_decoder_create(ON_VALUE_DECODED on_value_decoded, void* callback_context) +{ + AMQPVALUE_DECODER_HANDLE_DATA* decoder_instance; + + /* Codes_SRS_AMQPVALUE_01_312: [If the on_value_decoded argument is NULL, amqpvalue_decoder_create shall return NULL.] */ + if (on_value_decoded == NULL) + { + LogError("NULL on_value_decoded"); + decoder_instance = NULL; + } + else + { + decoder_instance = (AMQPVALUE_DECODER_HANDLE_DATA*)malloc(sizeof(AMQPVALUE_DECODER_HANDLE_DATA)); + /* Codes_SRS_AMQPVALUE_01_313: [If creating the decoder fails, amqpvalue_decoder_create shall return NULL.] */ + if (decoder_instance == NULL) + { + LogError("Could not allocate memory for AMQP value decoder"); + } + else + { + decoder_instance->decode_to_value = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (decoder_instance->decode_to_value == NULL) + { + /* Codes_SRS_AMQPVALUE_01_313: [If creating the decoder fails, amqpvalue_decoder_create shall return NULL.] */ + LogError("Could not allocate memory for decoded AMQP value"); + free(decoder_instance); + decoder_instance = NULL; + } + else + { + decoder_instance->decode_to_value->type = AMQP_TYPE_UNKNOWN; + decoder_instance->internal_decoder = internal_decoder_create(on_value_decoded, callback_context, decoder_instance->decode_to_value, false); + if (decoder_instance->internal_decoder == NULL) + { + /* Codes_SRS_AMQPVALUE_01_313: [If creating the decoder fails, amqpvalue_decoder_create shall return NULL.] */ + LogError("Could not create the internal decoder"); + free(decoder_instance->decode_to_value); + free(decoder_instance); + decoder_instance = NULL; + } + } + } + } + + /* Codes_SRS_AMQPVALUE_01_311: [amqpvalue_decoder_create shall create a new amqp value decoder and return a non-NULL handle to it.] */ + return decoder_instance; +} + +void amqpvalue_decoder_destroy(AMQPVALUE_DECODER_HANDLE handle) +{ + if (handle == NULL) + { + /* Codes_SRS_AMQPVALUE_01_317: [If handle is NULL, amqpvalue_decoder_destroy shall do nothing.] */ + LogError("NULL handle"); + } + else + { + AMQPVALUE_DECODER_HANDLE_DATA* decoder_instance = (AMQPVALUE_DECODER_HANDLE_DATA*)handle; + /* Codes_SRS_AMQPVALUE_01_316: [amqpvalue_decoder_destroy shall free all resources associated with the amqpvalue_decoder.] */ + amqpvalue_destroy(decoder_instance->internal_decoder->decode_to_value); + internal_decoder_destroy(decoder_instance->internal_decoder); + free(handle); + } +} + +/* Codes_SRS_AMQPVALUE_01_318: [amqpvalue_decode_bytes shall decode size bytes that are passed in the buffer argument.] */ +int amqpvalue_decode_bytes(AMQPVALUE_DECODER_HANDLE handle, const unsigned char* buffer, size_t size) +{ + int result; + + AMQPVALUE_DECODER_HANDLE_DATA* decoder_instance = (AMQPVALUE_DECODER_HANDLE_DATA*)handle; + /* Codes_SRS_AMQPVALUE_01_320: [If handle or buffer are NULL, amqpvalue_decode_bytes shall return a non-zero value.] */ + if ((decoder_instance == NULL) || + (buffer == NULL) || + /* Codes_SRS_AMQPVALUE_01_321: [If size is 0, amqpvalue_decode_bytes shall return a non-zero value.] */ + (size == 0)) + { + LogError("Bad arguments: decoder_instance = %p, buffer = %p, size = %u", + decoder_instance, buffer, size); + result = __FAILURE__; + } + else + { + size_t used_bytes; + + /* Codes_SRS_AMQPVALUE_01_318: [amqpvalue_decode_bytes shall decode size bytes that are passed in the buffer argument.] */ + if (internal_decoder_decode_bytes(decoder_instance->internal_decoder, buffer, size, &used_bytes) != 0) + { + LogError("Failed decoding bytes"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_AMQPVALUE_01_319: [On success, amqpvalue_decode_bytes shall return 0.] */ + result = 0; + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_inplace_descriptor(AMQP_VALUE value) +{ + AMQP_VALUE result; + + if (value == NULL) + { + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_DESCRIBED) && + (value_data->type != AMQP_TYPE_COMPOSITE)) + { + LogError("Type is not described or composite"); + result = NULL; + } + else + { + result = value_data->value.described_value.descriptor; + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_inplace_described_value(AMQP_VALUE value) +{ + AMQP_VALUE result; + + if (value == NULL) + { + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_DESCRIBED) && + (value_data->type != AMQP_TYPE_COMPOSITE)) + { + LogError("Type is not described or composite"); + result = NULL; + } + else + { + result = value_data->value.described_value.value; + } + } + + return result; +} + +AMQP_VALUE amqpvalue_create_described(AMQP_VALUE descriptor, AMQP_VALUE value) +{ + AMQP_VALUE_DATA* result = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + LogError("Cannot allocate memory for described type"); + } + else + { + result->type = AMQP_TYPE_DESCRIBED; + result->value.described_value.descriptor = descriptor; + result->value.described_value.value = value; + } + + return result; +} + +AMQP_VALUE amqpvalue_create_composite(AMQP_VALUE descriptor, uint32_t list_size) +{ + AMQP_VALUE_DATA* result = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + LogError("Cannot allocate memory for composite type"); + } + else + { + result->type = AMQP_TYPE_COMPOSITE; + result->value.described_value.descriptor = amqpvalue_clone(descriptor); + if (result->value.described_value.descriptor == NULL) + { + LogError("Cannot clone descriptor for composite type"); + free(result); + result = NULL; + } + else + { + result->value.described_value.value = amqpvalue_create_list(); + if (result->value.described_value.value == NULL) + { + LogError("Cannot create list for composite type"); + amqpvalue_destroy(result->value.described_value.descriptor); + free(result); + result = NULL; + } + else + { + if (amqpvalue_set_list_item_count(result->value.described_value.value, list_size) != 0) + { + LogError("Cannot set list item count for composite type"); + amqpvalue_destroy(result->value.described_value.descriptor); + amqpvalue_destroy(result->value.described_value.value); + free(result); + result = NULL; + } + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_create_composite_with_ulong_descriptor(uint64_t descriptor) +{ + AMQP_VALUE_DATA* result = (AMQP_VALUE_DATA*)REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA); + if (result == NULL) + { + LogError("Cannot allocate memory for composite type"); + } + else + { + AMQP_VALUE descriptor_ulong_value = amqpvalue_create_ulong(descriptor); + if (descriptor_ulong_value == NULL) + { + LogError("Cannot create ulong descriptor for composite type"); + free(result); + result = NULL; + } + else + { + result->type = AMQP_TYPE_COMPOSITE; + result->value.described_value.descriptor = descriptor_ulong_value; + result->value.described_value.value = amqpvalue_create_list(); + if (result->value.described_value.value == NULL) + { + LogError("Cannot create list for composite type"); + amqpvalue_destroy(descriptor_ulong_value); + free(result); + result = NULL; + } + } + } + + return result; +} + +int amqpvalue_set_composite_item(AMQP_VALUE value, uint32_t index, AMQP_VALUE item_value) +{ + int result; + + if (value == NULL) + { + LogError("NULL value"); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_COMPOSITE) && + (value_data->type != AMQP_TYPE_DESCRIBED)) + { + LogError("Attempt to set composite item on a non-composite type"); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_list_item(value_data->value.described_value.value, index, item_value) != 0) + { + LogError("amqpvalue_set_list_item failed for composite item"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_composite_item(AMQP_VALUE value, size_t index) +{ + AMQP_VALUE result; + + if (value == NULL) + { + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_COMPOSITE) && + (value_data->type != AMQP_TYPE_DESCRIBED)) + { + LogError("Attempt to get composite item on a non-composite type"); + result = NULL; + } + else + { + result = amqpvalue_get_list_item(value_data->value.described_value.value, index); + if (result == NULL) + { + LogError("amqpvalue_get_list_item failed for composite item"); + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_composite_item_in_place(AMQP_VALUE value, size_t index) +{ + AMQP_VALUE result; + + if (value == NULL) + { + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_COMPOSITE) && + (value_data->type != AMQP_TYPE_DESCRIBED)) + { + LogError("Attempt to get composite item in place on a non-composite type"); + result = NULL; + } + else + { + result = amqpvalue_get_list_item_in_place(value_data->value.described_value.value, index); + if (result == NULL) + { + LogError("amqpvalue_get_list_item_in_place failed for composite item"); + } + } + } + + return result; +} + +int amqpvalue_get_composite_item_count(AMQP_VALUE value, uint32_t* item_count) +{ + int result; + + if (value == NULL) + { + LogError("NULL value"); + result = __FAILURE__; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + if ((value_data->type != AMQP_TYPE_COMPOSITE) && + (value_data->type != AMQP_TYPE_DESCRIBED)) + { + LogError("Attempt to get composite item in place on a non-composite type"); + result = __FAILURE__; + } + else + { + if (amqpvalue_get_list_item_count(value_data->value.described_value.value, item_count) != 0) + { + LogError("amqpvalue_get_list_item_in_place failed for composite item"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +AMQP_VALUE amqpvalue_get_list_item_in_place(AMQP_VALUE value, size_t index) +{ + AMQP_VALUE result; + + if (value == NULL) + { + LogError("NULL value"); + result = NULL; + } + else + { + AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value; + + if ((value_data->type != AMQP_TYPE_LIST) || + (value_data->value.list_value.count <= index)) + { + LogError("Attempt to get list item in place on a non-list type"); + result = NULL; + } + else + { + result = value_data->value.list_value.items[index]; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/amqpvalue_to_string.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,719 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/uuid.h" +#include "azure_uamqp_c/amqpvalue_to_string.h" +#include "azure_uamqp_c/amqpvalue.h" + +#if _WIN32 +/* The MS runtime does not have snprintf */ +#define snprintf _snprintf +#endif + +static int string_concat(char** string, const char* to_concat) +{ + int result; + char* new_string; + size_t length = strlen(to_concat) + 1; + size_t src_length; + + if (*string != NULL) + { + src_length = strlen(*string); + } + else + { + src_length = 0; + } + + new_string = (char*)realloc(*string, src_length + length); + if (new_string == NULL) + { + LogError("Cannot allocate memory for the new string"); + result = __FAILURE__; + } + else + { + *string = new_string; + (void)memcpy(*string + src_length, to_concat, length); + result = 0; + } + + return result; +} + +char* amqpvalue_to_string(AMQP_VALUE amqp_value) +{ + char* result = NULL; + + if (amqp_value != NULL) + { + AMQP_TYPE amqp_type = amqpvalue_get_type(amqp_value); + switch (amqp_type) + { + default: + LogError("Unknown AMQP type"); + result = NULL; + break; + + case AMQP_TYPE_NULL: + if (string_concat(&result, "NULL") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + case AMQP_TYPE_BOOL: + { + bool value; + if (amqpvalue_get_boolean(amqp_value, &value) != 0) + { + LogError("Failure getting bool value"); + free(result); + result = NULL; + } + else if (string_concat(&result, (value == true) ? "true" : "false") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_UBYTE: + { + char str_value[4]; + uint8_t value; + if (amqpvalue_get_ubyte(amqp_value, &value) != 0) + { + LogError("Failure getting ubyte value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRIu8, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_USHORT: + { + char str_value[6]; + uint16_t value; + if (amqpvalue_get_ushort(amqp_value, &value) != 0) + { + LogError("Failure getting ushort value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRIu16, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_UINT: + { + char str_value[11]; + uint32_t value; + if (amqpvalue_get_uint(amqp_value, &value) != 0) + { + LogError("Failure getting uint value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRIu32, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_ULONG: + { + char str_value[21]; + uint64_t value; + if (amqpvalue_get_ulong(amqp_value, &value) != 0) + { + LogError("Failure getting ulong value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRIu64, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_BYTE: + { + char str_value[5]; + char value; + if (amqpvalue_get_byte(amqp_value, &value) != 0) + { + LogError("Failure getting byte value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRId8, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_SHORT: + { + char str_value[7]; + int16_t value; + if (amqpvalue_get_short(amqp_value, &value) != 0) + { + LogError("Failure getting short value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRId16, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_INT: + { + char str_value[12]; + int32_t value; + if (amqpvalue_get_int(amqp_value, &value) != 0) + { + LogError("Failure getting int value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRId32, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_LONG: + { + char str_value[21]; + int64_t value; + if (amqpvalue_get_long(amqp_value, &value) != 0) + { + LogError("Failure getting long value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRId64, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_FLOAT: + { + float float_value; + if (amqpvalue_get_float(amqp_value, &float_value) != 0) + { + LogError("Failure getting float value"); + free(result); + result = NULL; + } + else + { + char str_value[25]; + if ((snprintf(str_value, sizeof(str_value), "%.02f", float_value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_DOUBLE: + { + double double_value; + if (amqpvalue_get_double(amqp_value, &double_value) != 0) + { + LogError("Failure getting double value"); + free(result); + result = NULL; + } + else + { + char str_value[25]; + if ((snprintf(str_value, sizeof(str_value), "%.02lf", double_value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_CHAR: + { + uint32_t char_code; + if (amqpvalue_get_char(amqp_value, &char_code) != 0) + { + LogError("Failure getting char value"); + free(result); + result = NULL; + } + else + { + char str_value[25]; + if ((snprintf(str_value, sizeof(str_value), "U%02X%02X%02X%02X", char_code >> 24, (char_code >> 16) & 0xFF, (char_code >> 8) & 0xFF, char_code & 0xFF) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_TIMESTAMP: + { + char str_value[21]; + int64_t value; + if (amqpvalue_get_timestamp(amqp_value, &value) != 0) + { + LogError("Failure getting timestamp value"); + free(result); + result = NULL; + } + else if ((sprintf(str_value, "%" PRId64, value) < 0) || + (string_concat(&result, str_value) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + break; + } + case AMQP_TYPE_UUID: + { + uuid uuid_value; + if (amqpvalue_get_uuid(amqp_value, &uuid_value) != 0) + { + LogError("Failure getting uuid value"); + free(result); + result = NULL; + } + else + { + char* uuid_string_value = UUID_to_string(&uuid_value); + if (uuid_string_value == NULL) + { + LogError("Failure getting UUID stringified value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, uuid_string_value) != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + + free(uuid_string_value); + } + } + + break; + } + + case AMQP_TYPE_BINARY: + { + amqp_binary binary_value; + if (amqpvalue_get_binary(amqp_value, &binary_value) != 0) + { + LogError("Failure getting binary value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, "<") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + else + { + uint64_t i; + + for (i = 0; i < binary_value.length; i++) + { + char str_value[4]; + if ((snprintf(str_value, sizeof(str_value), "%s%02X", (i > 0) ? " " : "", ((unsigned char*)binary_value.bytes)[i]) < 0) || + (string_concat(&result, str_value) != 0)) + { + break; + } + } + + if ((i < binary_value.length) || + (string_concat(&result, ">") != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + } + break; + } + case AMQP_TYPE_STRING: + { + const char* string_value; + if (amqpvalue_get_string(amqp_value, &string_value) != 0) + { + LogError("Failure getting string value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, string_value) != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_SYMBOL: + { + const char* string_value; + if (amqpvalue_get_symbol(amqp_value, &string_value) != 0) + { + LogError("Failure getting symbol value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, string_value) != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_LIST: + { + uint32_t count; + if (amqpvalue_get_list_item_count(amqp_value, &count) != 0) + { + LogError("Failure getting list item count value"); + free(result); + result = NULL; + } + else if (string_concat(&result, "{") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + else + { + size_t i; + for (i = 0; i < count; i++) + { + AMQP_VALUE item = amqpvalue_get_list_item(amqp_value, i); + if (item == NULL) + { + LogError("Failure getting item %u from list", (unsigned int)i); + break; + } + else + { + char* item_string = amqpvalue_to_string(item); + if (item_string == NULL) + { + LogError("Failure converting item %u to string", (unsigned int)i); + amqpvalue_destroy(item); + break; + } + else + { + if (((i > 0) && (string_concat(&result, ",") != 0)) || + (string_concat(&result, item_string) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + break; + } + + free(item_string); + } + + amqpvalue_destroy(item); + } + } + + if (i < count) + { + // no log here, we already logged the error + free(result); + result = NULL; + } + else if (string_concat(&result, "}") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_MAP: + { + uint32_t count; + if (amqpvalue_get_map_pair_count(amqp_value, &count) != 0) + { + LogError("Failure getting map pair count"); + free(result); + result = NULL; + } + else if (string_concat(&result, "{") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + else + { + uint32_t i; + for (i = 0; i < count; i++) + { + AMQP_VALUE key; + AMQP_VALUE value; + if (amqpvalue_get_map_key_value_pair(amqp_value, i, &key, &value) != 0) + { + LogError("Failure getting key/value pair index %u", (unsigned int)i); + break; + } + else + { + char* key_string = amqpvalue_to_string(key); + if (key_string == NULL) + { + LogError("Failure getting stringified key value for index %u", (unsigned int)i); + amqpvalue_destroy(key); + amqpvalue_destroy(value); + break; + } + else + { + char* value_string = amqpvalue_to_string(value); + if (key_string == NULL) + { + LogError("Failure getting stringified value for index %u", (unsigned int)i); + free(key_string); + amqpvalue_destroy(key); + amqpvalue_destroy(value); + break; + } + else + { + if (((i > 0) && (string_concat(&result, ",") != 0)) || + (string_concat(&result, "[") != 0) || + (string_concat(&result, key_string) != 0) || + (string_concat(&result, ":") != 0) || + (string_concat(&result, value_string) != 0) || + (string_concat(&result, "]") != 0)) + { + LogError("Failure building amqp value string"); + free(key_string); + free(value_string); + amqpvalue_destroy(key); + amqpvalue_destroy(value); + break; + } + + free(value_string); + } + + free(key_string); + } + + amqpvalue_destroy(key); + amqpvalue_destroy(value); + } + } + + if (i < count) + { + // no log here, we already logged the error + free(result); + result = NULL; + } + else if (string_concat(&result, "}") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_ARRAY: + { + uint32_t count; + if (amqpvalue_get_array_item_count(amqp_value, &count) != 0) + { + LogError("Failure getting array item count"); + free(result); + result = NULL; + } + else if (string_concat(&result, "{") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + else + { + uint32_t i; + for (i = 0; i < count; i++) + { + AMQP_VALUE item = amqpvalue_get_array_item(amqp_value, i); + if (item == NULL) + { + LogError("Failure getting array item for index %u", (unsigned int)i); + break; + } + else + { + char* item_string = amqpvalue_to_string(item); + if (item_string == NULL) + { + LogError("Failure getting stringified array item value for index %u", (unsigned int)i); + amqpvalue_destroy(item); + break; + } + else + { + if (((i > 0) && (string_concat(&result, ",") != 0)) || + (string_concat(&result, item_string) != 0)) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + break; + } + + free(item_string); + } + + amqpvalue_destroy(item); + } + } + + if (i < count) + { + // no log here, we already logged the error + free(result); + result = NULL; + } + else if (string_concat(&result, "}") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + } + break; + } + case AMQP_TYPE_COMPOSITE: + case AMQP_TYPE_DESCRIBED: + { + AMQP_VALUE described_value = amqpvalue_get_inplace_described_value(amqp_value); + if (described_value == NULL) + { + LogError("Failure getting described value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, "* ") != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + else + { + char* described_value_string = amqpvalue_to_string(described_value); + if (described_value_string == NULL) + { + LogError("Failure getting stringified described value"); + free(result); + result = NULL; + } + else + { + if (string_concat(&result, described_value_string) != 0) + { + LogError("Failure building amqp value string"); + free(result); + result = NULL; + } + + free(described_value_string); + } + } + } + break; + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/async_operation.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/async_operation.h" + +typedef struct ASYNC_OPERATION_INSTANCE_TAG +{ + ASYNC_OPERATION_CANCEL_HANDLER_FUNC async_operation_cancel_handler; +} ASYNC_OPERATION_INSTANCE; + +ASYNC_OPERATION_HANDLE async_operation_create(ASYNC_OPERATION_CANCEL_HANDLER_FUNC async_operation_cancel_handler, size_t context_size) +{ + ASYNC_OPERATION_INSTANCE* async_operation; + + if (async_operation_cancel_handler == NULL) + { + /* Codes_SRS_ASYNC_OPERATION_01_002: [ If `async_operation_cancel_handler` is NULL, `async_operation_create` shall fail and return NULL.]*/ + LogError("Cannot allocate memory for async operation"); + async_operation = NULL; + } + else if (context_size < sizeof(ASYNC_OPERATION_INSTANCE)) + { + /* Codes_SRS_ASYNC_OPERATION_01_003: [ If `context_size` is less than the size of the `async_operation_cancel_handler` argument, `async_operation_create` shall fail and return NULL.]*/ + LogError("Context size too small"); + async_operation = NULL; + } + else + { + async_operation = (ASYNC_OPERATION_INSTANCE*)malloc(context_size); + if (async_operation == NULL) + { + /* Codes_SRS_ASYNC_OPERATION_01_004: [ If allocating memory for the new asynchronous operation instance fails, `async_operation_create` shall fail and return NULL.]*/ + LogError("Cannot allocate memory for async operation"); + } + else + { + /* Codes_SRS_ASYNC_OPERATION_01_001: [ `async_operation_create` shall return a non-NULL handle to a newly created asynchronous operation instance.]*/ + async_operation->async_operation_cancel_handler = async_operation_cancel_handler; + } + } + + return async_operation; +} + +void async_operation_destroy(ASYNC_OPERATION_HANDLE async_operation) +{ + if (async_operation == NULL) + { + /* Codes_SRS_ASYNC_OPERATION_01_006: [ If `async_operation` is NULL, `async_operation_destroy` shall do nothing.]*/ + LogError("NULL async_operation"); + } + else + { + /* Codes_SRS_ASYNC_OPERATION_01_005: [ `async_operation_destroy` shall free all recources associated with the asyncronous operation instance.]*/ + free(async_operation); + } +} + +int async_operation_cancel(ASYNC_OPERATION_HANDLE async_operation) +{ + int result; + + if (async_operation == NULL) + { + LogError("NULL async_operation"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_ASYNC_OPERATION_01_007: [ `async_operation_cancel` shall cancel the operation by calling the cancel handler function passed to `async_operation_create`.]*/ + async_operation->async_operation_cancel_handler(async_operation); + + /* Codes_SRS_ASYNC_OPERATION_01_008: [ On success `async_operation_cancel` shall return 0.]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/cbs.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,744 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/cbs.h" +#include "azure_uamqp_c/amqp_management.h" +#include "azure_uamqp_c/session.h" + +typedef enum CBS_STATE_TAG +{ + CBS_STATE_CLOSED, + CBS_STATE_OPENING, + CBS_STATE_OPEN, + CBS_STATE_ERROR +} CBS_STATE; + +typedef struct CBS_OPERATION_TAG +{ + ON_CBS_OPERATION_COMPLETE on_cbs_operation_complete; + void* on_cbs_operation_complete_context; + SINGLYLINKEDLIST_HANDLE pending_operations; +} CBS_OPERATION; + +typedef struct CBS_INSTANCE_TAG +{ + AMQP_MANAGEMENT_HANDLE amqp_management; + CBS_STATE cbs_state; + ON_CBS_OPEN_COMPLETE on_cbs_open_complete; + void* on_cbs_open_complete_context; + ON_CBS_ERROR on_cbs_error; + void* on_cbs_error_context; + SINGLYLINKEDLIST_HANDLE pending_operations; +} CBS_INSTANCE; + +static int add_string_key_value_pair_to_map(AMQP_VALUE map, const char* key, const char* value) +{ + int result; + + AMQP_VALUE key_value = amqpvalue_create_string(key); + if (key_value == NULL) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed creating value for property key %s", key); + result = __FAILURE__; + } + else + { + AMQP_VALUE value_value = amqpvalue_create_string(value); + if (value_value == NULL) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed creating value for property value %s", value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_map_value(map, key_value, value_value) != 0) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed inserting key/value pair in the map"); + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(value_value); + } + + amqpvalue_destroy(key_value); + } + + return result; +} + +static void on_underlying_amqp_management_open_complete(void* context, AMQP_MANAGEMENT_OPEN_RESULT open_result) +{ + if (context == NULL) + { + /* Codes_SRS_CBS_01_105: [ When `on_amqp_management_open_complete` is called with NULL `context`, it shall do nothing. ]*/ + LogError("on_underlying_amqp_management_open_complete called with NULL context"); + } + else + { + CBS_HANDLE cbs = (CBS_HANDLE)context; + + switch (cbs->cbs_state) + { + default: + LogError("AMQP management open complete in unknown state"); + break; + + case CBS_STATE_CLOSED: + case CBS_STATE_ERROR: + LogError("Unexpected AMQP management open complete"); + break; + + case CBS_STATE_OPEN: + LogError("Unexpected AMQP management open complete in OPEN"); + /* Codes_SRS_CBS_01_109: [ When `on_amqp_management_open_complete` is called when the CBS is OPEN, the callback `on_cbs_error` shall be called and the `on_cbs_error_context` shall be passed as argument. ]*/ + cbs->cbs_state = CBS_STATE_ERROR; + cbs->on_cbs_error(cbs->on_cbs_error_context); + break; + + case CBS_STATE_OPENING: + { + switch (open_result) + { + default: + LogError("Unknown AMQP management state"); + + case AMQP_MANAGEMENT_OPEN_ERROR: + cbs->cbs_state = CBS_STATE_CLOSED; + /* Codes_SRS_CBS_01_113: [ When `on_amqp_management_open_complete` reports a failure, the underlying AMQP management shall be closed by calling `amqp_management_close`. ]*/ + (void)amqp_management_close(cbs->amqp_management); + /* Codes_SRS_CBS_01_107: [ If CBS is OPENING and `open_result` is `AMQP_MANAGEMENT_OPEN_ERROR` the callback `on_cbs_open_complete` shall be called with `CBS_OPEN_ERROR` and the `on_cbs_open_complete_context` shall be passed as argument. ]*/ + cbs->on_cbs_open_complete(cbs->on_cbs_open_complete_context, CBS_OPEN_ERROR); + break; + + case AMQP_MANAGEMENT_OPEN_CANCELLED: + cbs->cbs_state = CBS_STATE_CLOSED; + /* Codes_SRS_CBS_01_113: [ When `on_amqp_management_open_complete` reports a failure, the underlying AMQP management shall be closed by calling `amqp_management_close`. ]*/ + (void)amqp_management_close(cbs->amqp_management); + /* Codes_SRS_CBS_01_108: [ If CBS is OPENING and `open_result` is `AMQP_MANAGEMENT_OPEN_CANCELLED` the callback `on_cbs_open_complete` shall be called with `CBS_OPEN_CANCELLED` and the `on_cbs_open_complete_context` shall be passed as argument. ]*/ + cbs->on_cbs_open_complete(cbs->on_cbs_open_complete_context, CBS_OPEN_CANCELLED); + break; + + case AMQP_MANAGEMENT_OPEN_OK: + /* Codes_SRS_CBS_01_106: [ If CBS is OPENING and `open_result` is `AMQP_MANAGEMENT_OPEN_OK` the callback `on_cbs_open_complete` shall be called with `CBS_OPEN_OK` and the `on_cbs_open_complete_context` shall be passed as argument. ]*/ + cbs->cbs_state = CBS_STATE_OPEN; + cbs->on_cbs_open_complete(cbs->on_cbs_open_complete_context, CBS_OPEN_OK); + break; + } + break; + } + } + } +} + +static void on_underlying_amqp_management_error(void* context) +{ + if (context == NULL) + { + /* Codes_SRS_CBS_01_110: [ When `on_amqp_management_error` is called with NULL `context`, it shall do nothing. ]*/ + LogError("on_underlying_amqp_management_error called with NULL context"); + } + else + { + CBS_HANDLE cbs = (CBS_HANDLE)context; + + switch (cbs->cbs_state) + { + default: + LogError("AMQP management error in unknown state"); + break; + + case CBS_STATE_CLOSED: + LogError("Unexpected AMQP error in CLOSED state"); + break; + + case CBS_STATE_OPENING: + cbs->cbs_state = CBS_STATE_CLOSED; + /* Codes_SRS_CBS_01_114: [ Additionally the underlying AMQP management shall be closed by calling `amqp_management_close`. ]*/ + (void)amqp_management_close(cbs->amqp_management); + /* Codes_SRS_CBS_01_111: [ If CBS is OPENING the callback `on_cbs_open_complete` shall be called with `CBS_OPEN_ERROR` and the `on_cbs_open_complete_context` shall be passed as argument. ]*/ + cbs->on_cbs_open_complete(cbs->on_cbs_open_complete_context, CBS_OPEN_ERROR); + break; + + case CBS_STATE_OPEN: + /* Codes_SRS_CBS_01_112: [ If CBS is OPEN the callback `on_cbs_error` shall be called and the `on_cbs_error_context` shall be passed as argument. ]*/ + cbs->cbs_state = CBS_STATE_ERROR; + cbs->on_cbs_error(cbs->on_cbs_error_context); + break; + } + } +} + +static void on_amqp_management_execute_operation_complete(void* context, AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT execute_operation_result, unsigned int status_code, const char* status_description, MESSAGE_HANDLE message) +{ + if (context == NULL) + { + /* Codes_SRS_CBS_01_091: [ When `on_amqp_management_execute_operation_complete` is called with a NULL context it shall do nothing. ]*/ + LogError("on_amqp_management_execute_operation_complete called with NULL context"); + } + else + { + /* Codes_SRS_CBS_01_103: [ The `context` shall be used to obtain the pending operation information stored in the pending operations linked list by calling `singlylinkedlist_item_get_value`. ]*/ + CBS_OPERATION* cbs_operation = (CBS_OPERATION*)singlylinkedlist_item_get_value((LIST_ITEM_HANDLE)context); + CBS_OPERATION_RESULT cbs_operation_result; + + (void)message; + + if (cbs_operation == NULL) + { + LogError("NULL cbs_operation"); + } + else + { + switch (execute_operation_result) + { + default: + cbs_operation_result = CBS_OPERATION_RESULT_CBS_ERROR; + break; + + case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: + /* Tests_SRS_CBS_01_094: [ When `on_amqp_management_execute_operation_complete` is called with `AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS`, the associated cbs operation complete callback shall be called with `CBS_OPERATION_RESULT_OPERATION_FAILED` and passing the `on_cbs_put_token_complete_context` as the context argument. ]*/ + cbs_operation_result = CBS_OPERATION_RESULT_OPERATION_FAILED; + break; + + case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: + /* Tests_SRS_CBS_01_115: [ When `on_amqp_management_execute_operation_complete` is called with `AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED`, the associated cbs operation complete callback shall be called with `CBS_OPERATION_RESULT_INSTANCE_CLOSED` and passing the `on_cbs_put_token_complete_context` as the context argument. ]*/ + cbs_operation_result = CBS_OPERATION_RESULT_INSTANCE_CLOSED; + break; + + case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: + /* Tests_SRS_CBS_01_093: [ When `on_amqp_management_execute_operation_complete` is called with `AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR`, the associated cbs operation complete callback shall be called with `CBS_OPERATION_RESULT_CBS_ERROR` and passing the `on_cbs_put_token_complete_context` as the context argument. ]*/ + cbs_operation_result = CBS_OPERATION_RESULT_CBS_ERROR; + break; + + case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: + /* Codes_SRS_CBS_01_092: [ When `on_amqp_management_execute_operation_complete` is called with `AMQP_MANAGEMENT_EXECUTE_OPERATION_OK`, the associated cbs operation complete callback shall be called with `CBS_OPERATION_RESULT_OK` and passing the `on_cbs_put_token_complete_context` as the context argument. ]*/ + cbs_operation_result = CBS_OPERATION_RESULT_OK; + break; + } + + /* Codes_SRS_CBS_01_095: [ `status_code` and `status_description` shall be passed as they are to the cbs operation complete callback. ]*/ + /* Codes_SRS_CBS_01_014: [ The response message has the following application-properties: ]*/ + /* Codes_SRS_CBS_01_013: [ status-code No int HTTP response code [RFC2616]. ]*/ + /* Codes_SRS_CBS_01_015: [ status-description Yes string Description of the status. ]*/ + /* Codes_SRS_CBS_01_016: [ The body of the message MUST be empty. ]*/ + /* Codes_SRS_CBS_01_026: [ The response message has the following application-properties: ]*/ + /* Codes_SRS_CBS_01_027: [ status-code Yes int HTTP response code [RFC2616]. ]*/ + /* Codes_SRS_CBS_01_028: [ status-description No string Description of the status. ]*/ + /* Codes_SRS_CBS_01_029: [ The body of the message MUST be empty. ]*/ + cbs_operation->on_cbs_operation_complete(cbs_operation->on_cbs_operation_complete_context, cbs_operation_result, status_code, status_description); + + /* Codes_SRS_CBS_01_102: [ The pending operation shall be removed from the pending operations list by calling `singlylinkedlist_remove`. ]*/ + if (singlylinkedlist_remove(cbs_operation->pending_operations, (LIST_ITEM_HANDLE)context) != 0) + { + LogError("Failed removing operation from the pending list"); + } + + /* Codes_SRS_CBS_01_096: [ The `context` for the operation shall also be freed. ]*/ + free(cbs_operation); + } + } +} + +CBS_HANDLE cbs_create(SESSION_HANDLE session) +{ + CBS_INSTANCE* cbs; + + if (session == NULL) + { + /* Codes_SRS_CBS_01_033: [** If `session` is NULL then `cbs_create` shall fail and return NULL. ]*/ + LogError("NULL session handle"); + cbs = NULL; + } + else + { + cbs = (CBS_INSTANCE*)malloc(sizeof(CBS_INSTANCE)); + if (cbs == NULL) + { + /* Codes_SRS_CBS_01_076: [ If allocating memory for the new handle fails, `cbs_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for cbs instance."); + } + else + { + /* Codes_SRS_CBS_01_097: [ `cbs_create` shall create a singly linked list for pending operations by calling `singlylinkedlist_create`. ]*/ + cbs->pending_operations = singlylinkedlist_create(); + if (cbs->pending_operations == NULL) + { + /* Codes_SRS_CBS_01_101: [ If `singlylinkedlist_create` fails, `cbs_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate pending operations list."); + } + else + { + /* Codes_SRS_CBS_01_034: [ `cbs_create` shall create an AMQP management handle by calling `amqp_management_create`. ]*/ + /* Codes_SRS_CBS_01_002: [ Tokens are communicated between AMQP peers by sending specially-formatted AMQP messages to the Claims-based Security Node. ]*/ + /* Codes_SRS_CBS_01_003: [ The mechanism follows the scheme defined in the AMQP Management specification [AMQPMAN]. ]*/ + cbs->amqp_management = amqp_management_create(session, "$cbs"); + if (cbs->amqp_management == NULL) + { + LogError("Cannot create AMQP management instance for the $cbs node."); + } + else + { + /* Codes_SRS_CBS_01_116: [ If setting the override key names fails, then `cbs_create` shall fail and return NULL. ]*/ + if (amqp_management_set_override_status_code_key_name(cbs->amqp_management, "status-code") != 0) + { + /* Codes_SRS_CBS_01_116: [ If setting the override key names fails, then `cbs_create` shall fail and return NULL. ]*/ + LogError("Cannot set the override status code key name"); + } + else + { + /* Codes_SRS_CBS_01_118: [ `cbs_create` shall set the override status description key name on the AMQP management handle to `status-description` by calling `amqp_management_set_override_status_description_key_name`. ]*/ + if (amqp_management_set_override_status_description_key_name(cbs->amqp_management, "status-description") != 0) + { + /* Codes_SRS_CBS_01_116: [ If setting the override key names fails, then `cbs_create` shall fail and return NULL. ]*/ + LogError("Cannot set the override status description key name"); + } + else + { + /* Codes_SRS_CBS_01_001: [ `cbs_create` shall create a new CBS instance and on success return a non-NULL handle to it. ]*/ + cbs->cbs_state = CBS_STATE_CLOSED; + + goto all_ok; + } + } + + amqp_management_destroy(cbs->amqp_management); + } + + singlylinkedlist_destroy(cbs->pending_operations); + } + + free(cbs); + cbs = NULL; + } + } + +all_ok: + return cbs; +} + +void cbs_destroy(CBS_HANDLE cbs) +{ + if (cbs == NULL) + { + /* Codes_SRS_CBS_01_037: [ If `cbs` is NULL, `cbs_destroy` shall do nothing. ]*/ + LogError("NULL cbs handle"); + } + else + { + LIST_ITEM_HANDLE first_pending_operation; + + /* Codes_SRS_CBS_01_100: [ If the CBS instance is not closed, all actions performed by `cbs_close` shall be performed. ]*/ + if (cbs->cbs_state != CBS_STATE_CLOSED) + { + (void)amqp_management_close(cbs->amqp_management); + } + + /* Codes_SRS_CBS_01_036: [ `cbs_destroy` shall free all resources associated with the handle `cbs`. ]*/ + /* Codes_SRS_CBS_01_038: [ `cbs_destroy` shall free the AMQP management handle created in `cbs_create` by calling `amqp_management_destroy`. ]*/ + amqp_management_destroy(cbs->amqp_management); + + /* Codes_SRS_CBS_01_099: [ All pending operations shall be freed. ]*/ + while ((first_pending_operation = singlylinkedlist_get_head_item(cbs->pending_operations)) != NULL) + { + CBS_OPERATION* pending_operation = (CBS_OPERATION*)singlylinkedlist_item_get_value(first_pending_operation); + if (pending_operation != NULL) + { + pending_operation->on_cbs_operation_complete(pending_operation->on_cbs_operation_complete_context, CBS_OPERATION_RESULT_INSTANCE_CLOSED, 0, NULL); + free(pending_operation); + } + + singlylinkedlist_remove(cbs->pending_operations, first_pending_operation); + } + + /* Codes_SRS_CBS_01_098: [ `cbs_destroy` shall free the pending operations list by calling `singlylinkedlist_destroy`. ]*/ + singlylinkedlist_destroy(cbs->pending_operations); + free(cbs); + } +} + +int cbs_open_async(CBS_HANDLE cbs, ON_CBS_OPEN_COMPLETE on_cbs_open_complete, void* on_cbs_open_complete_context, ON_CBS_ERROR on_cbs_error, void* on_cbs_error_context) +{ + int result; + + /* Codes_SRS_CBS_01_042: [ `on_cbs_open_complete_context` and `on_cbs_error_context` shall be allowed to be NULL. ]*/ + if ((cbs == NULL) || + (on_cbs_open_complete == NULL) || + (on_cbs_error == NULL)) + { + /* Codes_SRS_CBS_01_040: [ If any of the arguments `cbs`, `on_cbs_open_complete` or `on_cbs_error` is NULL, then `cbs_open_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: cbs = %p, on_cbs_open_complete = %p, on_cbs_error = %p", + cbs, on_cbs_open_complete, on_cbs_error); + result = __FAILURE__; + } + else if (cbs->cbs_state != CBS_STATE_CLOSED) + { + /* Codes_SRS_CBS_01_043: [ `cbs_open_async` while already open or opening shall fail and return a non-zero value. ]*/ + LogError("cbs instance already open"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_078: [ The cbs instance shall be considered OPENING until the `on_amqp_management_open_complete` callback is called by the AMQP management instance indicating that the open has completed (succesfull or not). ]*/ + cbs->cbs_state = CBS_STATE_OPENING; + cbs->on_cbs_open_complete = on_cbs_open_complete; + cbs->on_cbs_open_complete_context = on_cbs_open_complete_context; + cbs->on_cbs_error = on_cbs_error; + cbs->on_cbs_error_context = on_cbs_error_context; + + /* Codes_SRS_CBS_01_039: [ `cbs_open_async` shall open the cbs communication by calling `amqp_management_open_async` on the AMQP management handle created in `cbs_create`. ]*/ + if (amqp_management_open_async(cbs->amqp_management, on_underlying_amqp_management_open_complete, cbs, on_underlying_amqp_management_error, cbs) != 0) + { + /* Codes_SRS_CBS_01_041: [ If `amqp_management_open_async` fails, shall fail and return a non-zero value. ]*/ + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_077: [ On success, `cbs_open_async` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int cbs_close(CBS_HANDLE cbs) +{ + int result; + + if (cbs == NULL) + { + /* Codes_SRS_CBS_01_045: [ If the argument `cbs` is NULL, `cbs_close` shall fail and return a non-zero value. ]*/ + LogError("NULL cbs handle"); + result = __FAILURE__; + } + else if (cbs->cbs_state == CBS_STATE_CLOSED) + { + /* Codes_SRS_CBS_01_047: [ `cbs_close` when closed shall fail and return a non-zero value. ]*/ + /* Codes_SRS_CBS_01_048: [ `cbs_close` when not opened shall fail and return a non-zero value. ]*/ + LogError("Already closed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_044: [ `cbs_close` shall close the CBS instance by calling `amqp_management_close` on the underlying AMQP management handle. ]*/ + if (amqp_management_close(cbs->amqp_management) != 0) + { + /* Codes_SRS_CBS_01_046: [ If `amqp_management_close` fails, `cbs_close` shall fail and return a non-zero value. ]*/ + LogError("Failed closing AMQP management instance"); + result = __FAILURE__; + } + else + { + if (cbs->cbs_state == CBS_STATE_OPENING) + { + /* Codes_SRS_CBS_01_079: [ If `cbs_close` is called while OPENING, the `on_cbs_open_complete` callback shall be called with `CBS_OPEN_CANCELLED`, while passing the `on_cbs_open_complete_context` as argument. ]*/ + cbs->on_cbs_open_complete(cbs->on_cbs_open_complete_context, CBS_OPEN_CANCELLED); + } + + cbs->cbs_state = CBS_STATE_CLOSED; + + /* Codes_SRS_CBS_01_080: [ On success, `cbs_close` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int cbs_put_token_async(CBS_HANDLE cbs, const char* type, const char* audience, const char* token, ON_CBS_OPERATION_COMPLETE on_cbs_put_token_complete, void* on_cbs_put_token_complete_context) +{ + int result; + + /* Codes_SRS_CBS_01_083: [ `on_cbs_put_token_complete_context` shall be allowed to be NULL. ]*/ + if ((cbs == NULL) || + (type == NULL) || + (audience == NULL) || + (token == NULL) || + (on_cbs_put_token_complete == NULL)) + { + /* Codes_SRS_CBS_01_050: [ If any of the arguments `cbs`, `type`, `audience`, `token` or `on_cbs_put_token_complete` is NULL `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: cbs = %p, type = %p, audience = %p, token = %p, on_cbs_put_token_complete = %p", + cbs, type, audience, token, on_cbs_put_token_complete); + result = __FAILURE__; + } + else if ((cbs->cbs_state == CBS_STATE_CLOSED) || + (cbs->cbs_state == CBS_STATE_ERROR)) + { + /* Codes_SRS_CBS_01_058: [ If `cbs_put_token_async` is called when the CBS instance is not yet open or in error, it shall fail and return a non-zero value. ]*/ + LogError("put token called while closed or in error"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_049: [ `cbs_put_token_async` shall construct a request message for the `put-token` operation. ]*/ + MESSAGE_HANDLE message = message_create(); + if (message == NULL) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("message_create failed"); + result = __FAILURE__; + } + else + { + AMQP_VALUE token_value = amqpvalue_create_string(token); + if (token_value == NULL) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed creating token AMQP value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_009: [ The body of the message MUST contain the token. ]*/ + if (message_set_body_amqp_value(message, token_value) != 0) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed setting the token in the message body"); + result = __FAILURE__; + } + else + { + AMQP_VALUE application_properties = amqpvalue_create_map(); + if (application_properties == NULL) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed creating application properties map"); + result = __FAILURE__; + } + else + { + if (add_string_key_value_pair_to_map(application_properties, "name", audience) != 0) + { + result = __FAILURE__; + } + else + { + if (message_set_application_properties(message, application_properties) != 0) + { + /* Codes_SRS_CBS_01_072: [ If constructing the message fails, `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed setting message application properties"); + result = __FAILURE__; + } + else + { + CBS_OPERATION* cbs_operation = (CBS_OPERATION*)malloc(sizeof(CBS_OPERATION)); + if (cbs_operation == NULL) + { + LogError("Failed allocating CBS operation instance"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE list_item; + + cbs_operation->on_cbs_operation_complete = on_cbs_put_token_complete; + cbs_operation->on_cbs_operation_complete_context = on_cbs_put_token_complete_context; + cbs_operation->pending_operations = cbs->pending_operations; + + list_item = singlylinkedlist_add(cbs->pending_operations, cbs_operation); + if (list_item == NULL) + { + free(cbs_operation); + LogError("Failed adding pending operation to list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_051: [ `cbs_put_token_async` shall start the AMQP management operation by calling `amqp_management_execute_operation_async`, while passing to it: ]*/ + /* Codes_SRS_CBS_01_052: [ The `amqp_management` argument shall be the one for the AMQP management instance created in `cbs_create`. ]*/ + /* Codes_SRS_CBS_01_053: [ The `operation` argument shall be `put-token`. ]*/ + /* Codes_SRS_CBS_01_054: [ The `type` argument shall be set to the `type` argument. ]*/ + /* Codes_SRS_CBS_01_055: [ The `locales` argument shall be set to NULL. ]*/ + /* Codes_SRS_CBS_01_056: [ The `message` argument shall be the message constructed earlier according to the CBS spec. ]*/ + /* Codes_SRS_CBS_01_057: [ The arguments `on_execute_operation_complete` and `context` shall be set to a callback that is to be called by the AMQP management module when the operation is complete. ]*/ + /* Codes_SRS_CBS_01_005: [ operation No string "put-token" ]*/ + /* Codes_SRS_CBS_01_006: [ Type No string The type of the token being put, e.g., "amqp:jwt". ]*/ + /* Codes_SRS_CBS_01_007: [ name No string The "audience" to which the token applies. ]*/ + if (amqp_management_execute_operation_async(cbs->amqp_management, "put-token", type, NULL, message, on_amqp_management_execute_operation_complete, list_item) != 0) + { + singlylinkedlist_remove(cbs->pending_operations, list_item); + free(cbs_operation); + /* Codes_SRS_CBS_01_084: [ If `amqp_management_execute_operation_async` fails `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed starting AMQP management operation"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_081: [ On success `cbs_put_token_async` shall return 0. ]*/ + result = 0; + } + } + } + } + } + + amqpvalue_destroy(application_properties); + } + + amqpvalue_destroy(token_value); + } + } + + message_destroy(message); + } + } + + return result; +} + +int cbs_delete_token_async(CBS_HANDLE cbs, const char* type, const char* audience, ON_CBS_OPERATION_COMPLETE on_cbs_delete_token_complete, void* on_cbs_delete_token_complete_context) +{ + int result; + + /* Codes_SRS_CBS_01_086: [ `on_cbs_delete_token_complete_context` shall be allowed to be NULL. ]*/ + if ((cbs == NULL) || + (type == NULL) || + (audience == NULL) || + (on_cbs_delete_token_complete == NULL)) + { + /* Codes_SRS_CBS_01_060: [ If any of the arguments `cbs`, `type`, `audience` or `on_cbs_delete_token_complete` is NULL `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: cbs = %p, type = %p, audience = %p, on_cbs_delete_token_complete = %p", + cbs, type, audience, on_cbs_delete_token_complete); + result = __FAILURE__; + } + else if ((cbs->cbs_state == CBS_STATE_CLOSED) || + (cbs->cbs_state == CBS_STATE_ERROR)) + { + /* Codes_SRS_CBS_01_067: [ If `cbs_delete_token_async` is called when the CBS instance is not yet open or in error, it shall fail and return a non-zero value. ]*/ + LogError("put token called while closed or in error"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_025: [ The body of the message MUST be empty. ]*/ + /* Codes_SRS_CBS_01_059: [ `cbs_delete_token_async` shall construct a request message for the `delete-token` operation. ]*/ + MESSAGE_HANDLE message = message_create(); + if (message == NULL) + { + /* Codes_SRS_CBS_01_071: [ If constructing the message fails, `cbs_delete_token_async` shall fail and return a non-zero value. ]*/ + LogError("message_create failed"); + result = __FAILURE__; + } + else + { + AMQP_VALUE application_properties = amqpvalue_create_map(); + if (application_properties == NULL) + { + /* Codes_SRS_CBS_01_071: [ If constructing the message fails, `cbs_delete_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed creating application properties map"); + result = __FAILURE__; + } + else + { + if (add_string_key_value_pair_to_map(application_properties, "name", audience) != 0) + { + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_021: [ The request message has the following application-properties: ]*/ + if (message_set_application_properties(message, application_properties) != 0) + { + /* Codes_SRS_CBS_01_071: [ If constructing the message fails, `cbs_delete_token_async` shall fail and return a non-zero value. ]*/ + LogError("Failed setting message application properties"); + result = __FAILURE__; + } + else + { + CBS_OPERATION* cbs_operation = (CBS_OPERATION*)malloc(sizeof(CBS_OPERATION)); + if (cbs_operation == NULL) + { + LogError("Failed allocating CBS operation instance"); + result = __FAILURE__; + } + else + { + LIST_ITEM_HANDLE list_item; + + cbs_operation->on_cbs_operation_complete = on_cbs_delete_token_complete; + cbs_operation->on_cbs_operation_complete_context = on_cbs_delete_token_complete_context; + cbs_operation->pending_operations = cbs->pending_operations; + + list_item = singlylinkedlist_add(cbs->pending_operations, cbs_operation); + if (list_item == NULL) + { + free(cbs_operation); + LogError("Failed adding pending operation to list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_061: [ `cbs_delete_token_async` shall start the AMQP management operation by calling `amqp_management_execute_operation_async`, while passing to it: ]*/ + /* Codes_SRS_CBS_01_085: [ The `amqp_management` argument shall be the one for the AMQP management instance created in `cbs_create`. ]*/ + /* Codes_SRS_CBS_01_062: [ The `operation` argument shall be `delete-token`. ]*/ + /* Codes_SRS_CBS_01_063: [ The `type` argument shall be set to the `type` argument. ]*/ + /* Codes_SRS_CBS_01_064: [ The `locales` argument shall be set to NULL. ]*/ + /* Codes_SRS_CBS_01_065: [ The `message` argument shall be the message constructed earlier according to the CBS spec. ]*/ + /* Codes_SRS_CBS_01_066: [ The arguments `on_operation_complete` and `context` shall be set to a callback that is to be called by the AMQP management module when the operation is complete. ]*/ + /* Codes_SRS_CBS_01_020: [ To instruct a peer to delete a token associated with a specific audience, a "delete-token" message can be sent to the CBS Node ]*/ + /* Codes_SRS_CBS_01_022: [ operation Yes string "delete-token" ]*/ + /* Codes_SRS_CBS_01_023: [ Type Yes string The type of the token being deleted, e.g., "amqp:jwt". ]*/ + /* Codes_SRS_CBS_01_024: [ name Yes string The "audience" of the token being deleted. ]*/ + if (amqp_management_execute_operation_async(cbs->amqp_management, "delete-token", type, NULL, message, on_amqp_management_execute_operation_complete, list_item) != 0) + { + /* Codes_SRS_CBS_01_087: [ If `amqp_management_execute_operation_async` fails `cbs_put_token_async` shall fail and return a non-zero value. ]*/ + singlylinkedlist_remove(cbs->pending_operations, list_item); + free(cbs_operation); + LogError("Failed starting AMQP management operation"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_082: [ On success `cbs_delete_token_async` shall return 0. ]*/ + result = 0; + } + } + } + } + } + + amqpvalue_destroy(application_properties); + } + + message_destroy(message); + } + } + return result; +} + +int cbs_set_trace(CBS_HANDLE cbs, bool trace_on) +{ + int result; + + if (cbs == NULL) + { + /* Codes_SRS_CBS_01_090: [ If the argument `cbs` is NULL, `cbs_set_trace` shall fail and return a non-zero value. ]*/ + LogError("NULL cbs handle"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_CBS_01_088: [ `cbs_set_trace` shall enable or disable tracing by calling `amqp_management_set_trace` to pass down the `trace_on` value. ]*/ + amqp_management_set_trace(cbs->amqp_management, trace_on); + + /* Codes_SRS_CBS_01_089: [ On success, `cbs_set_trace` shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/connection.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2173 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> + +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" + +#include "azure_uamqp_c/connection.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/amqpvalue_to_string.h" + +/* Requirements satisfied by the virtue of implementing the ISO:*/ +/* Codes_S_R_S_CONNECTION_01_088: [Any data appearing beyond the protocol header MUST match the version indicated by the protocol header.] */ +/* Codes_S_R_S_CONNECTION_01_015: [Implementations SHOULD NOT expect to be able to reuse open TCP sockets after close performatives have been exchanged.] */ + +/* Codes_S_R_S_CONNECTION_01_087: [The protocol header consists of the upper case ASCII letters "AMQP" followed by a protocol id of zero, followed by three unsigned bytes representing the major, minor, and revision of the protocol version (currently 1 (MAJOR), 0 (MINOR), 0 (REVISION)). In total this is an 8-octet sequence] */ +static const unsigned char amqp_header[] = { 'A', 'M', 'Q', 'P', 0, 1, 0, 0 }; + +typedef enum RECEIVE_FRAME_STATE_TAG +{ + RECEIVE_FRAME_STATE_FRAME_SIZE, + RECEIVE_FRAME_STATE_FRAME_DATA +} RECEIVE_FRAME_STATE; + +typedef struct ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_TAG +{ + ON_CONNECTION_CLOSE_RECEIVED on_connection_close_received; + void* context; +} ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION; + +typedef struct ENDPOINT_INSTANCE_TAG +{ + uint16_t incoming_channel; + uint16_t outgoing_channel; + ON_ENDPOINT_FRAME_RECEIVED on_endpoint_frame_received; + ON_CONNECTION_STATE_CHANGED on_connection_state_changed; + void* callback_context; + CONNECTION_HANDLE connection; +} ENDPOINT_INSTANCE; + +typedef struct CONNECTION_INSTANCE_TAG +{ + XIO_HANDLE io; + size_t header_bytes_received; + CONNECTION_STATE connection_state; + FRAME_CODEC_HANDLE frame_codec; + AMQP_FRAME_CODEC_HANDLE amqp_frame_codec; + ENDPOINT_INSTANCE** endpoints; + uint32_t endpoint_count; + char* host_name; + char* container_id; + TICK_COUNTER_HANDLE tick_counter; + uint32_t remote_max_frame_size; + + ON_SEND_COMPLETE on_send_complete; + void* on_send_complete_callback_context; + + ON_NEW_ENDPOINT on_new_endpoint; + void* on_new_endpoint_callback_context; + + ON_CONNECTION_STATE_CHANGED on_connection_state_changed; + void* on_connection_state_changed_callback_context; + ON_IO_ERROR on_io_error; + void* on_io_error_callback_context; + + ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION on_connection_close_received_event_subscription; + + /* options */ + uint32_t max_frame_size; + uint16_t channel_max; + milliseconds idle_timeout; + milliseconds remote_idle_timeout; + milliseconds remote_idle_timeout_send_frame_millisecond; + double idle_timeout_empty_frame_send_ratio; + tickcounter_ms_t last_frame_received_time; + tickcounter_ms_t last_frame_sent_time; + fields properties; + + unsigned int is_underlying_io_open : 1; + unsigned int idle_timeout_specified : 1; + unsigned int is_remote_frame_received : 1; + unsigned int is_trace_on : 1; +} CONNECTION_INSTANCE; + +/* Codes_S_R_S_CONNECTION_01_258: [on_connection_state_changed shall be invoked whenever the connection state changes.]*/ +static void connection_set_state(CONNECTION_HANDLE connection, CONNECTION_STATE connection_state) +{ + uint64_t i; + + CONNECTION_STATE previous_state = connection->connection_state; + connection->connection_state = connection_state; + + /* Codes_S_R_S_CONNECTION_22_001: [If a connection state changed occurs and a callback is registered the callback shall be called.] */ + if (connection->on_connection_state_changed) + { + connection->on_connection_state_changed(connection->on_connection_state_changed_callback_context, connection_state, previous_state); + } + + /* Codes_S_R_S_CONNECTION_01_260: [Each endpoint's on_connection_state_changed shall be called.] */ + for (i = 0; i < connection->endpoint_count; i++) + { + /* Codes_S_R_S_CONNECTION_01_259: [The callback_context passed in connection_create_endpoint.] */ + if (connection->endpoints[i]->on_connection_state_changed != NULL) + { + connection->endpoints[i]->on_connection_state_changed(connection->endpoints[i]->callback_context, connection_state, previous_state); + } + } +} + +// This callback usage needs to be either verified and commented or integrated into +// the state machine. +static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + (void)context; + (void)send_result; +} + +static int send_header(CONNECTION_HANDLE connection) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_093: [_ When the client opens a new socket connection to a server, it MUST send a protocol header with the client's preferred protocol version.] */ + /* Codes_S_R_S_CONNECTION_01_104: [Sending the protocol header shall be done by using xio_send.] */ + if (xio_send(connection->io, amqp_header, sizeof(amqp_header), unchecked_on_send_complete, NULL) != 0) + { + /* Codes_S_R_S_CONNECTION_01_106: [When sending the protocol header fails, the connection shall be immediately closed.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + /* Codes_S_R_S_CONNECTION_01_057: [END In this state it is illegal for either endpoint to write anything more onto the connection. The connection can be safely closed and discarded.] */ + connection_set_state(connection, CONNECTION_STATE_END); + + /* Codes_S_R_S_CONNECTION_01_105: [When xio_send fails, connection_dowork shall return a non-zero value.] */ + result = __FAILURE__; + } + else + { + if (connection->is_trace_on == 1) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "-> Header (AMQP 0.1.0.0)"); + } + + /* Codes_S_R_S_CONNECTION_01_041: [HDR SENT In this state the connection header has been sent to the peer but no connection header has been received.] */ + connection_set_state(connection, CONNECTION_STATE_HDR_SENT); + result = 0; + } + + return result; +} + +#ifndef NO_LOGGING +static const char* get_frame_type_as_string(AMQP_VALUE descriptor) +{ + const char* result; + + if (is_open_type_by_descriptor(descriptor)) + { + result = "[OPEN]"; + } + else if (is_begin_type_by_descriptor(descriptor)) + { + result = "[BEGIN]"; + } + else if (is_attach_type_by_descriptor(descriptor)) + { + result = "[ATTACH]"; + } + else if (is_flow_type_by_descriptor(descriptor)) + { + result = "[FLOW]"; + } + else if (is_disposition_type_by_descriptor(descriptor)) + { + result = "[DISPOSITION]"; + } + else if (is_transfer_type_by_descriptor(descriptor)) + { + result = "[TRANSFER]"; + } + else if (is_detach_type_by_descriptor(descriptor)) + { + result = "[DETACH]"; + } + else if (is_end_type_by_descriptor(descriptor)) + { + result = "[END]"; + } + else if (is_close_type_by_descriptor(descriptor)) + { + result = "[CLOSE]"; + } + else + { + result = "[Unknown]"; + } + + return result; +} +#endif // NO_LOGGING + +static void log_incoming_frame(AMQP_VALUE performative) +{ +#ifdef NO_LOGGING + UNUSED(performative); +#else + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + if (descriptor == NULL) + { + LogError("Error getting performative descriptor"); + } + else + { + char* performative_as_string; + LOG(AZ_LOG_TRACE, 0, "<- "); + LOG(AZ_LOG_TRACE, 0, (char*)get_frame_type_as_string(descriptor)); + performative_as_string = NULL; + LOG(AZ_LOG_TRACE, LOG_LINE, (performative_as_string = amqpvalue_to_string(performative))); + if (performative_as_string != NULL) + { + free(performative_as_string); + } + } +#endif +} + +static void log_outgoing_frame(AMQP_VALUE performative) +{ +#ifdef NO_LOGGING + UNUSED(performative); +#else + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + if (descriptor == NULL) + { + LogError("Error getting performative descriptor"); + } + else + { + char* performative_as_string; + LOG(AZ_LOG_TRACE, 0, "-> "); + LOG(AZ_LOG_TRACE, 0, (char*)get_frame_type_as_string(descriptor)); + performative_as_string = NULL; + LOG(AZ_LOG_TRACE, LOG_LINE, (performative_as_string = amqpvalue_to_string(performative))); + if (performative_as_string != NULL) + { + free(performative_as_string); + } + } +#endif +} + +static void on_bytes_encoded(void* context, const unsigned char* bytes, size_t length, bool encode_complete) +{ + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)context; + if (xio_send(connection->io, bytes, length, + (encode_complete && connection->on_send_complete != NULL) ? connection->on_send_complete : unchecked_on_send_complete, + connection->on_send_complete_callback_context) != 0) + { + LogError("Cannot send encoded bytes"); + + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } +} + +static int send_open_frame(CONNECTION_HANDLE connection) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_151: [The connection max_frame_size setting shall be passed down to the frame_codec when the Open frame is sent.] */ + if (frame_codec_set_max_frame_size(connection->frame_codec, connection->max_frame_size) != 0) + { + LogError("Cannot set max frame size"); + + /* Codes_S_R_S_CONNECTION_01_207: [If frame_codec_set_max_frame_size fails the connection shall be closed and the state set to END.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_134: [The container id field shall be filled with the container id specified in connection_create.] */ + OPEN_HANDLE open_performative = open_create(connection->container_id); + if (open_performative == NULL) + { + LogError("Cannot create OPEN performative"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_137: [The max_frame_size connection setting shall be set in the open frame by using open_set_max_frame_size.] */ + if (open_set_max_frame_size(open_performative, connection->max_frame_size) != 0) + { + LogError("Cannot set max frame size"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + /* Codes_S_R_S_CONNECTION_01_139: [The channel_max connection setting shall be set in the open frame by using open_set_channel_max.] */ + else if (open_set_channel_max(open_performative, connection->channel_max) != 0) + { + LogError("Cannot set max channel"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + /* Codes_S_R_S_CONNECTION_01_142: [If no idle_timeout value has been specified, no value shall be stamped in the open frame (no call to open_set_idle_time_out shall be made).] */ + else if ((connection->idle_timeout_specified) && + /* Codes_S_R_S_CONNECTION_01_141: [If idle_timeout has been specified by a call to connection_set_idle_timeout, then that value shall be stamped in the open frame.] */ + (open_set_idle_time_out(open_performative, connection->idle_timeout) != 0)) + { + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + /* Codes_S_R_S_CONNECTION_01_136: [If no hostname value has been specified, no value shall be stamped in the open frame (no call to open_set_hostname shall be made).] */ + else if ((connection->host_name != NULL) && + /* Codes_S_R_S_CONNECTION_01_135: [If hostname has been specified by a call to connection_set_hostname, then that value shall be stamped in the open frame.] */ + (open_set_hostname(open_performative, connection->host_name) != 0)) + { + LogError("Cannot set hostname"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + /* Codes_S_R_S_CONNECTION_01_243: [If no properties value has been specified, no value shall be stamped in the open frame (no call to open_set_properties shall be made).] */ + else if ((connection->properties != NULL) && + /* Codes_S_R_S_CONNECTION_01_244: [If properties has been specified by a call to connection_set_properties, then that value shall be stamped in the open frame.] */ + (open_set_properties(open_performative, connection->properties) != 0)) + { + LogError("Cannot set properties"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + AMQP_VALUE open_performative_value = amqpvalue_create_open(open_performative); + if (open_performative_value == NULL) + { + LogError("Cannot create OPEN AMQP value"); + + /* Codes_S_R_S_CONNECTION_01_208: [If the open frame cannot be constructed, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_002: [Each AMQP connection begins with an exchange of capabilities and limitations, including the maximum frame size.] */ + /* Codes_S_R_S_CONNECTION_01_004: [After establishing or accepting a TCP connection and sending the protocol header, each peer MUST send an open frame before sending any other frames.] */ + /* Codes_S_R_S_CONNECTION_01_005: [The open frame describes the capabilities and limits of that peer.] */ + /* Codes_S_R_S_CONNECTION_01_205: [Sending the AMQP OPEN frame shall be done by calling amqp_frame_codec_begin_encode_frame with channel number 0, the actual performative payload and 0 as payload_size.] */ + /* Codes_S_R_S_CONNECTION_01_006: [The open frame can only be sent on channel 0.] */ + connection->on_send_complete = NULL; + connection->on_send_complete_callback_context = NULL; + if (amqp_frame_codec_encode_frame(connection->amqp_frame_codec, 0, open_performative_value, NULL, 0, on_bytes_encoded, connection) != 0) + { + LogError("amqp_frame_codec_encode_frame failed"); + + /* Codes_S_R_S_CONNECTION_01_206: [If sending the frame fails, the connection shall be closed and state set to END.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + if (connection->is_trace_on == 1) + { + log_outgoing_frame(open_performative_value); + } + + /* Codes_S_R_S_CONNECTION_01_046: [OPEN SENT In this state the connection headers have been exchanged. An open frame has been sent to the peer but no open frame has yet been received.] */ + connection_set_state(connection, CONNECTION_STATE_OPEN_SENT); + result = 0; + } + + amqpvalue_destroy(open_performative_value); + } + } + + open_destroy(open_performative); + } + } + + return result; +} + +static int send_close_frame(CONNECTION_HANDLE connection, ERROR_HANDLE error_handle) +{ + int result; + CLOSE_HANDLE close_performative; + + /* Codes_S_R_S_CONNECTION_01_217: [The CLOSE frame shall be constructed by using close_create.] */ + close_performative = close_create(); + if (close_performative == NULL) + { + LogError("Cannot create close performative"); + result = __FAILURE__; + } + else + { + if ((error_handle != NULL) && + /* Codes_S_R_S_CONNECTION_01_238: [If set, this field indicates that the connection is being closed due to an error condition.] */ + (close_set_error(close_performative, error_handle) != 0)) + { + LogError("Cannot set error on CLOSE"); + result = __FAILURE__; + } + else + { + AMQP_VALUE close_performative_value = amqpvalue_create_close(close_performative); + if (close_performative_value == NULL) + { + LogError("Cannot create AMQP CLOSE performative value"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_215: [Sending the AMQP CLOSE frame shall be done by calling amqp_frame_codec_begin_encode_frame with channel number 0, the actual performative payload and 0 as payload_size.] */ + /* Codes_S_R_S_CONNECTION_01_013: [However, implementations SHOULD send it on channel 0] */ + connection->on_send_complete = NULL; + connection->on_send_complete_callback_context = NULL; + if (amqp_frame_codec_encode_frame(connection->amqp_frame_codec, 0, close_performative_value, NULL, 0, on_bytes_encoded, connection) != 0) + { + LogError("amqp_frame_codec_encode_frame failed"); + result = __FAILURE__; + } + else + { + if (connection->is_trace_on == 1) + { + log_outgoing_frame(close_performative_value); + } + + result = 0; + } + + amqpvalue_destroy(close_performative_value); + } + } + + close_destroy(close_performative); + } + + return result; +} + +static void close_connection_with_error(CONNECTION_HANDLE connection, const char* condition_value, const char* description, AMQP_VALUE info) +{ + ERROR_HANDLE error_handle = error_create(condition_value); + + if (error_handle == NULL) + { + /* Codes_S_R_S_CONNECTION_01_214: [If the close frame cannot be constructed or sent, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } + else + { + /* Codes_S_R_S_CONNECTION_01_219: [The error description shall be set to an implementation defined string.] */ + if (error_set_description(error_handle, description) != 0) + { + LogError("Cannot set error description on CLOSE frame"); + + /* Codes_S_R_S_CONNECTION_01_214: [If the close frame cannot be constructed or sent, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } + else if ((info != NULL) && + (error_set_info(error_handle, info) != 0)) + { + LogError("Cannot set error info on CLOSE frame"); + + /* Codes_S_R_S_CONNECTION_01_214: [If the close frame cannot be constructed or sent, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } + else if (send_close_frame(connection, error_handle) != 0) + { + LogError("Cannot send CLOSE frame"); + + /* Codes_S_R_S_CONNECTION_01_214: [If the close frame cannot be constructed or sent, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } + else + { + /* Codes_S_R_S_CONNECTION_01_213: [When passing the bytes to frame_codec fails, a CLOSE frame shall be sent and the state shall be set to DISCARDING.] */ + /* Codes_S_R_S_CONNECTION_01_055: [DISCARDING The DISCARDING state is a variant of the CLOSE SENT state where the close is triggered by an error.] */ + /* Codes_S_R_S_CONNECTION_01_010: [After writing this frame the peer SHOULD continue to read from the connection until it receives the partner's close frame ] */ + connection_set_state(connection, CONNECTION_STATE_DISCARDING); + } + + error_destroy(error_handle); + } +} + +static ENDPOINT_INSTANCE* find_session_endpoint_by_outgoing_channel(CONNECTION_HANDLE connection, uint16_t outgoing_channel) +{ + uint32_t i; + ENDPOINT_INSTANCE* result; + + for (i = 0; i < connection->endpoint_count; i++) + { + if (connection->endpoints[i]->outgoing_channel == outgoing_channel) + { + break; + } + } + + if (i == connection->endpoint_count) + { + LogError("Cannot find session endpoint for channel %u", (unsigned int)outgoing_channel); + result = NULL; + } + else + { + result = connection->endpoints[i]; + } + + return result; +} + +static ENDPOINT_INSTANCE* find_session_endpoint_by_incoming_channel(CONNECTION_HANDLE connection, uint16_t incoming_channel) +{ + uint32_t i; + ENDPOINT_INSTANCE* result; + + for (i = 0; i < connection->endpoint_count; i++) + { + if (connection->endpoints[i]->incoming_channel == incoming_channel) + { + break; + } + } + + if (i == connection->endpoint_count) + { + LogError("Cannot find session endpoint for channel %u", (unsigned int)incoming_channel); + result = NULL; + } + else + { + result = connection->endpoints[i]; + } + + return result; +} + +static int connection_byte_received(CONNECTION_HANDLE connection, unsigned char b) +{ + int result; + + switch (connection->connection_state) + { + default: + LogError("Unknown connection state: %d", (int)connection->connection_state); + result = __FAILURE__; + break; + + /* Codes_S_R_S_CONNECTION_01_039: [START In this state a connection exists, but nothing has been sent or received. This is the state an implementation would be in immediately after performing a socket connect or socket accept.] */ + case CONNECTION_STATE_START: + + /* Codes_S_R_S_CONNECTION_01_041: [HDR SENT In this state the connection header has been sent to the peer but no connection header has been received.] */ + case CONNECTION_STATE_HDR_SENT: + if (b != amqp_header[connection->header_bytes_received]) + { + /* Codes_S_R_S_CONNECTION_01_089: [If the incoming and outgoing protocol headers do not match, both peers MUST close their outgoing stream] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + connection->header_bytes_received++; + if (connection->header_bytes_received == sizeof(amqp_header)) + { + if (connection->is_trace_on == 1) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "<- Header (AMQP 0.1.0.0)"); + } + + connection_set_state(connection, CONNECTION_STATE_HDR_EXCH); + + if (send_open_frame(connection) != 0) + { + LogError("Cannot send open frame"); + connection_set_state(connection, CONNECTION_STATE_END); + } + } + + result = 0; + } + break; + + /* Codes_S_R_S_CONNECTION_01_040: [HDR RCVD In this state the connection header has been received from the peer but a connection header has not been sent.] */ + case CONNECTION_STATE_HDR_RCVD: + + /* Codes_S_R_S_CONNECTION_01_042: [HDR EXCH In this state the connection header has been sent to the peer and a connection header has been received from the peer.] */ + /* we should not really get into this state, but just in case, we would treat that in the same way as HDR_RCVD */ + case CONNECTION_STATE_HDR_EXCH: + + /* Codes_S_R_S_CONNECTION_01_045: [OPEN RCVD In this state the connection headers have been exchanged. An open frame has been received from the peer but an open frame has not been sent.] */ + case CONNECTION_STATE_OPEN_RCVD: + + /* Codes_S_R_S_CONNECTION_01_046: [OPEN SENT In this state the connection headers have been exchanged. An open frame has been sent to the peer but no open frame has yet been received.] */ + case CONNECTION_STATE_OPEN_SENT: + + /* Codes_S_R_S_CONNECTION_01_048: [OPENED In this state the connection header and the open frame have been both sent and received.] */ + case CONNECTION_STATE_OPENED: + /* Codes_S_R_S_CONNECTION_01_212: [After the initial handshake has been done all bytes received from the io instance shall be passed to the frame_codec for decoding by calling frame_codec_receive_bytes.] */ + if (frame_codec_receive_bytes(connection->frame_codec, &b, 1) != 0) + { + LogError("Cannot process received bytes"); + /* Codes_S_R_S_CONNECTION_01_218: [The error amqp:internal-error shall be set in the error.condition field of the CLOSE frame.] */ + /* Codes_S_R_S_CONNECTION_01_219: [The error description shall be set to an implementation defined string.] */ + close_connection_with_error(connection, "amqp:internal-error", "connection_byte_received::frame_codec_receive_bytes failed", NULL); + result = __FAILURE__; + } + else + { + result = 0; + } + + break; + } + + return result; +} + +static void connection_on_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + if (connection_byte_received((CONNECTION_HANDLE)context, buffer[i]) != 0) + { + LogError("Cannot process received bytes"); + break; + } + } +} + +static void connection_on_io_open_complete(void* context, IO_OPEN_RESULT io_open_result) +{ + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)context; + + if (io_open_result == IO_OPEN_OK) + { + /* Codes_S_R_S_CONNECTION_01_084: [The connection_instance state machine implementing the protocol requirements shall be run as part of connection_dowork.] */ + switch (connection->connection_state) + { + default: + LogError("Unknown connection state: %d", (int)connection->connection_state); + break; + + case CONNECTION_STATE_START: + /* Codes_S_R_S_CONNECTION_01_086: [Prior to sending any frames on a connection_instance, each peer MUST start by sending a protocol header that indicates the protocol version used on the connection_instance.] */ + /* Codes_S_R_S_CONNECTION_01_091: [The AMQP peer which acted in the role of the TCP client (i.e. the peer that actively opened the connection_instance) MUST immediately send its outgoing protocol header on establishment of the TCP connection_instance.] */ + if (send_header(connection) != 0) + { + LogError("Cannot send header"); + } + break; + + case CONNECTION_STATE_HDR_SENT: + case CONNECTION_STATE_OPEN_SENT: + case CONNECTION_STATE_OPENED: + break; + + case CONNECTION_STATE_HDR_EXCH: + /* Codes_S_R_S_CONNECTION_01_002: [Each AMQP connection_instance begins with an exchange of capabilities and limitations, including the maximum frame size.] */ + /* Codes_S_R_S_CONNECTION_01_004: [After establishing or accepting a TCP connection_instance and sending the protocol header, each peer MUST send an open frame before sending any other frames.] */ + /* Codes_S_R_S_CONNECTION_01_005: [The open frame describes the capabilities and limits of that peer.] */ + if (send_open_frame(connection) != 0) + { + LogError("Cannot send OPEN frame"); + connection_set_state(connection, CONNECTION_STATE_END); + } + break; + + case CONNECTION_STATE_OPEN_RCVD: + break; + } + } + else + { + connection_set_state(connection, CONNECTION_STATE_END); + } +} + +static void connection_on_io_error(void* context) +{ + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)context; + + /* Codes_S_R_S_CONNECTION_22_005: [If the io notifies the connection instance of an IO_STATE_ERROR state and an io error callback is registered, the connection shall call the registered callback.] */ + if (connection->on_io_error) + { + connection->on_io_error(connection->on_io_error_callback_context); + } + + if (connection->connection_state != CONNECTION_STATE_END) + { + /* Codes_S_R_S_CONNECTION_01_202: [If the io notifies the connection instance of an IO_STATE_ERROR state the connection shall be closed and the state set to END.] */ + connection_set_state(connection, CONNECTION_STATE_ERROR); + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + } +} + +static void on_empty_amqp_frame_received(void* context, uint16_t channel) +{ + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)context; + /* It does not matter on which channel we received the frame */ + (void)channel; + + if (connection->is_trace_on == 1) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "<- Empty frame"); + } + if (tickcounter_get_current_ms(connection->tick_counter, &connection->last_frame_received_time) != 0) + { + LogError("Cannot get tickcounter value"); + } +} + +static void on_amqp_frame_received(void* context, uint16_t channel, AMQP_VALUE performative, const unsigned char* payload_bytes, uint32_t payload_size) +{ + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)context; + + (void)channel; + + if (tickcounter_get_current_ms(connection->tick_counter, &connection->last_frame_received_time) != 0) + { + LogError("Cannot get tickcounter value"); + close_connection_with_error(connection, "amqp:internal-error", "cannot get current tick count", NULL); + } + else + { + if (connection->is_underlying_io_open) + { + switch (connection->connection_state) + { + default: + if (performative == NULL) + { + /* Codes_S_R_S_CONNECTION_01_223: [If the on_endpoint_frame_received is called with a NULL performative then the connection shall be closed with the error condition amqp:internal-error and an implementation defined error description.] */ + close_connection_with_error(connection, "amqp:internal-error", "connection_endpoint_frame_received::NULL performative", NULL); + LogError("connection_endpoint_frame_received::NULL performative"); + } + else + { + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + + if (connection->is_trace_on == 1) + { + log_incoming_frame(performative); + } + + if (is_open_type_by_descriptor(descriptor)) + { + if (channel != 0) + { + /* Codes_S_R_S_CONNECTION_01_006: [The open frame can only be sent on channel 0.] */ + /* Codes_S_R_S_CONNECTION_01_222: [If an Open frame is received in a manner violating the ISO specification, the connection shall be closed with condition amqp:not-allowed and description being an implementation defined string.] */ + close_connection_with_error(connection, "amqp:not-allowed", "OPEN frame received on a channel that is not 0", NULL); + LogError("OPEN frame received on a channel that is not 0"); + } + + if (connection->connection_state == CONNECTION_STATE_OPENED) + { + /* Codes_S_R_S_CONNECTION_01_239: [If an Open frame is received in the Opened state the connection shall be closed with condition amqp:illegal-state and description being an implementation defined string.] */ + close_connection_with_error(connection, "amqp:illegal-state", "OPEN frame received in the OPENED state", NULL); + LogError("OPEN frame received in the OPENED state"); + } + else if ((connection->connection_state == CONNECTION_STATE_OPEN_SENT) || + (connection->connection_state == CONNECTION_STATE_HDR_EXCH)) + { + OPEN_HANDLE open_handle; + if (amqpvalue_get_open(performative, &open_handle) != 0) + { + /* Codes_S_R_S_CONNECTION_01_143: [If any of the values in the received open frame are invalid then the connection shall be closed.] */ + /* Codes_S_R_S_CONNECTION_01_220: [The error amqp:invalid-field shall be set in the error.condition field of the CLOSE frame.] */ + close_connection_with_error(connection, "amqp:invalid-field", "connection_endpoint_frame_received::failed parsing OPEN frame", NULL); + LogError("connection_endpoint_frame_received::failed parsing OPEN frame"); + } + else + { + if (open_get_idle_time_out(open_handle, &connection->remote_idle_timeout) == 0) + { + /* since we obtained the remote_idle_timeout, compute at what millisecond we should send the empty frame */ + connection->remote_idle_timeout_send_frame_millisecond = (milliseconds)(connection->idle_timeout_empty_frame_send_ratio * connection->remote_idle_timeout); + } + + if ((open_get_max_frame_size(open_handle, &connection->remote_max_frame_size) != 0) || + /* Codes_S_R_S_CONNECTION_01_167: [Both peers MUST accept frames of up to 512 (MIN-MAX-FRAME-SIZE) octets.] */ + (connection->remote_max_frame_size < 512)) + { + /* Codes_S_R_S_CONNECTION_01_143: [If any of the values in the received open frame are invalid then the connection shall be closed.] */ + /* Codes_S_R_S_CONNECTION_01_220: [The error amqp:invalid-field shall be set in the error.condition field of the CLOSE frame.] */ + close_connection_with_error(connection, "amqp:invalid-field", "connection_endpoint_frame_received::failed parsing OPEN frame", NULL); + LogError("connection_endpoint_frame_received::failed parsing OPEN frame"); + } + else + { + if (connection->connection_state == CONNECTION_STATE_OPEN_SENT) + { + connection_set_state(connection, CONNECTION_STATE_OPENED); + } + else + { + if (send_open_frame(connection) != 0) + { + connection_set_state(connection, CONNECTION_STATE_END); + } + else + { + connection_set_state(connection, CONNECTION_STATE_OPENED); + } + } + } + + open_destroy(open_handle); + } + } + else + { + /* do nothing for now ... */ + } + } + else if (is_close_type_by_descriptor(descriptor)) + { + /* Codes_S_R_S_CONNECTION_01_242: [The connection module shall accept CLOSE frames even if they have extra payload bytes besides the Close performative.] */ + + /* Codes_S_R_S_CONNECTION_01_225: [HDR_RCVD HDR OPEN] */ + if ((connection->connection_state == CONNECTION_STATE_HDR_RCVD) || + /* Codes_S_R_S_CONNECTION_01_227: [HDR_EXCH OPEN OPEN] */ + (connection->connection_state == CONNECTION_STATE_HDR_EXCH) || + /* Codes_S_R_S_CONNECTION_01_228: [OPEN_RCVD OPEN *] */ + (connection->connection_state == CONNECTION_STATE_OPEN_RCVD) || + /* Codes_S_R_S_CONNECTION_01_235: [CLOSE_SENT - * TCP Close for Write] */ + (connection->connection_state == CONNECTION_STATE_CLOSE_SENT) || + /* Codes_S_R_S_CONNECTION_01_236: [DISCARDING - * TCP Close for Write] */ + (connection->connection_state == CONNECTION_STATE_DISCARDING)) + { + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + } + else + { + CLOSE_HANDLE close_handle; + + /* Codes_S_R_S_CONNECTION_01_012: [A close frame MAY be received on any channel up to the maximum channel number negotiated in open.] */ + if (channel > connection->channel_max) + { + close_connection_with_error(connection, "amqp:invalid-field", "connection_endpoint_frame_received::failed parsing CLOSE frame", NULL); + LogError("connection_endpoint_frame_received::failed parsing CLOSE frame"); + } + else + { + if (amqpvalue_get_close(performative, &close_handle) != 0) + { + close_connection_with_error(connection, "amqp:invalid-field", "connection_endpoint_frame_received::failed parsing CLOSE frame", NULL); + LogError("connection_endpoint_frame_received::failed parsing CLOSE frame"); + } + else + { + ERROR_HANDLE error; + + if (close_get_error(close_handle, &error) != 0) + { + error = NULL; + } + + close_destroy(close_handle); + + connection_set_state(connection, CONNECTION_STATE_CLOSE_RCVD); + + if (send_close_frame(connection, NULL) != 0) + { + LogError("Cannot send CLOSE frame"); + } + + /* Codes_S_R_S_CONNECTION_01_214: [If the close frame cannot be constructed or sent, the connection shall be closed and set to the END state.] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + + if (connection->on_connection_close_received_event_subscription.on_connection_close_received != NULL) + { + connection->on_connection_close_received_event_subscription.on_connection_close_received(connection->on_connection_close_received_event_subscription.context, error); + } + + error_destroy(error); + } + } + } + } + else + { + uint64_t performative_ulong; + + if (amqpvalue_get_ulong(descriptor, &performative_ulong) != 0) + { + LogError("Failed getting ulong amqp performative"); + } + else + { + switch (performative_ulong) + { + default: + LogError("Bad performative: %02x", performative); + break; + + case AMQP_BEGIN: + { + BEGIN_HANDLE begin; + + if (amqpvalue_get_begin(performative, &begin) != 0) + { + LogError("Cannot get begin performative"); + } + else + { + uint16_t remote_channel; + ENDPOINT_HANDLE new_endpoint = NULL; + bool remote_begin = false; + + if (begin_get_remote_channel(begin, &remote_channel) != 0) + { + remote_begin = true; + if (connection->on_new_endpoint != NULL) + { + new_endpoint = connection_create_endpoint(connection); + if (!connection->on_new_endpoint(connection->on_new_endpoint_callback_context, new_endpoint)) + { + connection_destroy_endpoint(new_endpoint); + new_endpoint = NULL; + } + } + } + + if (!remote_begin) + { + ENDPOINT_INSTANCE* session_endpoint = find_session_endpoint_by_outgoing_channel(connection, remote_channel); + if (session_endpoint == NULL) + { + LogError("Cannot create session endpoint"); + } + else + { + session_endpoint->incoming_channel = channel; + session_endpoint->on_endpoint_frame_received(session_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + else + { + if (new_endpoint != NULL) + { + new_endpoint->incoming_channel = channel; + new_endpoint->on_endpoint_frame_received(new_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + + begin_destroy(begin); + } + + break; + } + + case AMQP_FLOW: + case AMQP_TRANSFER: + case AMQP_DISPOSITION: + case AMQP_END: + case AMQP_ATTACH: + case AMQP_DETACH: + { + ENDPOINT_INSTANCE* session_endpoint = find_session_endpoint_by_incoming_channel(connection, channel); + if (session_endpoint == NULL) + { + LogError("Cannot find session endpoint for channel %u", (unsigned int)channel); + } + else + { + session_endpoint->on_endpoint_frame_received(session_endpoint->callback_context, performative, payload_size, payload_bytes); + } + + break; + } + } + } + } + } + break; + + case CONNECTION_STATE_START: + /* Codes_S_R_S_CONNECTION_01_224: [START HDR HDR] */ + case CONNECTION_STATE_HDR_SENT: + /* Codes_S_R_S_CONNECTION_01_226: [HDR_SENT OPEN HDR] */ + case CONNECTION_STATE_OPEN_PIPE: + /* Codes_S_R_S_CONNECTION_01_230: [OPEN_PIPE ** HDR] */ + case CONNECTION_STATE_OC_PIPE: + /* Codes_S_R_S_CONNECTION_01_232: [OC_PIPE - HDR TCP Close for Write] */ + case CONNECTION_STATE_CLOSE_RCVD: + /* Codes_S_R_S_CONNECTION_01_234: [CLOSE_RCVD * - TCP Close for Read] */ + case CONNECTION_STATE_END: + /* Codes_S_R_S_CONNECTION_01_237: [END - - TCP Close] */ + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + break; + } + } + } +} + +static void frame_codec_error(void* context) +{ + /* Bug: some error handling should happen here + Filed: uAMQP: frame_codec error and amqp_frame_codec_error should handle the errors */ + LogError("A frame_codec_error occured"); + (void)context; +} + +static void amqp_frame_codec_error(void* context) +{ + /* Bug: some error handling should happen here + Filed: uAMQP: frame_codec error and amqp_frame_codec_error should handle the errors */ + LogError("An amqp_frame_codec_error occured"); + (void)context; +} + +/* Codes_S_R_S_CONNECTION_01_001: [connection_create shall open a new connection to a specified host/port.] */ +CONNECTION_HANDLE connection_create(XIO_HANDLE xio, const char* hostname, const char* container_id, ON_NEW_ENDPOINT on_new_endpoint, void* callback_context) +{ + return connection_create2(xio, hostname, container_id, on_new_endpoint, callback_context, NULL, NULL, NULL, NULL); +} + +/* Codes_S_R_S_CONNECTION_01_001: [connection_create shall open a new connection to a specified host/port.] */ +/* Codes_S_R_S_CONNECTION_22_002: [connection_create shall allow registering connections state and io error callbacks.] */ +CONNECTION_HANDLE connection_create2(XIO_HANDLE xio, const char* hostname, const char* container_id, ON_NEW_ENDPOINT on_new_endpoint, void* callback_context, ON_CONNECTION_STATE_CHANGED on_connection_state_changed, void* on_connection_state_changed_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + CONNECTION_HANDLE connection; + + if ((xio == NULL) || + (container_id == NULL)) + { + /* Codes_S_R_S_CONNECTION_01_071: [If xio or container_id is NULL, connection_create shall return NULL.] */ + LogError("Bad arguments: xio = %p, container_id = %p", + xio, container_id); + connection = NULL; + } + else + { + connection = (CONNECTION_HANDLE)malloc(sizeof(CONNECTION_INSTANCE)); + /* Codes_S_R_S_CONNECTION_01_081: [If allocating the memory for the connection fails then connection_create shall return NULL.] */ + if (connection == NULL) + { + LogError("Cannot allocate memory for connection"); + } + else + { + connection->io = xio; + + /* Codes_S_R_S_CONNECTION_01_082: [connection_create shall allocate a new frame_codec instance to be used for frame encoding/decoding.] */ + connection->frame_codec = frame_codec_create(frame_codec_error, connection); + if (connection->frame_codec == NULL) + { + /* Codes_S_R_S_CONNECTION_01_083: [If frame_codec_create fails then connection_create shall return NULL.] */ + LogError("Cannot create frame_codec"); + free(connection); + connection = NULL; + } + else + { + connection->amqp_frame_codec = amqp_frame_codec_create(connection->frame_codec, on_amqp_frame_received, on_empty_amqp_frame_received, amqp_frame_codec_error, connection); + if (connection->amqp_frame_codec == NULL) + { + /* Codes_S_R_S_CONNECTION_01_108: [If amqp_frame_codec_create fails, connection_create shall return NULL.] */ + LogError("Cannot create amqp_frame_codec"); + frame_codec_destroy(connection->frame_codec); + free(connection); + connection = NULL; + } + else + { + if (hostname != NULL) + { + size_t hostname_length = strlen(hostname); + connection->host_name = (char*)malloc(hostname_length + 1); + if (connection->host_name == NULL) + { + /* Codes_S_R_S_CONNECTION_01_081: [If allocating the memory for the connection fails then connection_create shall return NULL.] */ + LogError("Cannot allocate memory for host name"); + amqp_frame_codec_destroy(connection->amqp_frame_codec); + frame_codec_destroy(connection->frame_codec); + free(connection); + connection = NULL; + } + else + { + (void)memcpy(connection->host_name, hostname, hostname_length + 1); + } + } + else + { + connection->host_name = NULL; + } + + if (connection != NULL) + { + size_t container_id_length = strlen(container_id); + connection->container_id = (char*)malloc(container_id_length + 1); + if (connection->container_id == NULL) + { + /* Codes_S_R_S_CONNECTION_01_081: [If allocating the memory for the connection fails then connection_create shall return NULL.] */ + LogError("Cannot allocate memory for container_id"); + free(connection->host_name); + amqp_frame_codec_destroy(connection->amqp_frame_codec); + frame_codec_destroy(connection->frame_codec); + free(connection); + connection = NULL; + } + else + { + connection->tick_counter = tickcounter_create(); + if (connection->tick_counter == NULL) + { + LogError("Cannot create tick counter"); + free(connection->container_id); + free(connection->host_name); + amqp_frame_codec_destroy(connection->amqp_frame_codec); + frame_codec_destroy(connection->frame_codec); + free(connection); + connection = NULL; + } + else + { + (void)memcpy(connection->container_id, container_id, container_id_length + 1); + + /* Codes_S_R_S_CONNECTION_01_173: [<field name="max-frame-size" type="uint" default="4294967295"/>] */ + connection->max_frame_size = 4294967295u; + /* Codes: [<field name="channel-max" type="ushort" default="65535"/>] */ + connection->channel_max = 65535; + + /* Codes_S_R_S_CONNECTION_01_175: [<field name="idle-time-out" type="milliseconds"/>] */ + /* Codes_S_R_S_CONNECTION_01_192: [A value of zero is the same as if it was not set (null).] */ + connection->idle_timeout = 0; + connection->remote_idle_timeout = 0; + connection->remote_idle_timeout_send_frame_millisecond = 0; + connection->idle_timeout_empty_frame_send_ratio = 0.5; + + connection->endpoint_count = 0; + connection->endpoints = NULL; + connection->header_bytes_received = 0; + connection->is_remote_frame_received = 0; + connection->properties = NULL; + + connection->is_underlying_io_open = 0; + connection->remote_max_frame_size = 512; + connection->is_trace_on = 0; + + /* Mark that settings have not yet been set by the user */ + connection->idle_timeout_specified = 0; + + connection->on_new_endpoint = on_new_endpoint; + connection->on_new_endpoint_callback_context = callback_context; + + connection->on_connection_close_received_event_subscription.on_connection_close_received = NULL; + connection->on_connection_close_received_event_subscription.context = NULL; + + connection->on_io_error = on_io_error; + connection->on_io_error_callback_context = on_io_error_context; + connection->on_connection_state_changed = on_connection_state_changed; + connection->on_connection_state_changed_callback_context = on_connection_state_changed_context; + + if (tickcounter_get_current_ms(connection->tick_counter, &connection->last_frame_received_time) != 0) + { + LogError("Could not retrieve time for last frame received time"); + tickcounter_destroy(connection->tick_counter); + free(connection->container_id); + free(connection->host_name); + amqp_frame_codec_destroy(connection->amqp_frame_codec); + frame_codec_destroy(connection->frame_codec); + free(connection); + connection = NULL; + } + else + { + connection->last_frame_sent_time = connection->last_frame_received_time; + + /* Codes_S_R_S_CONNECTION_01_072: [When connection_create succeeds, the state of the connection shall be CONNECTION_STATE_START.] */ + connection_set_state(connection, CONNECTION_STATE_START); + } + } + } + } + } + } + } + } + + return connection; +} + +void connection_destroy(CONNECTION_HANDLE connection) +{ + /* Codes_S_R_S_CONNECTION_01_079: [If handle is NULL, connection_destroy shall do nothing.] */ + if (connection == NULL) + { + LogError("NULL connection"); + } + else + { + /* Codes_S_R_S_CONNECTION_01_073: [connection_destroy shall free all resources associated with a connection.] */ + if (connection->is_underlying_io_open) + { + (void)connection_close(connection, NULL, NULL, NULL); + } + + amqp_frame_codec_destroy(connection->amqp_frame_codec); + frame_codec_destroy(connection->frame_codec); + tickcounter_destroy(connection->tick_counter); + if (connection->properties != NULL) + { + amqpvalue_destroy(connection->properties); + } + + free(connection->host_name); + free(connection->container_id); + + /* Codes_S_R_S_CONNECTION_01_074: [connection_destroy shall close the socket connection.] */ + free(connection); + } +} + +int connection_open(CONNECTION_HANDLE connection) +{ + int result; + + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else + { + if (!connection->is_underlying_io_open) + { + if (xio_open(connection->io, connection_on_io_open_complete, connection, connection_on_bytes_received, connection, connection_on_io_error, connection) != 0) + { + LogError("Opening the underlying IO failed"); + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + connection->is_underlying_io_open = 1; + + connection_set_state(connection, CONNECTION_STATE_START); + + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +int connection_listen(CONNECTION_HANDLE connection) +{ + int result; + + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else + { + if (!connection->is_underlying_io_open) + { + if (xio_open(connection->io, connection_on_io_open_complete, connection, connection_on_bytes_received, connection, connection_on_io_error, connection) != 0) + { + LogError("Opening the underlying IO failed"); + connection_set_state(connection, CONNECTION_STATE_END); + result = __FAILURE__; + } + else + { + connection->is_underlying_io_open = 1; + + connection_set_state(connection, CONNECTION_STATE_HDR_EXCH); + + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +int connection_close(CONNECTION_HANDLE connection, const char* condition_value, const char* description, AMQP_VALUE info) +{ + int result; + + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else if ((info != NULL) && + (amqpvalue_get_type(info) != AMQP_TYPE_MAP) && + (amqpvalue_get_type(info) != AMQP_TYPE_NULL)) + { + LogError("Invalid info, expected a map"); + result = __FAILURE__; + } + else + { + if (condition_value != NULL) + { + close_connection_with_error(connection, condition_value, description, info); + } + else + { + if (send_close_frame(connection, NULL) != 0) + { + LogError("Sending CLOSE frame failed"); + } + + connection_set_state(connection, CONNECTION_STATE_END); + } + + if (xio_close(connection->io, NULL, NULL) != 0) + { + LogError("xio_close failed"); + } + + connection->is_underlying_io_open = 1; + + result = 0; + } + + return result; +} + +int connection_set_max_frame_size(CONNECTION_HANDLE connection, uint32_t max_frame_size) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_163: [If connection is NULL, connection_set_max_frame_size shall fail and return a non-zero value.] */ + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + /* Codes_S_R_S_CONNECTION_01_150: [If the max_frame_size is invalid then connection_set_max_frame_size shall fail and return a non-zero value.] */ + /* Codes_S_R_S_CONNECTION_01_167: [Both peers MUST accept frames of up to 512 (MIN-MAX-FRAME-SIZE) octets.] */ + else if (max_frame_size < 512) + { + LogError("max_frame_size too small"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_157: [If connection_set_max_frame_size is called after the initial Open frame has been sent, it shall fail and return a non-zero value.] */ + if (connection->connection_state != CONNECTION_STATE_START) + { + LogError("Connection already open"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_148: [connection_set_max_frame_size shall set the max_frame_size associated with a connection.] */ + /* Codes_S_R_S_CONNECTION_01_164: [If connection_set_max_frame_size fails, the previous max_frame_size setting shall be retained.] */ + connection->max_frame_size = max_frame_size; + + /* Codes_S_R_S_CONNECTION_01_149: [On success connection_set_max_frame_size shall return 0.] */ + result = 0; + } + } + + return result; +} + +int connection_get_max_frame_size(CONNECTION_HANDLE connection, uint32_t* max_frame_size) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_170: [If connection or max_frame_size is NULL, connection_get_max_frame_size shall fail and return a non-zero value.] */ + if ((connection == NULL) || + (max_frame_size == NULL)) + { + LogError("Bad arguments: connection = %p, max_frame_size = %p", + connection, max_frame_size); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_168: [connection_get_max_frame_size shall return in the max_frame_size argument the current max frame size setting.] */ + *max_frame_size = connection->max_frame_size; + + /* Codes_S_R_S_CONNECTION_01_169: [On success, connection_get_max_frame_size shall return 0.] */ + result = 0; + } + + return result; +} + +int connection_set_channel_max(CONNECTION_HANDLE connection, uint16_t channel_max) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_181: [If connection is NULL then connection_set_channel_max shall fail and return a non-zero value.] */ + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_156: [If connection_set_channel_max is called after the initial Open frame has been sent, it shall fail and return a non-zero value.] */ + if (connection->connection_state != CONNECTION_STATE_START) + { + LogError("Connection already open"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_153: [connection_set_channel_max shall set the channel_max associated with a connection.] */ + /* Codes_S_R_S_CONNECTION_01_165: [If connection_set_channel_max fails, the previous channel_max setting shall be retained.] */ + connection->channel_max = channel_max; + + /* Codes_S_R_S_CONNECTION_01_154: [On success connection_set_channel_max shall return 0.] */ + result = 0; + } + } + + return result; +} + +int connection_get_channel_max(CONNECTION_HANDLE connection, uint16_t* channel_max) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_184: [If connection or channel_max is NULL, connection_get_channel_max shall fail and return a non-zero value.] */ + if ((connection == NULL) || + (channel_max == NULL)) + { + LogError("Bad arguments: connection = %p, channel_max = %p", + connection, channel_max); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_182: [connection_get_channel_max shall return in the channel_max argument the current channel_max setting.] */ + *channel_max = connection->channel_max; + + /* Codes_S_R_S_CONNECTION_01_183: [On success, connection_get_channel_max shall return 0.] */ + result = 0; + } + + return result; +} + +int connection_set_idle_timeout(CONNECTION_HANDLE connection, milliseconds idle_timeout) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_191: [If connection is NULL, connection_set_idle_timeout shall fail and return a non-zero value.] */ + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_158: [If connection_set_idle_timeout is called after the initial Open frame has been sent, it shall fail and return a non-zero value.] */ + if (connection->connection_state != CONNECTION_STATE_START) + { + LogError("Connection already open"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_159: [connection_set_idle_timeout shall set the idle_timeout associated with a connection.] */ + /* Codes_S_R_S_CONNECTION_01_166: [If connection_set_idle_timeout fails, the previous idle_timeout setting shall be retained.] */ + connection->idle_timeout = idle_timeout; + connection->idle_timeout_specified = true; + + /* Codes_S_R_S_CONNECTION_01_160: [On success connection_set_idle_timeout shall return 0.] */ + result = 0; + } + } + + return result; +} + +int connection_get_idle_timeout(CONNECTION_HANDLE connection, milliseconds* idle_timeout) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_190: [If connection or idle_timeout is NULL, connection_get_idle_timeout shall fail and return a non-zero value.] */ + if ((connection == NULL) || + (idle_timeout == NULL)) + { + LogError("Bad arguments: connection = %p, idle_timeout = %p", + connection, idle_timeout); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_188: [connection_get_idle_timeout shall return in the idle_timeout argument the current idle_timeout setting.] */ + *idle_timeout = connection->idle_timeout; + + /* Codes_S_R_S_CONNECTION_01_189: [On success, connection_get_idle_timeout shall return 0.] */ + result = 0; + } + + return result; +} + +int connection_set_properties(CONNECTION_HANDLE connection, fields properties) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_261: [If connection is NULL, connection_set_properties shall fail and return a non-zero value.] */ + if (connection == NULL) + { + LogError("NULL connection"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_262: [If connection_set_properties is called after the initial Open frame has been sent, it shall fail and return a non-zero value.] */ + if (connection->connection_state != CONNECTION_STATE_START) + { + LogError("Connection already open"); + result = __FAILURE__; + } + else + { + if (properties == NULL) + { + /* Codes_S_R_S_CONNECTION_01_263: [ If `properties` is NULL, the previously stored properties associated with `connection` shall be freed. ]*/ + if (connection->properties != NULL) + { + fields_destroy(connection->properties); + connection->properties = NULL; + } + + /* Codes_S_R_S_CONNECTION_01_264: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + fields new_properties; + + /* Codes_S_R_S_CONNECTION_01_265: [ `connection_set_properties` shall copy the contents of `properties` as the properties contents for the connection instance identified by `connection`. ]*/ + /* Codes_S_R_S_CONNECTION_01_266: [ Cloning the properties shall be done by calling `fields_clone`. ]*/ + new_properties = fields_clone(properties); + if (new_properties == NULL) + { + /* Codes_S_R_S_CONNECTION_01_267: [ If `fields_clone` fails, `connection_set_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone connection properties"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_268: [ If setting the properties fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (connection->properties != NULL) + { + fields_destroy(connection->properties); + } + + connection->properties = new_properties; + + /* Codes_S_R_S_CONNECTION_01_264: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + } + + return result; +} + +int connection_get_properties(CONNECTION_HANDLE connection, fields* properties) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_269: [If connection or properties is NULL, connection_get_properties shall fail and return a non-zero value.] */ + if ((connection == NULL) || + (properties == NULL)) + { + LogError("Bad arguments: connection = %p, properties = %p", + connection, properties); + result = __FAILURE__; + } + else + { + if (connection->properties == NULL) + { + /* Codes_S_R_S_CONNECTION_01_270: [ If no properties have been set, `connection_get_properties` shall set `properties` to NULL. ]*/ + *properties = NULL; + + /* Codes_S_R_S_CONNECTION_01_271: [On success, connection_get_properties shall return 0.] */ + result = 0; + } + else + { + /* Codes_S_R_S_CONNECTION_01_272: [connection_get_properties shall return in the properties argument the current properties setting.] */ + /* Codes_S_R_S_CONNECTION_01_273: [ Cloning the properties shall be done by calling `fields_clone`. ]*/ + *properties = fields_clone(connection->properties); + if (*properties == NULL) + { + /* Codes_S_R_S_CONNECTION_01_274: [ If `fields_clone` fails, `connection_get_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone properties"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_271: [On success, connection_get_properties shall return 0.] */ + result = 0; + } + } + } + + return result; +} + +int connection_get_remote_max_frame_size(CONNECTION_HANDLE connection, uint32_t* remote_max_frame_size) +{ + int result; + + if ((connection == NULL) || + (remote_max_frame_size == NULL)) + { + LogError("Bad arguments: connection = %p, remote_max_frame_size = %p", + connection, remote_max_frame_size); + result = __FAILURE__; + } + else + { + *remote_max_frame_size = connection->remote_max_frame_size; + + result = 0; + } + + return result; +} + +uint64_t connection_handle_deadlines(CONNECTION_HANDLE connection) +{ + uint64_t local_deadline = (uint64_t)-1; + uint64_t remote_deadline = (uint64_t)-1; + + if (connection == NULL) + { + LogError("NULL connection"); + } + else + { + tickcounter_ms_t current_ms; + + if (tickcounter_get_current_ms(connection->tick_counter, ¤t_ms) != 0) + { + LogError("Could not get tick counter value"); + close_connection_with_error(connection, "amqp:internal-error", "Could not get tick count", NULL); + } + else + { + if (connection->idle_timeout_specified && (connection->idle_timeout != 0)) + { + /* Calculate time until configured idle timeout expires */ + + uint64_t time_since_last_received = current_ms - connection->last_frame_received_time; + if (time_since_last_received < connection->idle_timeout) + { + local_deadline = connection->idle_timeout - time_since_last_received; + } + else + { + local_deadline = 0; + + /* close connection */ + close_connection_with_error(connection, "amqp:internal-error", "No frame received for the idle timeout", NULL); + } + } + + if (local_deadline != 0 && connection->remote_idle_timeout != 0) + { + /* Calculate time until remote idle timeout expires */ + + uint64_t remote_idle_timeout = connection->remote_idle_timeout_send_frame_millisecond; + uint64_t time_since_last_sent = current_ms - connection->last_frame_sent_time; + + if (time_since_last_sent < remote_idle_timeout) + { + remote_deadline = remote_idle_timeout - time_since_last_sent; + } + else + { + connection->on_send_complete = NULL; + if (amqp_frame_codec_encode_empty_frame(connection->amqp_frame_codec, 0, on_bytes_encoded, connection) != 0) + { + LogError("Encoding the empty frame failed"); + /* close connection */ + close_connection_with_error(connection, "amqp:internal-error", "Cannot send empty frame", NULL); + } + else + { + if (connection->is_trace_on == 1) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "-> Empty frame"); + } + + connection->last_frame_sent_time = current_ms; + + remote_deadline = remote_idle_timeout; + } + } + } + } + } + + /* Return the shorter of each deadline, or 0 to indicate connection closed */ + return local_deadline > remote_deadline ? remote_deadline : local_deadline; +} + +void connection_dowork(CONNECTION_HANDLE connection) +{ + /* Codes_S_R_S_CONNECTION_01_078: [If handle is NULL, connection_dowork shall do nothing.] */ + if (connection == NULL) + { + LogError("NULL connection"); + } + else + { + if (connection_handle_deadlines(connection) > 0) + { + /* Codes_S_R_S_CONNECTION_01_076: [connection_dowork shall schedule the underlying IO interface to do its work by calling xio_dowork.] */ + xio_dowork(connection->io); + } + } +} + +ENDPOINT_HANDLE connection_create_endpoint(CONNECTION_HANDLE connection) +{ + ENDPOINT_HANDLE result; + + /* Codes_S_R_S_CONNECTION_01_113: [If connection, on_endpoint_frame_received or on_connection_state_changed is NULL, connection_create_endpoint shall fail and return NULL.] */ + /* Codes_S_R_S_CONNECTION_01_193: [The context argument shall be allowed to be NULL.] */ + if (connection == NULL) + { + LogError("NULL connection"); + result = NULL; + } + else + { + /* Codes_S_R_S_CONNECTION_01_115: [If no more endpoints can be created due to all channels being used, connection_create_endpoint shall fail and return NULL.] */ + if (connection->endpoint_count >= connection->channel_max) + { + result = NULL; + } + else + { + uint32_t i = 0; + + /* Codes_S_R_S_CONNECTION_01_128: [The lowest number outgoing channel shall be associated with the newly created endpoint.] */ + for (i = 0; i < connection->endpoint_count; i++) + { + if (connection->endpoints[i]->outgoing_channel > i) + { + /* found a gap in the sorted endpoint array */ + break; + } + } + + /* Codes_S_R_S_CONNECTION_01_127: [On success, connection_create_endpoint shall return a non-NULL handle to the newly created endpoint.] */ + result = (ENDPOINT_HANDLE)malloc(sizeof(ENDPOINT_INSTANCE)); + /* Codes_S_R_S_CONNECTION_01_196: [If memory cannot be allocated for the new endpoint, connection_create_endpoint shall fail and return NULL.] */ + if (result == NULL) + { + LogError("Cannot allocate memory for endpoint"); + } + else + { + ENDPOINT_HANDLE* new_endpoints; + + result->on_endpoint_frame_received = NULL; + result->on_connection_state_changed = NULL; + result->callback_context = NULL; + result->outgoing_channel = (uint16_t)i; + result->connection = connection; + + /* Codes_S_R_S_CONNECTION_01_197: [The newly created endpoint shall be added to the endpoints list, so that it can be tracked.] */ + new_endpoints = (ENDPOINT_HANDLE*)realloc(connection->endpoints, sizeof(ENDPOINT_HANDLE) * (connection->endpoint_count + 1)); + if (new_endpoints == NULL) + { + /* Tests_S_R_S_CONNECTION_01_198: [If adding the endpoint to the endpoints list tracked by the connection fails, connection_create_endpoint shall fail and return NULL.] */ + LogError("Cannot reallocate memory for connection endpoints"); + free(result); + result = NULL; + } + else + { + connection->endpoints = new_endpoints; + + if (i < connection->endpoint_count) + { + (void)memmove(&connection->endpoints[i + 1], &connection->endpoints[i], sizeof(ENDPOINT_INSTANCE*) * (connection->endpoint_count - i)); + } + + connection->endpoints[i] = result; + connection->endpoint_count++; + + /* Codes_S_R_S_CONNECTION_01_112: [connection_create_endpoint shall create a new endpoint that can be used by a session.] */ + } + } + } + } + + return result; +} + +int connection_start_endpoint(ENDPOINT_HANDLE endpoint, ON_ENDPOINT_FRAME_RECEIVED on_endpoint_frame_received, ON_CONNECTION_STATE_CHANGED on_connection_state_changed, void* context) +{ + int result; + + if ((endpoint == NULL) || + (on_endpoint_frame_received == NULL) || + (on_connection_state_changed == NULL)) + { + LogError("Bad arguments: endpoint = %p, on_endpoint_frame_received = %p, on_connection_state_changed = %p", + endpoint, on_endpoint_frame_received, on_connection_state_changed); + result = __FAILURE__; + } + else + { + endpoint->on_endpoint_frame_received = on_endpoint_frame_received; + endpoint->on_connection_state_changed = on_connection_state_changed; + endpoint->callback_context = context; + + result = 0; + } + + return result; +} + +int connection_endpoint_get_incoming_channel(ENDPOINT_HANDLE endpoint, uint16_t* incoming_channel) +{ + int result; + + if ((endpoint == NULL) || + (incoming_channel == NULL)) + { + LogError("Bad arguments: endpoint = %p, incoming_channel = %p", + endpoint, incoming_channel); + result = __FAILURE__; + } + else + { + *incoming_channel = endpoint->incoming_channel; + result = 0; + } + + return result; +} + +/* Codes_S_R_S_CONNECTION_01_129: [connection_destroy_endpoint shall free all resources associated with an endpoint created by connection_create_endpoint.] */ +void connection_destroy_endpoint(ENDPOINT_HANDLE endpoint) +{ + if (endpoint == NULL) + { + LogError("NULL endpoint"); + } + else + { + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)endpoint->connection; + size_t i; + + for (i = 0; i < connection->endpoint_count; i++) + { + if (connection->endpoints[i] == endpoint) + { + break; + } + } + + /* Codes_S_R_S_CONNECTION_01_130: [The outgoing channel associated with the endpoint shall be released by removing the endpoint from the endpoint list.] */ + /* Codes_S_R_S_CONNECTION_01_131: [Any incoming channel number associated with the endpoint shall be released.] */ + if (i < connection->endpoint_count) + { + // endpoint found + if (connection->endpoint_count == 1) + { + free(connection->endpoints); + connection->endpoints = NULL; + connection->endpoint_count = 0; + } + else + { + ENDPOINT_HANDLE* new_endpoints; + + if ((connection->endpoint_count - i - 1) > 0) + { + (void)memmove(connection->endpoints + i, connection->endpoints + i + 1, sizeof(ENDPOINT_HANDLE) * (connection->endpoint_count - i - 1)); + } + + new_endpoints = (ENDPOINT_HANDLE*)realloc(connection->endpoints, (connection->endpoint_count - 1) * sizeof(ENDPOINT_HANDLE)); + if (new_endpoints != NULL) + { + connection->endpoints = new_endpoints; + } + + connection->endpoint_count--; + } + } + + free(endpoint); + } +} + +/* Codes_S_R_S_CONNECTION_01_247: [connection_encode_frame shall send a frame for a certain endpoint.] */ +int connection_encode_frame(ENDPOINT_HANDLE endpoint, AMQP_VALUE performative, PAYLOAD* payloads, size_t payload_count, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_S_R_S_CONNECTION_01_249: [If endpoint or performative are NULL, connection_encode_frame shall fail and return a non-zero value.] */ + if ((endpoint == NULL) || + (performative == NULL)) + { + LogError("Bad arguments: endpoint = %p, performative = %p", + endpoint, performative); + result = __FAILURE__; + } + else + { + CONNECTION_HANDLE connection = (CONNECTION_HANDLE)endpoint->connection; + AMQP_FRAME_CODEC_HANDLE amqp_frame_codec = connection->amqp_frame_codec; + + /* Codes_S_R_S_CONNECTION_01_254: [If connection_encode_frame is called before the connection is in the OPENED state, connection_encode_frame shall fail and return a non-zero value.] */ + if (connection->connection_state != CONNECTION_STATE_OPENED) + { + LogError("Connection not open"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_255: [The payload size shall be computed based on all the payload chunks passed as argument in payloads.] */ + /* Codes_S_R_S_CONNECTION_01_250: [connection_encode_frame shall initiate the frame send by calling amqp_frame_codec_begin_encode_frame.] */ + /* Codes_S_R_S_CONNECTION_01_251: [The channel number passed to amqp_frame_codec_begin_encode_frame shall be the outgoing channel number associated with the endpoint by connection_create_endpoint.] */ + /* Codes_S_R_S_CONNECTION_01_252: [The performative passed to amqp_frame_codec_begin_encode_frame shall be the performative argument of connection_encode_frame.] */ + connection->on_send_complete = on_send_complete; + connection->on_send_complete_callback_context = callback_context; + if (amqp_frame_codec_encode_frame(amqp_frame_codec, endpoint->outgoing_channel, performative, payloads, payload_count, on_bytes_encoded, connection) != 0) + { + /* Codes_S_R_S_CONNECTION_01_253: [If amqp_frame_codec_begin_encode_frame or amqp_frame_codec_encode_payload_bytes fails, then connection_encode_frame shall fail and return a non-zero value.] */ + LogError("Encoding AMQP frame failed"); + result = __FAILURE__; + } + else + { + if (connection->is_trace_on == 1) + { + log_outgoing_frame(performative); + } + + if (tickcounter_get_current_ms(connection->tick_counter, &connection->last_frame_sent_time) != 0) + { + LogError("Getting tick counter value failed"); + result = __FAILURE__; + } + else + { + /* Codes_S_R_S_CONNECTION_01_248: [On success it shall return 0.] */ + result = 0; + } + } + } + } + + return result; +} + +void connection_set_trace(CONNECTION_HANDLE connection, bool trace_on) +{ + /* Codes_S_R_S_CONNECTION_07_002: [If connection is NULL then connection_set_trace shall do nothing.] */ + if (connection == NULL) + { + LogError("NULL connection"); + } + else + { + /* Codes_S_R_S_CONNECTION_07_001: [connection_set_trace shall set the ability to turn on and off trace logging.] */ + connection->is_trace_on = trace_on ? 1 : 0; + } +} + +int connection_set_remote_idle_timeout_empty_frame_send_ratio(CONNECTION_HANDLE connection, double idle_timeout_empty_frame_send_ratio) +{ + int result; + + if ((connection == NULL) || + (idle_timeout_empty_frame_send_ratio <= 0.0) || + (idle_timeout_empty_frame_send_ratio > 1.0)) + { + LogError("Bad arguments: connection = %p, idle_timeout_empty_frame_send_ratio = %f", + connection, idle_timeout_empty_frame_send_ratio); + result = __FAILURE__; + } + else + { + connection->idle_timeout_empty_frame_send_ratio = idle_timeout_empty_frame_send_ratio; + result = 0; + } + + return result; +} + +ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE connection_subscribe_on_connection_close_received(CONNECTION_HANDLE connection, ON_CONNECTION_CLOSE_RECEIVED on_connection_close_received, void* context) +{ + ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE result; + + /* Codes_S_R_S_CONNECTION_01_279: [ `context` shall be allowed to be NULL. ]*/ + + /* Codes_S_R_S_CONNECTION_01_277: [ If `connection` is NULL, `connection_subscribe_on_connection_close_received` shall fail and return NULL. ]*/ + if ((connection == NULL) || + /* Codes_S_R_S_CONNECTION_01_278: [ If `on_connection_close_received` is NULL, `connection_subscribe_on_connection_close_received` shall fail and return NULL. ]*/ + (on_connection_close_received == NULL)) + { + LogError("Invalid arguments: connection = %p, on_connection_close_received = %p, context = %p", + connection, on_connection_close_received, context); + result = NULL; + } + else + { + if (connection->on_connection_close_received_event_subscription.on_connection_close_received != NULL) + { + /* Codes_S_R_S_CONNECTION_01_280: [ Only one subscription shall be allowed per connection, if a subsequent second even subscription is done while a subscription is active, `connection_subscribe_on_connection_close_received` shall fail and return NULL. ]*/ + LogError("Already subscribed for on_connection_close_received events"); + result = NULL; + } + else + { + /* Codes_S_R_S_CONNECTION_01_275: [ `connection_subscribe_on_connection_close_received` shall register the `on_connection_close_received` handler to be triggered whenever a CLOSE performative is received.. ]*/ + connection->on_connection_close_received_event_subscription.on_connection_close_received = on_connection_close_received; + connection->on_connection_close_received_event_subscription.context = context; + + /* Codes_S_R_S_CONNECTION_01_276: [ On success, `connection_subscribe_on_connection_close_received` shall return a non-NULL handle to the event subcription. ]*/ + result = &connection->on_connection_close_received_event_subscription; + } + } + + return result; +} + +void connection_unsubscribe_on_connection_close_received(ON_CONNECTION_CLOSED_EVENT_SUBSCRIPTION_HANDLE event_subscription) +{ + if (event_subscription == NULL) + { + LogError("NULL event_subscription"); + } + else + { + /* Codes_S_R_S_CONNECTION_01_281: [ `connection_unsubscribe_on_connection_close_received` shall remove the subscription for the connection closed event that was made by calling `connection_subscribe_on_connection_close_received`. ]*/ + event_subscription->on_connection_close_received = NULL; + event_subscription->context = NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/frame_codec.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,694 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <inttypes.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/amqpvalue.h" + +#define FRAME_HEADER_SIZE 8 +#define MAX_TYPE_SPECIFIC_SIZE ((255 * 4) - 6) + +typedef enum RECEIVE_FRAME_STATE_TAG +{ + RECEIVE_FRAME_STATE_FRAME_SIZE, + RECEIVE_FRAME_STATE_DOFF, + RECEIVE_FRAME_STATE_FRAME_TYPE, + RECEIVE_FRAME_STATE_TYPE_SPECIFIC, + RECEIVE_FRAME_STATE_FRAME_BODY, + RECEIVE_FRAME_STATE_ERROR +} RECEIVE_FRAME_STATE; + +typedef struct SUBSCRIPTION_TAG +{ + uint8_t frame_type; + ON_FRAME_RECEIVED on_frame_received; + void* callback_context; +} SUBSCRIPTION; + +typedef struct FRAME_CODEC_INSTANCE_TAG +{ + /* subscriptions */ + SINGLYLINKEDLIST_HANDLE subscription_list; + + /* decode frame */ + RECEIVE_FRAME_STATE receive_frame_state; + size_t receive_frame_pos; + uint32_t receive_frame_size; + uint32_t type_specific_size; + uint8_t receive_frame_doff; + uint8_t receive_frame_type; + SUBSCRIPTION* receive_frame_subscription; + unsigned char* receive_frame_bytes; + ON_FRAME_CODEC_ERROR on_frame_codec_error; + void* on_frame_codec_error_callback_context; + + /* configuration */ + uint32_t max_frame_size; +} FRAME_CODEC_INSTANCE; + +static bool find_subscription_by_frame_type(LIST_ITEM_HANDLE list_item, const void* match_context) +{ + bool result; + SUBSCRIPTION* subscription = (SUBSCRIPTION*)singlylinkedlist_item_get_value(list_item); + + if (subscription == NULL) + { + LogError("Could not get subscription information from the list item"); + result = false; + } + else + { + result = subscription->frame_type == *((uint8_t*)match_context) ? true : false; + } + + return result; +} + +FRAME_CODEC_HANDLE frame_codec_create(ON_FRAME_CODEC_ERROR on_frame_codec_error, void* callback_context) +{ + FRAME_CODEC_INSTANCE* result; + + /* Codes_SRS_FRAME_CODEC_01_020: [If the on_frame_codec_error argument is NULL, frame_codec_create shall return NULL.] */ + /* Codes_SRS_FRAME_CODEC_01_104: [The callback_context shall be allowed to be NULL.] */ + if (on_frame_codec_error == NULL) + { + LogError("NULL on_frame_codec_error"); + result = NULL; + } + else + { + result = (FRAME_CODEC_INSTANCE*)malloc(sizeof(FRAME_CODEC_INSTANCE)); + /* Codes_SRS_FRAME_CODEC_01_022: [If allocating memory for the frame_codec instance fails, frame_codec_create shall return NULL.] */ + if (result == NULL) + { + LogError("Could not allocate frame codec"); + } + else + { + /* Codes_SRS_FRAME_CODEC_01_021: [frame_codec_create shall create a new instance of frame_codec and return a non-NULL handle to it on success.] */ + result->receive_frame_state = RECEIVE_FRAME_STATE_FRAME_SIZE; + result->on_frame_codec_error = on_frame_codec_error; + result->on_frame_codec_error_callback_context = callback_context; + result->receive_frame_pos = 0; + result->receive_frame_size = 0; + result->receive_frame_bytes = NULL; + result->subscription_list = singlylinkedlist_create(); + + /* Codes_SRS_FRAME_CODEC_01_082: [The initial max_frame_size_shall be 512.] */ + result->max_frame_size = 512; + } + } + + return result; +} + +void frame_codec_destroy(FRAME_CODEC_HANDLE frame_codec) +{ + /* Codes_SRS_FRAME_CODEC_01_024: [If frame_codec is NULL, frame_codec_destroy shall do nothing.] */ + if (frame_codec == NULL) + { + LogError("NULL frame_codec"); + } + else + { + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + + singlylinkedlist_destroy(frame_codec_data->subscription_list); + if (frame_codec_data->receive_frame_bytes != NULL) + { + free(frame_codec_data->receive_frame_bytes); + } + + /* Codes_SRS_FRAME_CODEC_01_023: [frame_codec_destroy shall free all resources associated with a frame_codec instance.] */ + free(frame_codec); + } +} + +int frame_codec_set_max_frame_size(FRAME_CODEC_HANDLE frame_codec, uint32_t max_frame_size) +{ + int result; + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + + /* Codes_SRS_FRAME_CODEC_01_077: [If frame_codec is NULL, frame_codec_set_max_frame_size shall return a non-zero value.] */ + if ((frame_codec == NULL) || + /* Codes_SRS_FRAME_CODEC_01_078: [If max_frame_size is invalid according to the AMQP standard, frame_codec_set_max_frame_size shall return a non-zero value.] */ + (max_frame_size < FRAME_HEADER_SIZE) || + /* Codes_SRS_FRAME_CODEC_01_081: [If a frame being decoded already has a size bigger than the max_frame_size argument then frame_codec_set_max_frame_size shall return a non-zero value and the previous frame size shall be kept.] */ + ((max_frame_size < frame_codec_data->receive_frame_size) && (frame_codec_data->receive_frame_state != RECEIVE_FRAME_STATE_FRAME_SIZE))) + { + LogError("Bad arguments: frame_codec = %p, max_frame_size = %" PRIu32, + frame_codec, + max_frame_size); + result = __FAILURE__; + } + /* Codes_SRS_FRAME_CODEC_01_097: [Setting a frame size on a frame_codec that had a decode error shall fail.] */ + else if (frame_codec_data->receive_frame_state == RECEIVE_FRAME_STATE_ERROR) + { + LogError("Frame codec in error state"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_075: [frame_codec_set_max_frame_size shall set the maximum frame size for a frame_codec.] */ + /* Codes_SRS_FRAME_CODEC_01_079: [The new frame size shall take effect immediately, even for a frame that is being decoded at the time of the call.] */ + frame_codec_data->max_frame_size = max_frame_size; + + /* Codes_SRS_FRAME_CODEC_01_076: [On success, frame_codec_set_max_frame_size shall return 0.] */ + result = 0; + } + + return result; +} + +/* Codes_SRS_FRAME_CODEC_01_001: [Frames are divided into three distinct areas: a fixed width frame header, a variable width extended header, and a variable width frame body.] */ +/* Codes_SRS_FRAME_CODEC_01_002: [frame header The frame header is a fixed size (8 byte) structure that precedes each frame.] */ +/* Codes_SRS_FRAME_CODEC_01_003: [The frame header includes mandatory information necessary to parse the rest of the frame including size and type information.] */ +/* Codes_SRS_FRAME_CODEC_01_004: [extended header The extended header is a variable width area preceding the frame body.] */ +/* Codes_SRS_FRAME_CODEC_01_007: [frame body The frame body is a variable width sequence of bytes the format of which depends on the frame type.] */ +/* Codes_SRS_FRAME_CODEC_01_028: [The sequence of bytes shall be decoded according to the AMQP ISO.] */ +/* Codes_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */ +int frame_codec_receive_bytes(FRAME_CODEC_HANDLE frame_codec, const unsigned char* buffer, size_t size) +{ + int result = __FAILURE__; + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + + /* Codes_SRS_FRAME_CODEC_01_026: [If frame_codec or buffer are NULL, frame_codec_receive_bytes shall return a non-zero value.] */ + if ((frame_codec == NULL) || + (buffer == NULL) || + /* Codes_SRS_FRAME_CODEC_01_027: [If size is zero, frame_codec_receive_bytes shall return a non-zero value.] */ + (size == 0)) + { + LogError("Bad arguments: frame_codec = %p, buffer = %p, size = %u", + frame_codec, + buffer, + (unsigned int)size); + result = __FAILURE__; + } + else + { + while (size > 0) + { + switch (frame_codec_data->receive_frame_state) + { + default: + case RECEIVE_FRAME_STATE_ERROR: + /* Codes_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_data_receive_bytes shall fail.] */ + LogError("Frame codec is in error state"); + result = __FAILURE__; + size = 0; + break; + + /* Codes_SRS_FRAME_CODEC_01_008: [SIZE Bytes 0-3 of the frame header contain the frame size.] */ + case RECEIVE_FRAME_STATE_FRAME_SIZE: + /* Codes_SRS_FRAME_CODEC_01_009: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */ + frame_codec_data->receive_frame_size += buffer[0] << (24 - frame_codec_data->receive_frame_pos * 8); + buffer++; + size--; + frame_codec_data->receive_frame_pos++; + + if (frame_codec_data->receive_frame_pos == 4) + { + /* Codes_SRS_FRAME_CODEC_01_010: [The frame is malformed if the size is less than the size of the frame header (8 bytes).] */ + if ((frame_codec_data->receive_frame_size < FRAME_HEADER_SIZE) || + /* Codes_SRS_FRAME_CODEC_01_096: [If a frame bigger than the current max frame size is received, frame_codec_receive_bytes shall fail and return a non-zero value.] */ + (frame_codec_data->receive_frame_size > frame_codec_data->max_frame_size)) + { + /* Codes_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_data_receive_bytes shall fail.] */ + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_ERROR; + /* Codes_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the on_frame_codec_error_callback_context argument passed to frame_codec_create.] */ + frame_codec_data->on_frame_codec_error(frame_codec_data->on_frame_codec_error_callback_context); + LogError("Received frame size is too big"); + result = __FAILURE__; + } + else + { + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_DOFF; + result = 0; + } + } + else + { + result = 0; + } + + break; + + case RECEIVE_FRAME_STATE_DOFF: + /* Codes_SRS_FRAME_CODEC_01_011: [DOFF Byte 4 of the frame header is the data offset.] */ + /* Codes_SRS_FRAME_CODEC_01_013: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */ + /* Codes_SRS_FRAME_CODEC_01_012: [This gives the position of the body within the frame.] */ + frame_codec_data->receive_frame_doff = buffer[0]; + buffer++; + size--; + + /* Codes_SRS_FRAME_CODEC_01_014: [Due to the mandatory 8-byte frame header, the frame is malformed if the value is less than 2.] */ + if (frame_codec_data->receive_frame_doff < 2) + { + /* Codes_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_data_receive_bytes shall fail.] */ + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_ERROR; + + /* Codes_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the on_frame_codec_error_callback_context argument passed to frame_codec_create.] */ + frame_codec_data->on_frame_codec_error(frame_codec_data->on_frame_codec_error_callback_context); + + LogError("Malformed frame received"); + result = __FAILURE__; + } + else + { + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_FRAME_TYPE; + result = 0; + } + + break; + + case RECEIVE_FRAME_STATE_FRAME_TYPE: + { + LIST_ITEM_HANDLE item_handle; + frame_codec_data->type_specific_size = (frame_codec_data->receive_frame_doff * 4) - 6; + + /* Codes_SRS_FRAME_CODEC_01_015: [TYPE Byte 5 of the frame header is a type code.] */ + frame_codec_data->receive_frame_type = buffer[0]; + buffer++; + size--; + + /* Codes_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value.] */ + item_handle = singlylinkedlist_find(frame_codec_data->subscription_list, find_subscription_by_frame_type, &frame_codec_data->receive_frame_type); + if (item_handle == NULL) + { + frame_codec_data->receive_frame_subscription = NULL; + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_TYPE_SPECIFIC; + result = 0; + break; + } + else + { + frame_codec_data->receive_frame_subscription = (SUBSCRIPTION*)singlylinkedlist_item_get_value(item_handle); + if (frame_codec_data->receive_frame_subscription == NULL) + { + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_TYPE_SPECIFIC; + result = 0; + break; + } + else + { + frame_codec_data->receive_frame_pos = 0; + + /* Codes_SRS_FRAME_CODEC_01_102: [frame_codec_receive_bytes shall allocate memory to hold the frame_body bytes.] */ + frame_codec_data->receive_frame_bytes = (unsigned char*)malloc(frame_codec_data->receive_frame_size - 6); + if (frame_codec_data->receive_frame_bytes == NULL) + { + /* Codes_SRS_FRAME_CODEC_01_101: [If the memory for the frame_body bytes cannot be allocated, frame_codec_receive_bytes shall fail and return a non-zero value.] */ + /* Codes_SRS_FRAME_CODEC_01_030: [If a decoding error occurs, frame_codec_data_receive_bytes shall return a non-zero value.] */ + /* Codes_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_data_receive_bytes shall fail.] */ + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_ERROR; + + /* Codes_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the on_frame_codec_error_callback_context argument passed to frame_codec_create.] */ + frame_codec_data->on_frame_codec_error(frame_codec_data->on_frame_codec_error_callback_context); + + LogError("Cannot allocate memort for frame bytes"); + result = __FAILURE__; + break; + } + else + { + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_TYPE_SPECIFIC; + result = 0; + break; + } + } + } + } + + case RECEIVE_FRAME_STATE_TYPE_SPECIFIC: + { + size_t to_copy = frame_codec_data->type_specific_size - frame_codec_data->receive_frame_pos; + if (to_copy > size) + { + to_copy = size; + } + + if (frame_codec_data->receive_frame_subscription != NULL) + { + (void)memcpy(&frame_codec_data->receive_frame_bytes[frame_codec_data->receive_frame_pos], buffer, to_copy); + frame_codec_data->receive_frame_pos += to_copy; + buffer += to_copy; + size -= to_copy; + } + else + { + frame_codec_data->receive_frame_pos += to_copy; + buffer += to_copy; + size -= to_copy; + } + + if (frame_codec_data->receive_frame_pos == frame_codec_data->type_specific_size) + { + if (frame_codec_data->receive_frame_size == FRAME_HEADER_SIZE) + { + if (frame_codec_data->receive_frame_subscription != NULL) + { + /* Codes_SRS_FRAME_CODEC_01_031: [When a complete frame is successfully decoded it shall be indicated to the upper layer by invoking the on_frame_received passed to frame_codec_subscribe.] */ + /* Codes_SRS_FRAME_CODEC_01_032: [Besides passing the frame information, the callback_context value passed to frame_codec_data_subscribe shall be passed to the on_frame_received function.] */ + /* Codes_SRS_FRAME_CODEC_01_005: [This is an extension point defined for future expansion.] */ + /* Codes_SRS_FRAME_CODEC_01_006: [The treatment of this area depends on the frame type.] */ + /* Codes_SRS_FRAME_CODEC_01_100: [If the frame body size is 0, the frame_body pointer passed to on_frame_received shall be NULL.] */ + frame_codec_data->receive_frame_subscription->on_frame_received(frame_codec_data->receive_frame_subscription->callback_context, frame_codec_data->receive_frame_bytes, frame_codec_data->type_specific_size, NULL, 0); + free(frame_codec_data->receive_frame_bytes); + frame_codec_data->receive_frame_bytes = NULL; + } + + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_FRAME_SIZE; + frame_codec_data->receive_frame_size = 0; + } + else + { + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_FRAME_BODY; + } + + frame_codec_data->receive_frame_pos = 0; + } + + result = 0; + break; + } + + case RECEIVE_FRAME_STATE_FRAME_BODY: + { + uint32_t frame_body_size = frame_codec_data->receive_frame_size - (frame_codec_data->receive_frame_doff * 4); + size_t to_copy = frame_body_size - frame_codec_data->receive_frame_pos; + + if (to_copy > size) + { + to_copy = size; + } + + (void)memcpy(frame_codec_data->receive_frame_bytes + frame_codec_data->receive_frame_pos + frame_codec_data->type_specific_size, buffer, to_copy); + + buffer += to_copy; + size -= to_copy; + frame_codec_data->receive_frame_pos += to_copy; + + if (frame_codec_data->receive_frame_pos == frame_body_size) + { + if (frame_codec_data->receive_frame_subscription != NULL) + { + /* Codes_SRS_FRAME_CODEC_01_031: [When a complete frame is successfully decoded it shall be indicated to the upper layer by invoking the on_frame_received passed to frame_codec_subscribe.] */ + /* Codes_SRS_FRAME_CODEC_01_032: [Besides passing the frame information, the callback_context value passed to frame_codec_data_subscribe shall be passed to the on_frame_received function.] */ + /* Codes_SRS_FRAME_CODEC_01_005: [This is an extension point defined for future expansion.] */ + /* Codes_SRS_FRAME_CODEC_01_006: [The treatment of this area depends on the frame type.] */ + /* Codes_SRS_FRAME_CODEC_01_099: [A pointer to the frame_body bytes shall also be passed to the on_frame_received.] */ + frame_codec_data->receive_frame_subscription->on_frame_received(frame_codec_data->receive_frame_subscription->callback_context, frame_codec_data->receive_frame_bytes, frame_codec_data->type_specific_size, frame_codec_data->receive_frame_bytes + frame_codec_data->type_specific_size, frame_body_size); + free(frame_codec_data->receive_frame_bytes); + frame_codec_data->receive_frame_bytes = NULL; + } + + frame_codec_data->receive_frame_state = RECEIVE_FRAME_STATE_FRAME_SIZE; + frame_codec_data->receive_frame_pos = 0; + frame_codec_data->receive_frame_size = 0; + } + result = 0; + + break; + } + } + } + } + + return result; +} + +/* Codes_SRS_FRAME_CODEC_01_033: [frame_codec_subscribe subscribes for a certain type of frame received by the frame_codec instance identified by frame_codec.] */ +int frame_codec_subscribe(FRAME_CODEC_HANDLE frame_codec, uint8_t type, ON_FRAME_RECEIVED on_frame_received, void* callback_context) +{ + int result; + + /* Codes_SRS_FRAME_CODEC_01_034: [If any of the frame_codec or on_frame_received arguments is NULL, frame_codec_subscribe shall return a non-zero value.] */ + if ((frame_codec == NULL) || + (on_frame_received == NULL)) + { + LogError("Bad arguments: frame_codec = %p, on_frame_received = %p", + frame_codec, on_frame_received); + result = __FAILURE__; + } + else + { + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + SUBSCRIPTION* subscription; + + /* Codes_SRS_FRAME_CODEC_01_036: [Only one callback pair shall be allowed to be registered for a given frame type.] */ + /* find the subscription for this frame type */ + LIST_ITEM_HANDLE list_item = singlylinkedlist_find(frame_codec_data->subscription_list, find_subscription_by_frame_type, &type); + if (list_item != NULL) + { + subscription = (SUBSCRIPTION*)singlylinkedlist_item_get_value(list_item); + if (subscription == NULL) + { + /* Codes_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */ + LogError("Cannot retrieve subscription information from the list for type %u", (unsigned int)type); + result = __FAILURE__; + } + else + { + /* a subscription was found */ + subscription->on_frame_received = on_frame_received; + subscription->callback_context = callback_context; + + /* Codes_SRS_FRAME_CODEC_01_087: [On success, frame_codec_subscribe shall return zero.] */ + result = 0; + } + } + else + { + /* add a new subscription */ + subscription = (SUBSCRIPTION*)malloc(sizeof(SUBSCRIPTION)); + /* Codes_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */ + if (subscription == NULL) + { + LogError("Cannot allocate memory for new subscription"); + result = __FAILURE__; + } + else + { + subscription->on_frame_received = on_frame_received; + subscription->callback_context = callback_context; + subscription->frame_type = type; + + /* Codes_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */ + if (singlylinkedlist_add(frame_codec_data->subscription_list, subscription) == NULL) + { + free(subscription); + LogError("Cannot add subscription to list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_087: [On success, frame_codec_subscribe shall return zero.] */ + result = 0; + } + } + } + } + + return result; +} + +int frame_codec_unsubscribe(FRAME_CODEC_HANDLE frame_codec, uint8_t type) +{ + int result; + + /* Codes_SRS_FRAME_CODEC_01_039: [If frame_codec is NULL, frame_codec_unsubscribe shall return a non-zero value.] */ + if (frame_codec == NULL) + { + LogError("NULL frame_codec"); + result = __FAILURE__; + } + else + { + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + LIST_ITEM_HANDLE list_item = singlylinkedlist_find(frame_codec_data->subscription_list, find_subscription_by_frame_type, &type); + + if (list_item == NULL) + { + /* Codes_SRS_FRAME_CODEC_01_040: [If no subscription for the type frame type exists, frame_codec_unsubscribe shall return a non-zero value.] */ + /* Codes_SRS_FRAME_CODEC_01_041: [If any failure occurs while performing the unsubscribe operation, frame_codec_unsubscribe shall return a non-zero value.] */ + LogError("Cannot find subscription for type %u", (unsigned int)type); + result = __FAILURE__; + } + else + { + SUBSCRIPTION* subscription = (SUBSCRIPTION*)singlylinkedlist_item_get_value(list_item); + if (subscription == NULL) + { + /* Codes_SRS_FRAME_CODEC_01_041: [If any failure occurs while performing the unsubscribe operation, frame_codec_unsubscribe shall return a non-zero value.] */ + LogError("singlylinkedlist_item_get_value failed when unsubscribing"); + result = __FAILURE__; + } + else + { + free(subscription); + if (singlylinkedlist_remove(frame_codec_data->subscription_list, list_item) != 0) + { + /* Codes_SRS_FRAME_CODEC_01_041: [If any failure occurs while performing the unsubscribe operation, frame_codec_unsubscribe shall return a non-zero value.] */ + LogError("Cannot remove subscription from list"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */ + result = 0; + } + } + } + } + + return result; +} + +int frame_codec_encode_frame(FRAME_CODEC_HANDLE frame_codec, uint8_t type, const PAYLOAD* payloads, size_t payload_count, const unsigned char* type_specific_bytes, uint32_t type_specific_size, ON_BYTES_ENCODED on_bytes_encoded, void* callback_context) +{ + int result; + + FRAME_CODEC_INSTANCE* frame_codec_data = (FRAME_CODEC_INSTANCE*)frame_codec; + + /* Codes_SRS_FRAME_CODEC_01_044: [If any of arguments `frame_codec` or `on_bytes_encoded` is NULL, `frame_codec_encode_frame` shall return a non-zero value.] */ + if ((frame_codec == NULL) || + (on_bytes_encoded == NULL) || + /* Codes_SRS_FRAME_CODEC_01_091: [If the argument type_specific_size is greater than 0 and type_specific_bytes is NULL, frame_codec_encode_frame shall return a non-zero value.] */ + ((type_specific_size > 0) && (type_specific_bytes == NULL)) || + /* Codes_SRS_FRAME_CODEC_01_092: [If type_specific_size is too big to allow encoding the frame according to the AMQP ISO then frame_codec_encode_frame shall return a non-zero value.] */ + (type_specific_size > MAX_TYPE_SPECIFIC_SIZE)) + { + LogError("Bad arguments: frame_codec = %p, on_bytes_encoded = %p, type_specific_size = %u, type_specific_bytes = %p", + frame_codec, on_bytes_encoded, (unsigned int)type_specific_size, type_specific_bytes); + result = __FAILURE__; + } + else if ((payloads == NULL) && (payload_count > 0)) + { + /* Codes_SRS_FRAME_CODEC_01_107: [If the argument `payloads` is NULL and `payload_count` is non-zero, `frame_codec_encode_frame` shall return a non-zero value.]*/ + LogError("NULL payloads argument with non-zero payload count"); + result = __FAILURE__; + } + else + { + /* round up to the 4 bytes for doff */ + /* Codes_SRS_FRAME_CODEC_01_067: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */ + /* Codes_SRS_FRAME_CODEC_01_068: [Due to the mandatory 8-byte frame header, the frame is malformed if the value is less than 2.] */ + uint8_t padding_byte_count; + uint32_t frame_body_offset = type_specific_size + 6; + uint8_t doff = (uint8_t)((frame_body_offset + 3) / 4); + size_t i; + size_t frame_size; + size_t frame_body_size = 0; + frame_body_offset = doff * 4; + padding_byte_count = (uint8_t)(frame_body_offset - type_specific_size - 6); + + for (i = 0; i < payload_count; i++) + { + /* Codes_SRS_FRAME_CODEC_01_110: [ If the `bytes` member of a payload entry is NULL, `frame_codec_encode_frame` shall fail and return a non-zero value. ] */ + if ((payloads[i].bytes == NULL) || + /* Codes_SRS_FRAME_CODEC_01_111: [ If the `length` member of a payload entry is 0, `frame_codec_encode_frame` shall fail and return a non-zero value. ] */ + (payloads[i].length == 0)) + { + break; + } + + frame_body_size += payloads[i].length; + } + + if (i < payload_count) + { + LogError("Bad payload entry"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_063: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */ + frame_size = frame_body_size + frame_body_offset; + + if (frame_size > frame_codec_data->max_frame_size) + { + /* Codes_SRS_FRAME_CODEC_01_095: [If the frame_size needed for the frame is bigger than the maximum frame size, frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Encoded frame size exceeds the maximum allowed frame size"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_108: [ Memory shall be allocated to hold the entire frame. ]*/ + unsigned char* encoded_frame = (unsigned char*)malloc(frame_size); + if (encoded_frame == NULL) + { + /* Codes_SRS_FRAME_CODEC_01_109: [ If allocating memory fails, `frame_codec_encode_frame` shall fail and return a non-zero value. ]*/ + LogError("Cannot allocate memory for frame"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_FRAME_CODEC_01_042: [frame_codec_encode_frame encodes the header, type specific bytes and frame payload of a frame that has frame_payload_size bytes.]*/ + /* Codes_SRS_FRAME_CODEC_01_055: [Frames are divided into three distinct areas: a fixed width frame header, a variable width extended header, and a variable width frame body.] */ + /* Codes_SRS_FRAME_CODEC_01_056: [frame header The frame header is a fixed size (8 byte) structure that precedes each frame.] */ + /* Codes_SRS_FRAME_CODEC_01_057: [The frame header includes mandatory information necessary to parse the rest of the frame including size and type information.] */ + /* Codes_SRS_FRAME_CODEC_01_058: [extended header The extended header is a variable width area preceding the frame body.] */ + /* Codes_SRS_FRAME_CODEC_01_059: [This is an extension point defined for future expansion.] */ + /* Codes_SRS_FRAME_CODEC_01_060: [The treatment of this area depends on the frame type.]*/ + /* Codes_SRS_FRAME_CODEC_01_062: [SIZE Bytes 0-3 of the frame header contain the frame size.] */ + /* Codes_SRS_FRAME_CODEC_01_063: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */ + /* Codes_SRS_FRAME_CODEC_01_064: [The frame is malformed if the size is less than the size of the frame header (8 bytes).] */ + unsigned char frame_header[6]; + size_t current_pos = 0; + /* Codes_SRS_FRAME_CODEC_01_090: [If the type_specific_size - 2 does not divide by 4, frame_codec_encode_frame shall pad the type_specific bytes with zeroes so that type specific data is according to the AMQP ISO.] */ + unsigned char padding_bytes[] = { 0x00, 0x00, 0x00 }; + + frame_header[0] = (frame_size >> 24) & 0xFF; + frame_header[1] = (frame_size >> 16) & 0xFF; + frame_header[2] = (frame_size >> 8) & 0xFF; + frame_header[3] = frame_size & 0xFF; + /* Codes_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */ + frame_header[4] = doff; + /* Codes_SRS_FRAME_CODEC_01_069: [TYPE Byte 5 of the frame header is a type code.] */ + frame_header[5] = type; + + (void)memcpy(encoded_frame, frame_header, sizeof(frame_header)); + current_pos += sizeof(frame_header); + + if (type_specific_size > 0) + { + (void)memcpy(encoded_frame + current_pos, type_specific_bytes, type_specific_size); + current_pos += type_specific_size; + } + + /* send padding bytes */ + if (padding_byte_count > 0) + { + (void)memcpy(encoded_frame + current_pos, padding_bytes, padding_byte_count); + current_pos += padding_byte_count; + } + + /* Codes_SRS_FRAME_CODEC_01_106: [All payloads shall be encoded in order as part of the frame.] */ + for (i = 0; i < payload_count; i++) + { + (void)memcpy(encoded_frame + current_pos, payloads[i].bytes, payloads[i].length); + current_pos += payloads[i].length; + } + + /* Codes_SRS_FRAME_CODEC_01_088: [Encoded bytes shall be passed to the `on_bytes_encoded` callback in a single call, while setting the `encode complete` argument to true.] */ + on_bytes_encoded(callback_context, encoded_frame, frame_size, true); + + free(encoded_frame); + + /* Codes_SRS_FRAME_CODEC_01_043: [On success it shall return 0.] */ + result = 0; + } + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/header_detect_io.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,955 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdbool.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_uamqp_c/header_detect_io.h" +#include "azure_uamqp_c/server_protocol_io.h" + +static const unsigned char amqp_header_bytes[] = { 'A', 'M', 'Q', 'P', 0, 1, 0, 0 }; +static const unsigned char sasl_amqp_header_bytes[] = { 'A', 'M', 'Q', 'P', 3, 1, 0, 0 }; + +typedef enum IO_STATE_TAG +{ + IO_STATE_NOT_OPEN, + IO_STATE_OPENING_UNDERLYING_IO, + IO_STATE_WAIT_FOR_HEADER, + IO_STATE_OPENING_DETECTED_IO, + IO_STATE_OPEN, + IO_STATE_CLOSING, + IO_STATE_ERROR +} IO_STATE; + +typedef struct INTERNAL_HEADER_DETECT_ENTRY_TAG +{ + unsigned char* header_bytes; + size_t header_size; + const IO_INTERFACE_DESCRIPTION* io_interface_description; +} INTERNAL_HEADER_DETECT_ENTRY; + +typedef struct CHAINED_IO_TAG +{ + XIO_HANDLE detected_io; + ON_BYTES_RECEIVED on_bytes_received; + void* on_bytes_received_context; +} CHAINED_IO; + +typedef struct HEADER_DETECT_IO_INSTANCE_TAG +{ + XIO_HANDLE underlying_io; + IO_STATE io_state; + size_t header_pos; + ON_IO_OPEN_COMPLETE on_io_open_complete; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + ON_IO_ERROR on_io_error; + ON_BYTES_RECEIVED on_bytes_received; + void* on_io_open_complete_context; + void* on_io_close_complete_context; + void* on_io_error_context; + void* on_bytes_received_context; + INTERNAL_HEADER_DETECT_ENTRY* header_detect_entries; + size_t header_detect_entry_count; + SINGLYLINKEDLIST_HANDLE chained_io_list; + XIO_HANDLE* last_io; +} HEADER_DETECT_IO_INSTANCE; + +static void destroy_io_chain(HEADER_DETECT_IO_INSTANCE* header_detect_io) +{ + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(header_detect_io->chained_io_list); + while (list_item != NULL) + { + CHAINED_IO* chained_io = (CHAINED_IO*)singlylinkedlist_item_get_value(list_item); + + if (singlylinkedlist_remove(header_detect_io->chained_io_list, list_item) != 0) + { + LogError("Cannot remove detected IO from list"); + } + + xio_destroy(chained_io->detected_io); + free(chained_io); + + list_item = singlylinkedlist_get_head_item(header_detect_io->chained_io_list); + } + + header_detect_io->last_io = &header_detect_io->underlying_io; +} + +static void internal_close(HEADER_DETECT_IO_INSTANCE* header_detect_io) +{ + // close the last underlying IO (the one that we're talking to) + if (xio_close(*header_detect_io->last_io, NULL, NULL) != 0) + { + LogError("Cannot close underlying IO"); + } + + destroy_io_chain(header_detect_io); + + header_detect_io->io_state = IO_STATE_NOT_OPEN; +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result); +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size); +static void on_underlying_io_error(void* context); + +static void indicate_error(HEADER_DETECT_IO_INSTANCE* header_detect_io_instance) +{ + if (header_detect_io_instance->on_io_error != NULL) + { + header_detect_io_instance->on_io_error(header_detect_io_instance->on_io_error_context); + } +} + +static void indicate_open_complete(HEADER_DETECT_IO_INSTANCE* header_detect_io_instance, IO_OPEN_RESULT open_result) +{ + if (header_detect_io_instance->on_io_open_complete != NULL) + { + header_detect_io_instance->on_io_open_complete(header_detect_io_instance->on_io_open_complete_context, open_result); + } +} + +static void indicate_close_complete(HEADER_DETECT_IO_INSTANCE* header_detect_io_instance) +{ + if (header_detect_io_instance->on_io_close_complete != NULL) + { + header_detect_io_instance->on_io_close_complete(header_detect_io_instance->on_io_close_complete_context); + } +} + +static void on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + // able to send the header + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)context; + + if (send_result != IO_SEND_OK) + { + // signal error + indicate_error(header_detect_io_instance); + } +} + +// This callback usage needs to be either verified and commented or integrated into +// the state machine. +static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + (void)context; + (void)send_result; +} + +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + if (context == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_050: [ If `context` is NULL, `on_underlying_io_bytes_received` shall do nothing. ]*/ + LogError("NULL context"); + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)context; + + if ((buffer == NULL) || + (size == 0)) + { + switch (header_detect_io_instance->io_state) + { + default: + break; + + case IO_STATE_OPEN: + /* Codes_SRS_HEADER_DETECT_IO_01_051: [ If `buffer` is NULL or `size` is 0 while the IO is OPEN an error shall be indicated by calling `on_io_error`. ]*/ + indicate_error(header_detect_io_instance); + break; + } + } + else + { + while (size > 0) + { + switch (header_detect_io_instance->io_state) + { + default: + break; + + case IO_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_HEADER_DETECT_IO_01_049: [ When `on_underlying_io_bytes_received` is called while opening the underlying IO (before the underlying open complete is received), an error shall be indicated by calling `on_io_open_complete` with `IO_OPEN_ERROR`. ]*/ + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + size = 0; + break; + + case IO_STATE_OPENING_DETECTED_IO: + { + /* Codes_SRS_HEADER_DETECT_IO_01_087: [ If `on_underlying_io_bytes_received` is called while waiting for the detected IO to complete its open, the bytes shall be given to the last created IO by calling its `on_bytes_received` callback that was filled into the `on_bytes_received` member of `SERVER_PROTOCOL_IO_CONFIG`. ]*/ + CHAINED_IO* chained_io = (CHAINED_IO*)(((unsigned char*)header_detect_io_instance->last_io) - offsetof(CHAINED_IO, detected_io)); + (chained_io->on_bytes_received)(chained_io->on_bytes_received_context, buffer, size); + size = 0; + break; + } + + case IO_STATE_WAIT_FOR_HEADER: + { + size_t i; + bool has_one_match = false; + + /* check if any of the headers matches */ + for (i = 0; i < header_detect_io_instance->header_detect_entry_count; i++) + { + /* Codes_SRS_HEADER_DETECT_IO_01_067: [ When `on_underlying_io_bytes_received` is called while waiting for header bytes (after the underlying IO was open), the bytes shall be matched against the entries provided in the configuration passed to `header_detect_io_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_068: [ Header bytes shall be accepted in multiple `on_underlying_io_bytes_received` calls. ]*/ + if ((header_detect_io_instance->header_pos < header_detect_io_instance->header_detect_entries[i].header_size) && + (header_detect_io_instance->header_detect_entries[i].header_bytes[header_detect_io_instance->header_pos] == buffer[0])) + { + has_one_match = true; + + if (header_detect_io_instance->header_pos + 1 == header_detect_io_instance->header_detect_entries[i].header_size) + { + /* recognized one header */ + if (xio_send(*header_detect_io_instance->last_io, header_detect_io_instance->header_detect_entries[i].header_bytes, header_detect_io_instance->header_detect_entries[i].header_size, on_send_complete, header_detect_io_instance) != 0) + { + LogError("Failed sending header"); + header_detect_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + } + else + { + // wait for send complete and then start the detected IO open + if (header_detect_io_instance->header_detect_entries[i].io_interface_description == NULL) + { + header_detect_io_instance->io_state = IO_STATE_OPEN; + indicate_open_complete(header_detect_io_instance, IO_OPEN_OK); + } + else + { + SERVER_PROTOCOL_IO_CONFIG server_protocol_io_config; + CHAINED_IO* chained_io = (CHAINED_IO*)malloc(sizeof(CHAINED_IO)); + if (chained_io == NULL) + { + LogError("Cannot allocate memory for chained IO"); + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_076: [ If no detected IO was created then the underlying IO in the `SERVER_PROTOCOL_IO_CONFIG` structure shall be set to the `underlying_io` passed in the create arguments. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_075: [ The underlying IO in the `SERVER_PROTOCOL_IO_CONFIG` structure shall be set to the last detected IO that was created if any. ]*/ + server_protocol_io_config.underlying_io = *header_detect_io_instance->last_io; + server_protocol_io_config.on_bytes_received = &chained_io->on_bytes_received; + server_protocol_io_config.on_bytes_received_context = &chained_io->on_bytes_received_context; + + /* Codes_SRS_HEADER_DETECT_IO_01_069: [ If a header match was detected on an entry with a non-NULL io handle, a new IO associated shall be created by calling `xio_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_073: [ The interface description passed to `xio_create` shall be the interface description associated with the detected header. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_074: [ The IO create parameters shall be a `SERVER_PROTOCOL_IO_CONFIG` structure. ]*/ + chained_io->detected_io = xio_create(header_detect_io_instance->header_detect_entries[i].io_interface_description, &server_protocol_io_config); + if (chained_io->detected_io == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_077: [ If `xio_create` fails the header detect IO shall be closed and an error shall be indicated by calling `on_io_open_complete` with `IO_OPEN_ERROR`. ]*/ + LogError("Creating detected IO failed"); + free(chained_io); + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_086: [ The newly created IO shall be added to the chain of IOs by calling `singlylinkedlist_add`. ]*/ + LIST_ITEM_HANDLE new_list_item = singlylinkedlist_add(header_detect_io_instance->chained_io_list, chained_io); + if (new_list_item == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_084: [ If `singlylinkedlist_add` fails the newly created IO shall be destroyed and an error shall be indicated by calling `on_io_open_complete` with `IO_OPEN_ERROR`. ]*/ + LogError("Cannot add detected IO to list"); + xio_destroy(chained_io->detected_io); + free(chained_io); + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_063: [ `header_detect_io_close_async` shall close the last detected IO that was created as a result of matching a header. ]*/ + XIO_HANDLE* previous_last_io = header_detect_io_instance->last_io; + header_detect_io_instance->last_io = &chained_io->detected_io; + + /* Codes_SRS_HEADER_DETECT_IO_01_083: [ The header detect IO shall wait for opening of the detected IO (signaled by the `on_underlying_io_open_complete`). ]*/ + header_detect_io_instance->io_state = IO_STATE_OPENING_DETECTED_IO; + + /* Codes_SRS_HEADER_DETECT_IO_01_078: [ The newly create IO shall be open by calling `xio_open`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_079: [ The `on_io_open_complete` callback passed to `xio_open` shall be `on_underlying_io_open_complete`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_080: [ The `on_bytes_received` callback passed to `xio_open` shall be `on_underlying_io_bytes_received`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_081: [ The `on_io_error` callback passed to `xio_open` shall be `on_underlying_io_error`. ]*/ + if (xio_open(chained_io->detected_io, on_underlying_io_open_complete, header_detect_io_instance, on_underlying_io_bytes_received, header_detect_io_instance, on_underlying_io_error, header_detect_io_instance) != 0) + { + /* Codes_SRS_HEADER_DETECT_IO_01_082: [ If `xio_open` fails the header detect IO shall be closed and an error shall be indicated by calling `on_io_open_complete` with `IO_OPEN_ERROR`. ]*/ + LogError("Opening detected IO failed"); + if (singlylinkedlist_remove(header_detect_io_instance->chained_io_list, new_list_item) != 0) + { + LogError("Cannot remove chained IO from list"); + } + + xio_destroy(chained_io->detected_io); + free(chained_io); + header_detect_io_instance->last_io = previous_last_io; + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + } + else + { + // all OK + } + } + } + } + } + } + + break; + } + } + } + + if (has_one_match) + { + if (header_detect_io_instance->io_state == IO_STATE_OPENING_DETECTED_IO) + { + header_detect_io_instance->header_pos = 0; + } + else + { + header_detect_io_instance->header_pos++; + } + + size--; + buffer++; + } + else + { + /* all header matches failed, we can't proceed, send back to the peer the first header we know of, */ + /* then close as per spec. We do not care if we fail sending */ + if (xio_send(header_detect_io_instance->underlying_io, header_detect_io_instance->header_detect_entries[0].header_bytes, header_detect_io_instance->header_detect_entries[0].header_size, unchecked_on_send_complete, NULL) != 0) + { + LogError("Failed sending header"); + } + + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + size = 0; + } + + break; + } + + case IO_STATE_OPEN: + /* Codes_SRS_HEADER_DETECT_IO_01_089: [ If `on_underlying_io_bytes_received` is called while header detect IO is OPEN the bytes shall be given to the user via the `on_bytes_received` callback that was the `on_bytes_received` callback passed to `header_detect_io_open_async`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_090: [ If no detected IOs were created and `on_underlying_io_bytes_received` is called while header detect IO is OPEN, the `on_bytes_received` callback passed to `header_detect_io_open_async` shall be called to indicate the bytes as received. ]*/ + header_detect_io_instance->on_bytes_received(header_detect_io_instance->on_bytes_received_context, buffer, size); + size = 0; + break; + } + } + } + } +} + +static void on_underlying_io_close_complete(void* context) +{ + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)context; + + switch (header_detect_io_instance->io_state) + { + default: + break; + + case IO_STATE_CLOSING: + /* Codes_SRS_HEADER_DETECT_IO_01_095: [ When `on_underlying_io_open_complete` is called when the IO is closing, it shall destroy all the detected IOs that were created. ]*/ + destroy_io_chain(header_detect_io_instance); + + header_detect_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_close_complete(header_detect_io_instance); + break; + + case IO_STATE_WAIT_FOR_HEADER: + case IO_STATE_OPENING_DETECTED_IO: + case IO_STATE_OPENING_UNDERLYING_IO: + header_detect_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + break; + } +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + if (context == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_048: [ If `context` is NULL, `on_underlying_io_open_complete` shall do nothing. ]*/ + LogError("NULL context"); + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)context; + + if (open_result == IO_OPEN_OK) + { + switch (header_detect_io_instance->io_state) + { + default: + LogError("on_io_open_complete called in unexpected state: %d", (int)header_detect_io_instance->io_state); + break; + + case IO_STATE_OPENING_DETECTED_IO: + case IO_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_HEADER_DETECT_IO_01_046: [ When `on_underlying_io_open_complete` is called with `open_result` being `IO_OPEN_OK` while OPENING, the IO shall start monitoring received bytes in order to detect headers. ]*/ + header_detect_io_instance->io_state = IO_STATE_WAIT_FOR_HEADER; + break; + } + } + else + { + switch (header_detect_io_instance->io_state) + { + default: + LogError("on_io_open_complete called in unexpected state: %d", (int)header_detect_io_instance->io_state); + break; + + case IO_STATE_OPENING_DETECTED_IO: + case IO_STATE_OPENING_UNDERLYING_IO: + /* Codes_SRS_HEADER_DETECT_IO_01_047: [ When `on_underlying_io_open_complete` is called with `open_result` being `IO_OPEN_ERROR` while OPENING, the `on_io_open_complete` callback passed to `header_detect_io_open` shall be called with `IO_OPEN_ERROR`. ]*/ + internal_close(header_detect_io_instance); + + header_detect_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + break; + } + } + } +} + +static void on_underlying_io_error(void* context) +{ + if (context == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_058: [ If `context` is NULL, `on_underlying_io_error` shall do nothing. ]*/ + LogError("NULL context"); + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)context; + + switch (header_detect_io_instance->io_state) + { + default: + break; + + case IO_STATE_WAIT_FOR_HEADER: + case IO_STATE_OPENING_DETECTED_IO: + case IO_STATE_OPENING_UNDERLYING_IO: + /* Tests_SRS_HEADER_DETECT_IO_01_057: [ When `on_underlying_io_error` is called while OPENING, the IO shall indicate an error by calling `on_io_open_complete` with `IO_OPEN_ERROR` and it shall close the underlying IOs. ]*/ + internal_close(header_detect_io_instance); + indicate_open_complete(header_detect_io_instance, IO_OPEN_ERROR); + break; + + case IO_STATE_OPEN: + /* Codes_SRS_HEADER_DETECT_IO_01_059: [ When `on_underlying_io_error` is called while OPEN, the error should be indicated to the consumer by calling `on_io_error` and passing the `on_io_error_context` to it. ]*/ + header_detect_io_instance->io_state = IO_STATE_ERROR; + indicate_error(header_detect_io_instance); + break; + } + } +} + +static CONCRETE_IO_HANDLE header_detect_io_create(void* io_create_parameters) +{ + HEADER_DETECT_IO_INSTANCE* result; + + if (io_create_parameters == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_003: [ If `io_create_parameters` is NULL, `header_detect_io_create` shall fail and return NULL. ]*/ + LogError("NULL io_create_parameters"); + result = NULL; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_004: [ `io_create_parameters` shall be used as `HEADER_DETECT_IO_CONFIG*`. ]*/ + HEADER_DETECT_IO_CONFIG* header_detect_io_config = (HEADER_DETECT_IO_CONFIG*)io_create_parameters; + + /* Codes_SRS_HEADER_DETECT_IO_01_005: [ If the member `header_detect_entry_count` of `HEADER_DETECT_IO_CONFIG` is 0 then `header_detect_io_create` shall fail and return NULL. ]*/ + if ((header_detect_io_config->header_detect_entry_count == 0) || + /* Codes_SRS_HEADER_DETECT_IO_01_006: [ If the member `header_detect_entries` is NULL then `header_detect_io_create` shall fail and return NULL. ]*/ + (header_detect_io_config->header_detect_entries == NULL) || + /* Codes_SRS_HEADER_DETECT_IO_01_007: [ If the member `underlying_io` is NULL then `header_detect_io_create` shall fail and return NULL. ]*/ + (header_detect_io_config->underlying_io == NULL)) + { + LogError("Bad create parameters: header_detect_entry_count = %u, header_detect_entries = %p, underlying_io = %p", + header_detect_io_config->header_detect_entry_count, + header_detect_io_config->header_detect_entries, + header_detect_io_config->underlying_io); + result = NULL; + } + else + { + size_t i; + bool null_io_found = false; + + for (i = 0; i < header_detect_io_config->header_detect_entry_count; i++) + { + /* Codes_SRS_HEADER_DETECT_IO_01_052: [ The `io` member in the in each of the `header_detect_entries` shall be allowed to be NULL. ]*/ + if (header_detect_io_config->header_detect_entries[i].header.header_bytes == NULL) + { + LogError("header detect entry %u is invalid", (unsigned int)i); + break; + } + + if (header_detect_io_config->header_detect_entries[i].io_interface_description == NULL) + { + null_io_found = true; + } + } + + if (i < header_detect_io_config->header_detect_entry_count) + { + result = NULL; + } + else if (!null_io_found) + { + /* Codes_SRS_HEADER_DETECT_IO_01_054: [ At least one entry in `header_detect_entries` shall have IO set to NULL, otherwise `header_detect_io_create` shall fail and return NULL. ]*/ + LogError("No default header found"); + result = NULL; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_001: [ `header_detect_io_create` shall create a new header detect IO instance and on success it shall return a non-NULL handle to the newly created instance. ] */ + result = (HEADER_DETECT_IO_INSTANCE*)malloc(sizeof(HEADER_DETECT_IO_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_002: [ If allocating memory for the header detect IO instance fails, `header_detect_io_create` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for header detect IO"); + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_009: [ The `header_detect_entries` array shall be copied so that it can be later used when detecting which header was received. ]*/ + result->header_detect_entries = (INTERNAL_HEADER_DETECT_ENTRY*)malloc(header_detect_io_config->header_detect_entry_count * sizeof(INTERNAL_HEADER_DETECT_ENTRY)); + if (result->header_detect_entries == NULL) + { + free(result); + result = NULL; + } + else + { + result->header_detect_entry_count = header_detect_io_config->header_detect_entry_count; + + /* Codes_SRS_HEADER_DETECT_IO_01_009: [ The `header_detect_entries` array shall be copied so that it can be later used when detecting which header was received. ]*/ + for (i = 0; i < header_detect_io_config->header_detect_entry_count; i++) + { + result->header_detect_entries[i].header_size = header_detect_io_config->header_detect_entries[i].header.header_size; + result->header_detect_entries[i].header_bytes = (unsigned char*)malloc(result->header_detect_entries[i].header_size); + if (result->header_detect_entries[i].header_bytes == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_010: [ If allocating memory for the `header_detect_entries` or its constituents fails then `header_detect_io_create` shall fail and return NULL. ]*/ + break; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_014: [ For each entry in `header_detect_entries` the `header` field shall also be copied. ]*/ + (void)memcpy(result->header_detect_entries[i].header_bytes, header_detect_io_config->header_detect_entries[i].header.header_bytes, result->header_detect_entries[i].header_size); + result->header_detect_entries[i].io_interface_description = header_detect_io_config->header_detect_entries[i].io_interface_description; + } + } + + if (i < header_detect_io_config->header_detect_entry_count) + { + size_t j; + + LogError("Failed copying header detect configuration"); + for (j = 0; j < i; j++) + { + free(result->header_detect_entries[j].header_bytes); + } + + free(result->header_detect_entries); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_060: [ `header_detect_io_create` shall create a singly linked list by calling `singlylinkedlist_create` where the chained detected IOs shall be stored. ]*/ + result->chained_io_list = singlylinkedlist_create(); + if (result->chained_io_list == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_065: [ If `singlylinkedlist_create` fails then `header_detect_io_create` shall fail and return NULL. ]*/ + LogError("Failed copying header detect configuration"); + for (i = 0; i < result->header_detect_entry_count; i++) + { + free(result->header_detect_entries[i].header_bytes); + } + + free(result->header_detect_entries); + free(result); + result = NULL; + } + else + { + result->underlying_io = header_detect_io_config->underlying_io; + result->on_io_open_complete = NULL; + result->on_io_close_complete = NULL; + result->on_io_error = NULL; + result->on_bytes_received = NULL; + result->on_io_open_complete_context = NULL; + result->on_io_close_complete_context = NULL; + result->on_io_error_context = NULL; + result->on_bytes_received_context = NULL; + + /* Codes_SRS_HEADER_DETECT_IO_01_070: [ If no detected IO was created then `header_detect_io_close_async` shall close the `underlying_io` passed in `header_detect_io_create`. ]*/ + result->last_io = &result->underlying_io; + + result->io_state = IO_STATE_NOT_OPEN; + } + } + } + } + } + } + } + + return result; +} + +static void header_detect_io_destroy(CONCRETE_IO_HANDLE header_detect_io) +{ + if (header_detect_io == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_012: [ If `header_detect_io` is NULL, `header_detect_io_destroy` shall do nothing. ]*/ + LogError("NULL header_detect_io"); + } + else + { + size_t i; + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + if (header_detect_io_instance->io_state != IO_STATE_NOT_OPEN) + { + /* Codes_SRS_HEADER_DETECT_IO_01_062: [ If the IO is still open when `header_detect_io_destroy` is called, all actions normally executed when closing the IO shall also be executed. ]*/ + internal_close(header_detect_io_instance); + } + + /* Codes_SRS_HEADER_DETECT_IO_01_061: [ `header_detect_io_destroy` shall destroy the chained IO list by calling `singlylinkedlist_destroy`. ]*/ + singlylinkedlist_destroy(header_detect_io_instance->chained_io_list); + + /* Codes_SRS_HEADER_DETECT_IO_01_011: [ `header_detect_io_destroy` shall free all resources associated with the `header_detect_io` handle. ]*/ + for (i = 0; i < header_detect_io_instance->header_detect_entry_count; i++) + { + /* Codes_SRS_HEADER_DETECT_IO_01_013: [ `header_detect_io_destroy` shall free the memory allocated for the `header_detect_entries`. ]*/ + free(header_detect_io_instance->header_detect_entries[i].header_bytes); + } + + free(header_detect_io_instance->header_detect_entries); + + free(header_detect_io); + } +} + +static int header_detect_io_open_async(CONCRETE_IO_HANDLE header_detect_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result; + + if ((header_detect_io == NULL) || + (on_io_open_complete == NULL) || + (on_bytes_received == NULL) || + (on_io_error == NULL)) + { + /* Codes_SRS_HEADER_DETECT_IO_01_021: [ If `header_detect_io`, `on_io_open_complete`, `on_bytes_received` or `on_io_error` is NULL, `header_detect_io_open_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: header_detect_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error = %p", + header_detect_io, on_io_open_complete, on_bytes_received, on_io_error); + result = __FAILURE__; + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + if (header_detect_io_instance->io_state != IO_STATE_NOT_OPEN) + { + /* Codes_SRS_HEADER_DETECT_IO_01_020: [ If the IO is already OPEN or OPENING then `header_detect_io_open_async` shall fail and return a non-zero value. ]*/ + LogError("Already OPEN"); + result = __FAILURE__; + } + else + { + header_detect_io_instance->on_bytes_received = on_bytes_received; + header_detect_io_instance->on_io_open_complete = on_io_open_complete; + header_detect_io_instance->on_io_error = on_io_error; + header_detect_io_instance->on_bytes_received_context = on_bytes_received_context; + header_detect_io_instance->on_io_open_complete_context = on_io_open_complete_context; + header_detect_io_instance->on_io_error_context = on_io_error_context; + + header_detect_io_instance->io_state = IO_STATE_OPENING_UNDERLYING_IO; + header_detect_io_instance->header_pos = 0; + + /* Codes_SRS_HEADER_DETECT_IO_01_015: [ `header_detect_io_open_async` shall open the underlying IO by calling `xio_open` and passing to it: ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_016: [ - `xio` shall be the `underlying_io` member of the `io_create_parameters` passed to `header_detect_io_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_017: [ - `on_io_open_complete`, `on_io_open_complete_context`, `on_bytes_received`, `on_bytes_received_context`, `on_error` and `on_error_context` shall be set to implementation specific values of `header_detect_io`. ]*/ + if (xio_open(header_detect_io_instance->underlying_io, on_underlying_io_open_complete, header_detect_io_instance, on_underlying_io_bytes_received, header_detect_io_instance, on_underlying_io_error, header_detect_io_instance) != 0) + { + /* Codes_SRS_HEADER_DETECT_IO_01_019: [ If `xio_open` fails, `header_detect_io_open_async` shall fail and return a non-zero value. ]*/ + LogError("xio_open failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_018: [ On success `header_detect_io_open_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static int header_detect_io_close_async(CONCRETE_IO_HANDLE header_detect_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result; + + if (header_detect_io == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_026: [ If `header_detect_io` is NULL, `header_detect_io_close_async` shall fail and return a non-zero value. ]*/ + LogError("NULL header_detect_io"); + result = __FAILURE__; + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + /* Codes_SRS_HEADER_DETECT_IO_01_027: [ If the IO is not OPEN (open has not been called or close has been completely carried out) `header_detect_io_close_async` shall fail and return a non-zero value. ]*/ + if ((header_detect_io_instance->io_state == IO_STATE_OPENING_UNDERLYING_IO) || + (header_detect_io_instance->io_state == IO_STATE_OPENING_DETECTED_IO) || + (header_detect_io_instance->io_state == IO_STATE_WAIT_FOR_HEADER)) + { + /* Codes_SRS_HEADER_DETECT_IO_01_028: [ If the IO is OPENING (`header_detect_io_open_async` has been called, but no header has been detected yet), `header_detect_io_close_async` shall close the underlying IO and call `on_io_open_complete` with `IO_OPEN_CANCELLED`. ]*/ + (void)internal_close(header_detect_io_instance); + header_detect_io_instance->on_io_open_complete(header_detect_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED); + result = 0; + } + else if ((header_detect_io_instance->io_state == IO_STATE_NOT_OPEN) || + /* Codes_SRS_HEADER_DETECT_IO_01_053: [ If the IO is CLOSING then `header_detect_io_close_async` shall fail and return a non-zero value. ]*/ + (header_detect_io_instance->io_state == IO_STATE_CLOSING)) + { + LogError("Not open"); + result = __FAILURE__; + } + else + { + header_detect_io_instance->io_state = IO_STATE_CLOSING; + header_detect_io_instance->on_io_close_complete = on_io_close_complete; + header_detect_io_instance->on_io_close_complete_context = callback_context; + + /* Codes_SRS_HEADER_DETECT_IO_01_022: [ `header_detect_io_close_async` shall close the underlying IO by calling `xio_close` and passing to it: ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_023: [ - `xio` shall be the `underlying_io` member of the `io_create_parameters` passed to `header_detect_io_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_024: [ - `on_io_close_complete` shall be set to implementation specific values of `header_detect_io`. ]*/ + if (xio_close(*header_detect_io_instance->last_io, on_underlying_io_close_complete, header_detect_io_instance) != 0) + { + /* Codes_SRS_HEADER_DETECT_IO_01_092: [ If `xio_close` fails `header_detect_io_close_async` shall fail and return a non-zero value. ]*/ + LogError("xio_close failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_025: [ On success `header_detect_io_close_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static int header_detect_io_send_async(CONCRETE_IO_HANDLE header_detect_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_SRS_HEADER_DETECT_IO_01_055: [ `on_send_complete` and `callback_context` shall be allowed to be NULL. ]*/ + if ((header_detect_io == NULL) || + (buffer == NULL) || + /* Codes_SRS_HEADER_DETECT_IO_01_034: [ If `size` is 0, `header_detect_io_send_async` shall fail and return a non-zero value. ]*/ + (size == 0)) + { + /* Codes_SRS_HEADER_DETECT_IO_01_033: [ If `header_detect_io` or `buffer` is NULL, `header_detect_io_send_async` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: header_detect_io = %p, buffer = %p, size = %u", + header_detect_io, buffer, (unsigned int)size); + result = __FAILURE__; + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + if (header_detect_io_instance->io_state != IO_STATE_OPEN) + { + /* Codes_SRS_HEADER_DETECT_IO_01_093: [ `header_detect_io_send_async` when the IO is not open shall fail and return a non-zero value. ]*/ + LogError("header_detect_io not OPEN"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_029: [ If no detected IO was created, `header_detect_io_send_async` shall send the bytes to the underlying IO passed via `header_detect_io_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_030: [ The `buffer`, `size`, `on_send_complete` and `callback_context` shall be passed as is to `xio_send`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_071: [ If the header IO is open `header_detect_io_send_async` shall send the bytes to the last detected IO by calling `xio_send` that was created as result of matching a header. ]*/ + if (xio_send(*header_detect_io_instance->last_io, buffer, size, on_send_complete, callback_context) != 0) + { + LogError("xio_send failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_031: [ On success `header_detect_io_send_async` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +static void header_detect_io_dowork(CONCRETE_IO_HANDLE header_detect_io) +{ + if (header_detect_io == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_036: [ If `header_detect_io` is NULL, `header_detect_io_dowork` shall do nothing. ]*/ + LogError("NULL header_detect_io"); + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + /* Codes_SRS_HEADER_DETECT_IO_01_037: [ No work shall be scheduled if `header_detect_io` is not OPEN or in ERROR (an error has been indicated to the user). ]*/ + if ((header_detect_io_instance->io_state != IO_STATE_NOT_OPEN) && + (header_detect_io_instance->io_state != IO_STATE_ERROR)) + { + /* Codes_SRS_HEADER_DETECT_IO_01_056: [ `header_detect_io_dowork` shall call `xio_dowork` for all detected IOs created as a result of matching headers. ]*/ + LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(header_detect_io_instance->chained_io_list); + while (list_item != NULL) + { + CHAINED_IO* chained_io = (CHAINED_IO*)singlylinkedlist_item_get_value(list_item); + xio_dowork(chained_io->detected_io); + + list_item = singlylinkedlist_get_next_item(list_item); + } + + /* Codes_SRS_HEADER_DETECT_IO_01_035: [ `header_detect_io_dowork` shall schedule work for the underlying IO associated with `header_detect_io` by calling `xio_dowork` and passing as argument the `underlying_io` member of the `io_create_parameters` passed to `header_detect_io_create`. ]*/ + xio_dowork(header_detect_io_instance->underlying_io); + } + } +} + +static int header_detect_io_set_option(CONCRETE_IO_HANDLE header_detect_io, const char* option_name, const void* value) +{ + int result; + + if ((header_detect_io == NULL) || + (option_name == NULL)) + { + /* Codes_SRS_HEADER_DETECT_IO_01_044: [ If `header_detect_io` or `optionName` is NULL, `header_detect_io_set_option` shall fail and return a non-zero value. ]*/ + LogError("NULL header_detect_io"); + result = __FAILURE__; + } + else + { + HEADER_DETECT_IO_INSTANCE* header_detect_io_instance = (HEADER_DETECT_IO_INSTANCE*)header_detect_io; + + /* Codes_SRS_HEADER_DETECT_IO_01_042: [ If no detected IO was created `header_detect_io_set_option` shall pass any option to the underlying IO by calling `xio_setoption` and passing as IO handle the `underlying_io` member of the `io_create_parameters` passed to `header_detect_io_create`. ]*/ + /* Codes_SRS_HEADER_DETECT_IO_01_072: [ If any detected IO was created, `header_detect_io_set_option` shall pass any option to the last detected IO by calling `xio_setoption` and passing as IO handle the `underlying_io` member of the `io_create_parameters` passed to `header_detect_io_create`. ]*/ + if (xio_setoption(*header_detect_io_instance->last_io, option_name, value) != 0) + { + /* Codes_SRS_HEADER_DETECT_IO_01_045: [ If `xio_setoption` fails, `header_detect_io_set_option` shall fail and return a non-zero value. ]*/ + LogError("Setting the option on the underlying IO failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_043: [ On success, `header_detect_io_set_option` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +/*this function will clone an option given by name and value*/ +static void* header_detect_io_clone_option(const char* name, const void* value) +{ + (void)name; + (void)value; + return NULL; +} + +/*this function destroys an option previously created*/ +static void header_detect_io_destroy_option(const char* name, const void* value) +{ + (void)name; + (void)value; +} + +static OPTIONHANDLER_HANDLE header_detect_io_retrieve_options(CONCRETE_IO_HANDLE header_detect_io) +{ + OPTIONHANDLER_HANDLE result; + + if (header_detect_io == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_041: [ If `header_detect_io` is NULL, `header_detect_io_retrieve_options` shall return NULL. ]*/ + LogError("NULL header_detect_io"); + result = NULL; + } + else + { + /* Codes_SRS_HEADER_DETECT_IO_01_038: [ `header_detect_io_retrieve_options` shall create a new `OPTIONHANDLER_HANDLE` by calling `OptionHandler_Create` and on success it shall return a non-NULL handle to the newly created option handler. ]*/ + result = OptionHandler_Create(header_detect_io_clone_option, header_detect_io_destroy_option, header_detect_io_set_option); + if (result == NULL) + { + /* Codes_SRS_HEADER_DETECT_IO_01_040: [ If `OptionHandler_Create` fails, `header_detect_io_retrieve_options` shall return NULL. ]*/ + LogError("unable to OptionHandler_Create"); + /*return as is*/ + } + else + { + /*insert here work to add the options to "result" handle*/ + /* Codes_SRS_HEADER_DETECT_IO_01_039: [ No options shall be added to the newly created option handler. ]*/ + } + } + return result; +} + +static const IO_INTERFACE_DESCRIPTION header_detect_io_interface_description = +{ + header_detect_io_retrieve_options, + header_detect_io_create, + header_detect_io_destroy, + header_detect_io_open_async, + header_detect_io_close_async, + header_detect_io_send_async, + header_detect_io_dowork, + header_detect_io_set_option +}; + +const IO_INTERFACE_DESCRIPTION* header_detect_io_get_interface_description(void) +{ + return &header_detect_io_interface_description; +} + +static const AMQP_HEADER amqp_header = +{ + amqp_header_bytes, + sizeof(amqp_header_bytes) +}; + +static const AMQP_HEADER sasl_amqp_header = +{ + sasl_amqp_header_bytes, + sizeof(sasl_amqp_header_bytes) +}; + +AMQP_HEADER header_detect_io_get_amqp_header(void) +{ + /* Codes_SRS_HEADER_DETECT_IO_01_091: [ `header_detect_io_get_amqp_header` shall return a structure that should point to a buffer that contains the bytes { 'A', 'M', 'Q', 'P', 0, 1, 0, 0 }. ]*/ + return amqp_header; +} + +AMQP_HEADER header_detect_io_get_sasl_amqp_header(void) +{ + /* Codes_SRS_HEADER_DETECT_IO_01_091: [ `header_detect_io_get_sasl_amqp_header` shall return a structure that should point to a buffer that contains the bytes { 'A', 'M', 'Q', 'P', 3, 1, 0, 0 }. ]*/ + return sasl_amqp_header; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/link.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1655 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/amqp_frame_codec.h" +#include "azure_uamqp_c/async_operation.h" + +#define DEFAULT_LINK_CREDIT 10000 + +typedef struct DELIVERY_INSTANCE_TAG +{ + delivery_number delivery_id; + ON_DELIVERY_SETTLED on_delivery_settled; + void* callback_context; + void* link; + tickcounter_ms_t start_tick; + tickcounter_ms_t timeout; +} DELIVERY_INSTANCE; + +typedef struct ON_LINK_DETACH_EVENT_SUBSCRIPTION_TAG +{ + ON_LINK_DETACH_RECEIVED on_link_detach_received; + void* context; +} ON_LINK_DETACH_EVENT_SUBSCRIPTION; + +typedef struct LINK_INSTANCE_TAG +{ + SESSION_HANDLE session; + LINK_STATE link_state; + LINK_STATE previous_link_state; + AMQP_VALUE source; + AMQP_VALUE target; + handle handle; + LINK_ENDPOINT_HANDLE link_endpoint; + char* name; + SINGLYLINKEDLIST_HANDLE pending_deliveries; + sequence_no delivery_count; + role role; + ON_LINK_STATE_CHANGED on_link_state_changed; + ON_LINK_FLOW_ON on_link_flow_on; + ON_TRANSFER_RECEIVED on_transfer_received; + void* callback_context; + sender_settle_mode snd_settle_mode; + receiver_settle_mode rcv_settle_mode; + sequence_no initial_delivery_count; + uint64_t max_message_size; + uint64_t peer_max_message_size; + uint32_t current_link_credit; + uint32_t max_link_credit; + uint32_t available; + fields attach_properties; + bool is_underlying_session_begun; + bool is_closed; + unsigned char* received_payload; + uint32_t received_payload_size; + delivery_number received_delivery_id; + TICK_COUNTER_HANDLE tick_counter; + ON_LINK_DETACH_EVENT_SUBSCRIPTION on_link_detach_received_event_subscription; +} LINK_INSTANCE; + +DEFINE_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE); + +static void set_link_state(LINK_INSTANCE* link_instance, LINK_STATE link_state) +{ + link_instance->previous_link_state = link_instance->link_state; + link_instance->link_state = link_state; + + if (link_instance->on_link_state_changed != NULL) + { + link_instance->on_link_state_changed(link_instance->callback_context, link_state, link_instance->previous_link_state); + } +} + +static void remove_all_pending_deliveries(LINK_INSTANCE* link, bool indicate_settled) +{ + if (link->pending_deliveries != NULL) + { + LIST_ITEM_HANDLE item = singlylinkedlist_get_head_item(link->pending_deliveries); + while (item != NULL) + { + LIST_ITEM_HANDLE next_item = singlylinkedlist_get_next_item(item); + ASYNC_OPERATION_HANDLE pending_delivery_operation = (ASYNC_OPERATION_HANDLE)singlylinkedlist_item_get_value(item); + if (pending_delivery_operation != NULL) + { + DELIVERY_INSTANCE* delivery_instance = (DELIVERY_INSTANCE*)GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, pending_delivery_operation); + if (indicate_settled && (delivery_instance->on_delivery_settled != NULL)) + { + delivery_instance->on_delivery_settled(delivery_instance->callback_context, delivery_instance->delivery_id, LINK_DELIVERY_SETTLE_REASON_NOT_DELIVERED, NULL); + } + + async_operation_destroy(pending_delivery_operation); + } + + item = next_item; + } + + singlylinkedlist_destroy(link->pending_deliveries); + link->pending_deliveries = NULL; + } +} + +static int send_flow(LINK_INSTANCE* link) +{ + int result; + FLOW_HANDLE flow = flow_create(0, 0, 0); + + if (flow == NULL) + { + LogError("NULL flow performative"); + result = __FAILURE__; + } + else + { + if (flow_set_link_credit(flow, link->current_link_credit) != 0) + { + LogError("Cannot set link credit on flow performative"); + result = __FAILURE__; + } + else if (flow_set_handle(flow, link->handle) != 0) + { + LogError("Cannot set handle on flow performative"); + result = __FAILURE__; + } + else if (flow_set_delivery_count(flow, link->delivery_count) != 0) + { + LogError("Cannot set delivery count on flow performative"); + result = __FAILURE__; + } + else + { + if (session_send_flow(link->link_endpoint, flow) != 0) + { + LogError("Sending flow frame failed in session send"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + flow_destroy(flow); + } + + return result; +} + +static int send_disposition(LINK_INSTANCE* link_instance, delivery_number delivery_number, AMQP_VALUE delivery_state) +{ + int result; + + DISPOSITION_HANDLE disposition = disposition_create(link_instance->role, delivery_number); + if (disposition == NULL) + { + LogError("NULL disposition performative"); + result = __FAILURE__; + } + else + { + if (disposition_set_last(disposition, delivery_number) != 0) + { + LogError("Failed setting last on disposition performative"); + result = __FAILURE__; + } + else if (disposition_set_settled(disposition, true) != 0) + { + LogError("Failed setting settled on disposition performative"); + result = __FAILURE__; + } + else if ((delivery_state != NULL) && (disposition_set_state(disposition, delivery_state) != 0)) + { + LogError("Failed setting state on disposition performative"); + result = __FAILURE__; + } + else + { + if (session_send_disposition(link_instance->link_endpoint, disposition) != 0) + { + LogError("Sending disposition failed in session send"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + disposition_destroy(disposition); + } + + return result; +} + +static int send_detach(LINK_INSTANCE* link_instance, bool close, ERROR_HANDLE error_handle) +{ + int result; + DETACH_HANDLE detach_performative; + + detach_performative = detach_create(0); + if (detach_performative == NULL) + { + LogError("NULL detach performative"); + result = __FAILURE__; + } + else + { + if ((error_handle != NULL) && + (detach_set_error(detach_performative, error_handle) != 0)) + { + LogError("Failed setting error on detach frame"); + result = __FAILURE__; + } + else if (close && + (detach_set_closed(detach_performative, true) != 0)) + { + LogError("Failed setting closed field on detach frame"); + result = __FAILURE__; + } + else + { + if (session_send_detach(link_instance->link_endpoint, detach_performative) != 0) + { + LogError("Sending detach frame failed in session send"); + result = __FAILURE__; + } + else + { + if (close) + { + /* Declare link to be closed */ + link_instance->is_closed = true; + } + + result = 0; + } + } + + detach_destroy(detach_performative); + } + + return result; +} + +static int send_attach(LINK_INSTANCE* link, const char* name, handle handle, role role) +{ + int result; + ATTACH_HANDLE attach = attach_create(name, handle, role); + + if (attach == NULL) + { + LogError("NULL attach performative"); + result = __FAILURE__; + } + else + { + result = 0; + + link->delivery_count = link->initial_delivery_count; + + attach_set_snd_settle_mode(attach, link->snd_settle_mode); + attach_set_rcv_settle_mode(attach, link->rcv_settle_mode); + attach_set_role(attach, role); + attach_set_source(attach, link->source); + attach_set_target(attach, link->target); + if (link->attach_properties != NULL) + { + (void)attach_set_properties(attach, link->attach_properties); + } + + if (role == role_sender) + { + if (attach_set_initial_delivery_count(attach, link->delivery_count) != 0) + { + LogError("Cannot set attach initial delivery count"); + result = __FAILURE__; + } + } + + if (result == 0) + { + if (attach_set_max_message_size(attach, link->max_message_size) != 0) + { + LogError("Cannot set max message size"); + result = __FAILURE__; + } + else if (session_send_attach(link->link_endpoint, attach) != 0) + { + LogError("Sending attach failed in session send"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + attach_destroy(attach); + } + + return result; +} + +static void link_frame_received(void* context, AMQP_VALUE performative, uint32_t payload_size, const unsigned char* payload_bytes) +{ + LINK_INSTANCE* link_instance = (LINK_INSTANCE*)context; + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + + if (is_attach_type_by_descriptor(descriptor)) + { + ATTACH_HANDLE attach_handle; + + if (amqpvalue_get_attach(performative, &attach_handle) != 0) + { + LogError("Cannot get attach performative"); + } + else + { + if ((link_instance->role == role_receiver) && + (attach_get_initial_delivery_count(attach_handle, &link_instance->delivery_count) != 0)) + { + LogError("Cannot get initial delivery count"); + remove_all_pending_deliveries(link_instance, true); + set_link_state(link_instance, LINK_STATE_DETACHED); + } + else + { + if (attach_get_max_message_size(attach_handle, &link_instance->peer_max_message_size) != 0) + { + LogError("Could not retrieve peer_max_message_size from attach frame"); + } + + if ((link_instance->link_state == LINK_STATE_DETACHED) || + (link_instance->link_state == LINK_STATE_HALF_ATTACHED_ATTACH_SENT)) + { + if (link_instance->role == role_receiver) + { + link_instance->current_link_credit = link_instance->max_link_credit; + send_flow(link_instance); + } + else + { + link_instance->current_link_credit = 0; + } + + if (link_instance->link_state == LINK_STATE_DETACHED) + { + set_link_state(link_instance, LINK_STATE_HALF_ATTACHED_ATTACH_RECEIVED); + } + else + { + set_link_state(link_instance, LINK_STATE_ATTACHED); + } + } + } + + attach_destroy(attach_handle); + } + } + else if (is_flow_type_by_descriptor(descriptor)) + { + FLOW_HANDLE flow_handle; + if (amqpvalue_get_flow(performative, &flow_handle) != 0) + { + LogError("Cannot get flow performative"); + } + else + { + if (link_instance->role == role_sender) + { + delivery_number rcv_delivery_count; + uint32_t rcv_link_credit; + + if (flow_get_link_credit(flow_handle, &rcv_link_credit) != 0) + { + LogError("Cannot get link credit"); + remove_all_pending_deliveries(link_instance, true); + set_link_state(link_instance, LINK_STATE_DETACHED); + } + else if (flow_get_delivery_count(flow_handle, &rcv_delivery_count) != 0) + { + LogError("Cannot get delivery count"); + remove_all_pending_deliveries(link_instance, true); + set_link_state(link_instance, LINK_STATE_DETACHED); + } + else + { + link_instance->current_link_credit = rcv_delivery_count + rcv_link_credit - link_instance->delivery_count; + if (link_instance->current_link_credit > 0) + { + link_instance->on_link_flow_on(link_instance->callback_context); + } + } + } + } + + flow_destroy(flow_handle); + } + else if (is_transfer_type_by_descriptor(descriptor)) + { + if (link_instance->on_transfer_received != NULL) + { + TRANSFER_HANDLE transfer_handle; + if (amqpvalue_get_transfer(performative, &transfer_handle) != 0) + { + LogError("Cannot get transfer performative"); + } + else + { + AMQP_VALUE delivery_state; + bool more; + bool is_error; + + link_instance->current_link_credit--; + link_instance->delivery_count++; + if (link_instance->current_link_credit == 0) + { + link_instance->current_link_credit = link_instance->max_link_credit; + send_flow(link_instance); + } + + more = false; + /* Attempt to get more flag, default to false */ + (void)transfer_get_more(transfer_handle, &more); + is_error = false; + + if (transfer_get_delivery_id(transfer_handle, &link_instance->received_delivery_id) != 0) + { + /* is this not a continuation transfer? */ + if (link_instance->received_payload_size == 0) + { + LogError("Could not get the delivery Id from the transfer performative"); + is_error = true; + } + } + + if (!is_error) + { + /* If this is a continuation transfer or if this is the first chunk of a multi frame transfer */ + if ((link_instance->received_payload_size > 0) || more) + { + unsigned char* new_received_payload = (unsigned char*)realloc(link_instance->received_payload, link_instance->received_payload_size + payload_size); + if (new_received_payload == NULL) + { + LogError("Could not allocate memory for the received payload"); + } + else + { + link_instance->received_payload = new_received_payload; + (void)memcpy(link_instance->received_payload + link_instance->received_payload_size, payload_bytes, payload_size); + link_instance->received_payload_size += payload_size; + } + } + + if (!more) + { + const unsigned char* indicate_payload_bytes; + uint32_t indicate_payload_size; + + /* if no previously stored chunks then simply report the current payload */ + if (link_instance->received_payload_size > 0) + { + indicate_payload_size = link_instance->received_payload_size; + indicate_payload_bytes = link_instance->received_payload; + } + else + { + indicate_payload_size = payload_size; + indicate_payload_bytes = payload_bytes; + } + + delivery_state = link_instance->on_transfer_received(link_instance->callback_context, transfer_handle, indicate_payload_size, indicate_payload_bytes); + + if (link_instance->received_payload_size > 0) + { + free(link_instance->received_payload); + link_instance->received_payload = NULL; + link_instance->received_payload_size = 0; + } + + if (delivery_state != NULL) + { + if (send_disposition(link_instance, link_instance->received_delivery_id, delivery_state) != 0) + { + LogError("Cannot send disposition frame"); + } + + amqpvalue_destroy(delivery_state); + } + } + } + + transfer_destroy(transfer_handle); + } + } + } + else if (is_disposition_type_by_descriptor(descriptor)) + { + DISPOSITION_HANDLE disposition; + if (amqpvalue_get_disposition(performative, &disposition) != 0) + { + LogError("Cannot get disposition performative"); + } + else + { + delivery_number first; + delivery_number last; + + if (disposition_get_first(disposition, &first) != 0) + { + LogError("Cannot get first field"); + } + else + { + bool settled; + + if (disposition_get_last(disposition, &last) != 0) + { + last = first; + } + + if (disposition_get_settled(disposition, &settled) != 0) + { + settled = false; + } + + if (settled) + { + LIST_ITEM_HANDLE pending_delivery = singlylinkedlist_get_head_item(link_instance->pending_deliveries); + while (pending_delivery != NULL) + { + LIST_ITEM_HANDLE next_pending_delivery = singlylinkedlist_get_next_item(pending_delivery); + ASYNC_OPERATION_HANDLE pending_delivery_operation = (ASYNC_OPERATION_HANDLE)singlylinkedlist_item_get_value(pending_delivery); + if (pending_delivery_operation == NULL) + { + LogError("Cannot obtain pending delivery"); + break; + } + else + { + DELIVERY_INSTANCE* delivery_instance = (DELIVERY_INSTANCE*)GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, pending_delivery_operation); + + if ((delivery_instance->delivery_id >= first) && (delivery_instance->delivery_id <= last)) + { + AMQP_VALUE delivery_state; + if (disposition_get_state(disposition, &delivery_state) == 0) + { + delivery_instance->on_delivery_settled(delivery_instance->callback_context, delivery_instance->delivery_id, LINK_DELIVERY_SETTLE_REASON_DISPOSITION_RECEIVED, delivery_state); + async_operation_destroy(pending_delivery_operation); + if (singlylinkedlist_remove(link_instance->pending_deliveries, pending_delivery) != 0) + { + LogError("Cannot remove pending delivery"); + break; + } + + pending_delivery = next_pending_delivery; + } + } + else + { + pending_delivery = next_pending_delivery; + } + } + } + } + } + + disposition_destroy(disposition); + } + } + else if (is_detach_type_by_descriptor(descriptor)) + { + DETACH_HANDLE detach; + + /* Set link state appropriately based on whether we received detach condition */ + if (amqpvalue_get_detach(performative, &detach) != 0) + { + LogError("Cannot get detach performative"); + } + else + { + bool closed = false; + ERROR_HANDLE error; + + (void)detach_get_closed(detach, &closed); + + /* Received a detach while attached */ + if (link_instance->link_state == LINK_STATE_ATTACHED) + { + /* Respond with ack */ + if (send_detach(link_instance, closed, NULL) != 0) + { + LogError("Failed sending detach frame"); + } + } + /* Received a closing detach after we sent a non-closing detach. */ + else if (closed && + ((link_instance->link_state == LINK_STATE_HALF_ATTACHED_ATTACH_SENT) || (link_instance->link_state == LINK_STATE_HALF_ATTACHED_ATTACH_RECEIVED)) && + !link_instance->is_closed) + { + + /* In this case, we MUST signal that we closed by reattaching and then sending a closing detach.*/ + if (send_attach(link_instance, link_instance->name, 0, link_instance->role) != 0) + { + LogError("Failed sending attach frame"); + } + + if (send_detach(link_instance, true, NULL) != 0) + { + LogError("Failed sending detach frame"); + } + } + + if (detach_get_error(detach, &error) != 0) + { + error = NULL; + } + remove_all_pending_deliveries(link_instance, true); + // signal link detach received in order to handle cases like redirect + if (link_instance->on_link_detach_received_event_subscription.on_link_detach_received != NULL) + { + link_instance->on_link_detach_received_event_subscription.on_link_detach_received(link_instance->on_link_detach_received_event_subscription.context, error); + } + + if (error != NULL) + { + set_link_state(link_instance, LINK_STATE_ERROR); + error_destroy(error); + } + else + { + set_link_state(link_instance, LINK_STATE_DETACHED); + } + + detach_destroy(detach); + } + } +} + +static void on_session_state_changed(void* context, SESSION_STATE new_session_state, SESSION_STATE previous_session_state) +{ + LINK_INSTANCE* link_instance = (LINK_INSTANCE*)context; + (void)previous_session_state; + + if (new_session_state == SESSION_STATE_MAPPED) + { + if ((link_instance->link_state == LINK_STATE_DETACHED) && (!link_instance->is_closed)) + { + if (send_attach(link_instance, link_instance->name, 0, link_instance->role) == 0) + { + set_link_state(link_instance, LINK_STATE_HALF_ATTACHED_ATTACH_SENT); + } + } + } + else if (new_session_state == SESSION_STATE_DISCARDING) + { + remove_all_pending_deliveries(link_instance, true); + set_link_state(link_instance, LINK_STATE_DETACHED); + } + else if (new_session_state == SESSION_STATE_ERROR) + { + remove_all_pending_deliveries(link_instance, true); + set_link_state(link_instance, LINK_STATE_ERROR); + } +} + +static void on_session_flow_on(void* context) +{ + LINK_INSTANCE* link_instance = (LINK_INSTANCE*)context; + if (link_instance->role == role_sender) + { + link_instance->on_link_flow_on(link_instance->callback_context); + } +} + +static void on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + LIST_ITEM_HANDLE delivery_instance_list_item = (LIST_ITEM_HANDLE)context; + ASYNC_OPERATION_HANDLE pending_delivery_operation = (ASYNC_OPERATION_HANDLE)singlylinkedlist_item_get_value(delivery_instance_list_item); + DELIVERY_INSTANCE* delivery_instance = (DELIVERY_INSTANCE*)GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, pending_delivery_operation); + LINK_HANDLE link = (LINK_HANDLE)delivery_instance->link; + (void)send_result; + if (link->snd_settle_mode == sender_settle_mode_settled) + { + delivery_instance->on_delivery_settled(delivery_instance->callback_context, delivery_instance->delivery_id, LINK_DELIVERY_SETTLE_REASON_SETTLED, NULL); + async_operation_destroy(pending_delivery_operation); + (void)singlylinkedlist_remove(link->pending_deliveries, delivery_instance_list_item); + } +} + +LINK_HANDLE link_create(SESSION_HANDLE session, const char* name, role role, AMQP_VALUE source, AMQP_VALUE target) +{ + LINK_INSTANCE* result = (LINK_INSTANCE*)malloc(sizeof(LINK_INSTANCE)); + if (result == NULL) + { + LogError("Cannot create link"); + } + else + { + result->link_state = LINK_STATE_DETACHED; + result->previous_link_state = LINK_STATE_DETACHED; + result->role = role; + result->source = amqpvalue_clone(source); + result->target = amqpvalue_clone(target); + result->session = session; + result->handle = 0; + result->snd_settle_mode = sender_settle_mode_unsettled; + result->rcv_settle_mode = receiver_settle_mode_first; + result->delivery_count = 0; + result->initial_delivery_count = 0; + result->max_message_size = 0; + result->max_link_credit = DEFAULT_LINK_CREDIT; + result->peer_max_message_size = 0; + result->is_underlying_session_begun = false; + result->is_closed = false; + result->attach_properties = NULL; + result->received_payload = NULL; + result->received_payload_size = 0; + result->received_delivery_id = 0; + result->on_link_detach_received_event_subscription.on_link_detach_received = NULL; + result->on_link_detach_received_event_subscription.context = NULL; + + result->tick_counter = tickcounter_create(); + if (result->tick_counter == NULL) + { + LogError("Cannot create tick counter for link"); + free(result); + result = NULL; + } + else + { + result->pending_deliveries = singlylinkedlist_create(); + if (result->pending_deliveries == NULL) + { + LogError("Cannot create pending deliveries list"); + tickcounter_destroy(result->tick_counter); + free(result); + result = NULL; + } + else + { + size_t name_length = strlen(name); + result->name = (char*)malloc(name_length + 1); + if (result->name == NULL) + { + LogError("Cannot allocate memory for link name"); + tickcounter_destroy(result->tick_counter); + singlylinkedlist_destroy(result->pending_deliveries); + free(result); + result = NULL; + } + else + { + result->on_link_state_changed = NULL; + result->callback_context = NULL; + set_link_state(result, LINK_STATE_DETACHED); + + (void)memcpy(result->name, name, name_length + 1); + result->link_endpoint = session_create_link_endpoint(session, name); + if (result->link_endpoint == NULL) + { + LogError("Cannot create link endpoint"); + tickcounter_destroy(result->tick_counter); + singlylinkedlist_destroy(result->pending_deliveries); + free(result->name); + free(result); + result = NULL; + } + } + } + } + } + + return result; +} + +LINK_HANDLE link_create_from_endpoint(SESSION_HANDLE session, LINK_ENDPOINT_HANDLE link_endpoint, const char* name, role role, AMQP_VALUE source, AMQP_VALUE target) +{ + LINK_INSTANCE* result = (LINK_INSTANCE*)malloc(sizeof(LINK_INSTANCE)); + if (result == NULL) + { + LogError("Cannot create link"); + } + else + { + result->link_state = LINK_STATE_DETACHED; + result->previous_link_state = LINK_STATE_DETACHED; + result->session = session; + result->handle = 0; + result->snd_settle_mode = sender_settle_mode_unsettled; + result->rcv_settle_mode = receiver_settle_mode_first; + result->delivery_count = 0; + result->initial_delivery_count = 0; + result->max_message_size = 0; + result->max_link_credit = DEFAULT_LINK_CREDIT; + result->peer_max_message_size = 0; + result->is_underlying_session_begun = false; + result->is_closed = false; + result->attach_properties = NULL; + result->received_payload = NULL; + result->received_payload_size = 0; + result->received_delivery_id = 0; + result->source = amqpvalue_clone(target); + result->target = amqpvalue_clone(source); + result->on_link_detach_received_event_subscription.on_link_detach_received = NULL; + result->on_link_detach_received_event_subscription.context = NULL; + + if (role == role_sender) + { + result->role = role_receiver; + } + else + { + result->role = role_sender; + } + + result->tick_counter = tickcounter_create(); + if (result->tick_counter == NULL) + { + LogError("Cannot create tick counter for link"); + free(result); + result = NULL; + } + else + { + result->pending_deliveries = singlylinkedlist_create(); + if (result->pending_deliveries == NULL) + { + LogError("Cannot create pending deliveries list"); + tickcounter_destroy(result->tick_counter); + free(result); + result = NULL; + } + else + { + size_t name_length = strlen(name); + result->name = (char*)malloc(name_length + 1); + if (result->name == NULL) + { + LogError("Cannot allocate memory for link name"); + tickcounter_destroy(result->tick_counter); + singlylinkedlist_destroy(result->pending_deliveries); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->name, name, name_length + 1); + result->on_link_state_changed = NULL; + result->callback_context = NULL; + result->link_endpoint = link_endpoint; + } + } + } + } + + return result; +} + +void link_destroy(LINK_HANDLE link) +{ + if (link == NULL) + { + LogError("NULL link"); + } + else + { + remove_all_pending_deliveries((LINK_INSTANCE*)link, false); + tickcounter_destroy(link->tick_counter); + + link->on_link_state_changed = NULL; + (void)link_detach(link, true, NULL, NULL, NULL); + session_destroy_link_endpoint(link->link_endpoint); + amqpvalue_destroy(link->source); + amqpvalue_destroy(link->target); + + if (link->name != NULL) + { + free(link->name); + } + + if (link->attach_properties != NULL) + { + amqpvalue_destroy(link->attach_properties); + } + + if (link->received_payload != NULL) + { + free(link->received_payload); + } + + free(link); + } +} + +int link_set_snd_settle_mode(LINK_HANDLE link, sender_settle_mode snd_settle_mode) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + link->snd_settle_mode = snd_settle_mode; + result = 0; + } + + return result; +} + +int link_get_snd_settle_mode(LINK_HANDLE link, sender_settle_mode* snd_settle_mode) +{ + int result; + + if ((link == NULL) || + (snd_settle_mode == NULL)) + { + LogError("Bad arguments: link = %p, snd_settle_mode = %p", + link, snd_settle_mode); + result = __FAILURE__; + } + else + { + *snd_settle_mode = link->snd_settle_mode; + + result = 0; + } + + return result; +} + +int link_set_rcv_settle_mode(LINK_HANDLE link, receiver_settle_mode rcv_settle_mode) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + link->rcv_settle_mode = rcv_settle_mode; + result = 0; + } + + return result; +} + +int link_get_rcv_settle_mode(LINK_HANDLE link, receiver_settle_mode* rcv_settle_mode) +{ + int result; + + if ((link == NULL) || + (rcv_settle_mode == NULL)) + { + LogError("Bad arguments: link = %p, rcv_settle_mode = %p", + link, rcv_settle_mode); + result = __FAILURE__; + } + else + { + *rcv_settle_mode = link->rcv_settle_mode; + result = 0; + } + + return result; +} + +int link_set_initial_delivery_count(LINK_HANDLE link, sequence_no initial_delivery_count) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + link->initial_delivery_count = initial_delivery_count; + result = 0; + } + + return result; +} + +int link_get_initial_delivery_count(LINK_HANDLE link, sequence_no* initial_delivery_count) +{ + int result; + + if ((link == NULL) || + (initial_delivery_count == NULL)) + { + LogError("Bad arguments: link = %p, initial_delivery_count = %p", + link, initial_delivery_count); + result = __FAILURE__; + } + else + { + *initial_delivery_count = link->initial_delivery_count; + result = 0; + } + + return result; +} + +int link_set_max_message_size(LINK_HANDLE link, uint64_t max_message_size) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + link->max_message_size = max_message_size; + result = 0; + } + + return result; +} + +int link_get_max_message_size(LINK_HANDLE link, uint64_t* max_message_size) +{ + int result; + + if ((link == NULL) || + (max_message_size == NULL)) + { + LogError("Bad arguments: link = %p, max_message_size = %p", + link, max_message_size); + result = __FAILURE__; + } + else + { + *max_message_size = link->max_message_size; + result = 0; + } + + return result; +} + +int link_get_peer_max_message_size(LINK_HANDLE link, uint64_t* peer_max_message_size) +{ + int result; + + if ((link == NULL) || + (peer_max_message_size == NULL)) + { + LogError("Bad arguments: link = %p, peer_max_message_size = %p", + link, peer_max_message_size); + result = __FAILURE__; + } + else if ((link->link_state != LINK_STATE_ATTACHED) && + (link->link_state != LINK_STATE_HALF_ATTACHED_ATTACH_RECEIVED)) + { + LogError("Attempting to read peer max message size before it was received"); + result = __FAILURE__; + } + else + { + *peer_max_message_size = link->peer_max_message_size; + result = 0; + } + + return result; +} + +int link_set_attach_properties(LINK_HANDLE link, fields attach_properties) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + link->attach_properties = amqpvalue_clone(attach_properties); + if (link->attach_properties == NULL) + { + LogError("Failed cloning attach properties"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +int link_set_max_link_credit(LINK_HANDLE link, uint32_t max_link_credit) +{ + int result; + + if (link == NULL) + { + result = __FAILURE__; + } + else + { + link->max_link_credit = max_link_credit; + result = 0; + } + + return result; +} + +int link_attach(LINK_HANDLE link, ON_TRANSFER_RECEIVED on_transfer_received, ON_LINK_STATE_CHANGED on_link_state_changed, ON_LINK_FLOW_ON on_link_flow_on, void* callback_context) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else if (link->is_closed) + { + LogError("Already attached"); + result = __FAILURE__; + } + else + { + if (!link->is_underlying_session_begun) + { + link->on_link_state_changed = on_link_state_changed; + link->on_transfer_received = on_transfer_received; + link->on_link_flow_on = on_link_flow_on; + link->callback_context = callback_context; + + if (session_begin(link->session) != 0) + { + LogError("Begin session failed"); + result = __FAILURE__; + } + else + { + link->is_underlying_session_begun = true; + + if (session_start_link_endpoint(link->link_endpoint, link_frame_received, on_session_state_changed, on_session_flow_on, link) != 0) + { + LogError("Binding link endpoint to session failed"); + result = __FAILURE__; + } + else + { + link->received_payload_size = 0; + + result = 0; + } + } + } + else + { + result = 0; + } + } + + return result; +} + +int link_detach(LINK_HANDLE link, bool close, const char* error_condition, const char* error_description, AMQP_VALUE info) +{ + int result; + + (void)error_condition; + (void)error_description; + (void)info; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else if (link->is_closed) + { + result = 0; + } + else + { + ERROR_HANDLE error; + + if (error_condition != NULL) + { + error = error_create(error_condition); + if (error == NULL) + { + LogInfo("Cannot create error for detach, detaching without error anyhow"); + } + else + { + if (error_description != NULL) + { + if (error_set_description(error, error_description) != 0) + { + LogInfo("Cannot set error description on detach error, detaching anyhow"); + } + } + + if (info != NULL) + { + if (error_set_info(error, info) != 0) + { + LogInfo("Cannot set info map on detach error, detaching anyhow"); + } + } + } + } + else + { + error = NULL; + } + + switch (link->link_state) + { + case LINK_STATE_HALF_ATTACHED_ATTACH_SENT: + case LINK_STATE_HALF_ATTACHED_ATTACH_RECEIVED: + /* Sending detach when remote is not yet attached */ + if (send_detach(link, close, error) != 0) + { + LogError("Sending detach frame failed"); + result = __FAILURE__; + } + else + { + set_link_state(link, LINK_STATE_DETACHED); + result = 0; + } + break; + + case LINK_STATE_ATTACHED: + /* Send detach and wait for remote to respond */ + if (send_detach(link, close, error) != 0) + { + LogError("Sending detach frame failed"); + result = __FAILURE__; + } + else + { + set_link_state(link, LINK_STATE_HALF_ATTACHED_ATTACH_SENT); + result = 0; + } + break; + + case LINK_STATE_DETACHED: + /* Already detached */ + result = 0; + break; + + default: + case LINK_STATE_ERROR: + /* Already detached and in error state */ + result = __FAILURE__; + break; + } + + if (error != NULL) + { + error_destroy(error); + } + } + + return result; +} + +static bool remove_pending_delivery_condition_function(const void* item, const void* match_context, bool* continue_processing) +{ + bool result; + + if (item == match_context) + { + result = true; + *continue_processing = false; + } + else + { + result = false; + *continue_processing = true; + } + + return result; +} + +static void link_transfer_cancel_handler(ASYNC_OPERATION_HANDLE link_transfer_operation) +{ + DELIVERY_INSTANCE* pending_delivery = GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, link_transfer_operation); + if (pending_delivery->on_delivery_settled != NULL) + { + pending_delivery->on_delivery_settled(pending_delivery->callback_context, pending_delivery->delivery_id, LINK_DELIVERY_SETTLE_REASON_CANCELLED, NULL); + } + + (void)singlylinkedlist_remove_if(((LINK_HANDLE)pending_delivery->link)->pending_deliveries, remove_pending_delivery_condition_function, pending_delivery); + + async_operation_destroy(link_transfer_operation); +} + +ASYNC_OPERATION_HANDLE link_transfer_async(LINK_HANDLE link, message_format message_format, PAYLOAD* payloads, size_t payload_count, ON_DELIVERY_SETTLED on_delivery_settled, void* callback_context, LINK_TRANSFER_RESULT* link_transfer_error, tickcounter_ms_t timeout) +{ + ASYNC_OPERATION_HANDLE result; + + if ((link == NULL) || + (link_transfer_error == NULL)) + { + if (link_transfer_error != NULL) + { + *link_transfer_error = LINK_TRANSFER_ERROR; + } + + LogError("Invalid arguments: link = %p, link_transfer_error = %p", + link, link_transfer_error); + result = NULL; + } + else + { + if (link->role != role_sender) + { + LogError("Link is not a sender link"); + *link_transfer_error = LINK_TRANSFER_ERROR; + result = NULL; + } + else if (link->link_state != LINK_STATE_ATTACHED) + { + LogError("Link is not attached"); + *link_transfer_error = LINK_TRANSFER_ERROR; + result = NULL; + } + else if (link->current_link_credit == 0) + { + *link_transfer_error = LINK_TRANSFER_BUSY; + result = NULL; + } + else + { + result = CREATE_ASYNC_OPERATION(DELIVERY_INSTANCE, link_transfer_cancel_handler); + if (result == NULL) + { + LogError("Error creating async operation"); + *link_transfer_error = LINK_TRANSFER_ERROR; + } + else + { + TRANSFER_HANDLE transfer = transfer_create(0); + if (transfer == NULL) + { + LogError("Error creating transfer"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + sequence_no delivery_count = link->delivery_count + 1; + unsigned char delivery_tag_bytes[sizeof(delivery_count)]; + delivery_tag delivery_tag; + bool settled; + + (void)memcpy(delivery_tag_bytes, &delivery_count, sizeof(delivery_count)); + + delivery_tag.bytes = &delivery_tag_bytes; + delivery_tag.length = sizeof(delivery_tag_bytes); + + if (link->snd_settle_mode == sender_settle_mode_unsettled) + { + settled = false; + } + else + { + settled = true; + } + + if (transfer_set_delivery_tag(transfer, delivery_tag) != 0) + { + LogError("Failed setting delivery tag"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else if (transfer_set_message_format(transfer, message_format) != 0) + { + LogError("Failed setting message format"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else if (transfer_set_settled(transfer, settled) != 0) + { + LogError("Failed setting settled flag"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + AMQP_VALUE transfer_value = amqpvalue_create_transfer(transfer); + if (transfer_value == NULL) + { + LogError("Failed creating transfer performative AMQP value"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + DELIVERY_INSTANCE* pending_delivery = GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, result); + if (pending_delivery == NULL) + { + LogError("Failed getting pending delivery"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + if (tickcounter_get_current_ms(link->tick_counter, &pending_delivery->start_tick) != 0) + { + LogError("Failed getting current tick"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + LIST_ITEM_HANDLE delivery_instance_list_item; + pending_delivery->timeout = timeout; + pending_delivery->on_delivery_settled = on_delivery_settled; + pending_delivery->callback_context = callback_context; + pending_delivery->link = link; + delivery_instance_list_item = singlylinkedlist_add(link->pending_deliveries, result); + + if (delivery_instance_list_item == NULL) + { + LogError("Failed adding delivery to list"); + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + } + else + { + /* here we should feed data to the transfer frame */ + switch (session_send_transfer(link->link_endpoint, transfer, payloads, payload_count, &pending_delivery->delivery_id, (settled) ? on_send_complete : NULL, delivery_instance_list_item)) + { + default: + case SESSION_SEND_TRANSFER_ERROR: + LogError("Failed session send transfer"); + if (singlylinkedlist_remove(link->pending_deliveries, delivery_instance_list_item) != 0) + { + LogError("Error removing pending delivery from the list"); + } + + *link_transfer_error = LINK_TRANSFER_ERROR; + async_operation_destroy(result); + result = NULL; + break; + + case SESSION_SEND_TRANSFER_BUSY: + /* Ensure we remove from list again since sender will attempt to transfer again on flow on */ + LogError("Failed session send transfer"); + if (singlylinkedlist_remove(link->pending_deliveries, delivery_instance_list_item) != 0) + { + LogError("Error removing pending delivery from the list"); + } + + *link_transfer_error = LINK_TRANSFER_BUSY; + async_operation_destroy(result); + result = NULL; + break; + + case SESSION_SEND_TRANSFER_OK: + link->delivery_count = delivery_count; + link->current_link_credit--; + break; + } + } + } + } + + amqpvalue_destroy(transfer_value); + } + } + + transfer_destroy(transfer); + } + } + } + } + + return result; +} + +int link_get_name(LINK_HANDLE link, const char** link_name) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + *link_name = link->name; + result = 0; + } + + return result; +} + +int link_get_received_message_id(LINK_HANDLE link, delivery_number* message_id) +{ + int result; + + if (link == NULL) + { + LogError("NULL link"); + result = __FAILURE__; + } + else + { + *message_id = link->received_delivery_id; + result = 0; + } + + return result; +} + +int link_send_disposition(LINK_HANDLE link, delivery_number message_id, AMQP_VALUE delivery_state) +{ + int result; + + if (delivery_state == NULL) + { + result = 0; + } + else + { + result = send_disposition(link, message_id, delivery_state); + if (result != 0) + { + LogError("Cannot send disposition frame"); + result = __FAILURE__; + } + } + + return result; +} + +void link_dowork(LINK_HANDLE link) +{ + if (link == NULL) + { + LogError("NULL link"); + } + else + { + tickcounter_ms_t current_tick; + + if (tickcounter_get_current_ms(link->tick_counter, ¤t_tick) != 0) + { + LogError("Cannot get tick counter value"); + } + else + { + // go through all and find timed out deliveries + LIST_ITEM_HANDLE item = singlylinkedlist_get_head_item(link->pending_deliveries); + while (item != NULL) + { + LIST_ITEM_HANDLE next_item = singlylinkedlist_get_next_item(item); + ASYNC_OPERATION_HANDLE delivery_instance_async_operation = (ASYNC_OPERATION_HANDLE)singlylinkedlist_item_get_value(item); + DELIVERY_INSTANCE* delivery_instance = (DELIVERY_INSTANCE*)GET_ASYNC_OPERATION_CONTEXT(DELIVERY_INSTANCE, delivery_instance_async_operation); + + if ((delivery_instance->timeout != 0) && + (current_tick - delivery_instance->start_tick >= delivery_instance->timeout)) + { + if (delivery_instance->on_delivery_settled != NULL) + { + delivery_instance->on_delivery_settled(delivery_instance->callback_context, delivery_instance->delivery_id, LINK_DELIVERY_SETTLE_REASON_TIMEOUT, NULL); + } + + if (singlylinkedlist_remove(link->pending_deliveries, item) != 0) + { + LogError("Cannot remove item from list"); + } + + async_operation_destroy(delivery_instance_async_operation); + } + + item = next_item; + } + } + } +} + +ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE link_subscribe_on_link_detach_received(LINK_HANDLE link, ON_LINK_DETACH_RECEIVED on_link_detach_received, void* context) +{ + ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE result; + + if ((link == NULL) || + (on_link_detach_received == NULL)) + { + LogError("Invalid arguments: link = %p, on_link_detach_received = %p, context = %p", + link, on_link_detach_received, context); + result = NULL; + } + else + { + if (link->on_link_detach_received_event_subscription.on_link_detach_received != NULL) + { + LogError("Already subscribed for on_link_detach_received events"); + result = NULL; + } + else + { + link->on_link_detach_received_event_subscription.on_link_detach_received = on_link_detach_received; + link->on_link_detach_received_event_subscription.context = context; + + result = &link->on_link_detach_received_event_subscription; + } + } + + return result; +} + +void link_unsubscribe_on_link_detach_received(ON_LINK_DETACH_EVENT_SUBSCRIPTION_HANDLE event_subscription) +{ + if (event_subscription == NULL) + { + LogError("NULL event_subscription"); + } + else + { + event_subscription->on_link_detach_received = NULL; + event_subscription->context = NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/message.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1450 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/amqpvalue.h" + +typedef struct BODY_AMQP_DATA_TAG +{ + unsigned char* body_data_section_bytes; + size_t body_data_section_length; +} BODY_AMQP_DATA; + +typedef struct MESSAGE_INSTANCE_TAG +{ + BODY_AMQP_DATA* body_amqp_data_items; + size_t body_amqp_data_count; + AMQP_VALUE* body_amqp_sequence_items; + size_t body_amqp_sequence_count; + AMQP_VALUE body_amqp_value; + HEADER_HANDLE header; + delivery_annotations delivery_annotations; + message_annotations message_annotations; + PROPERTIES_HANDLE properties; + application_properties application_properties; + annotations footer; + uint32_t message_format; +} MESSAGE_INSTANCE; + +MESSAGE_BODY_TYPE internal_get_body_type(MESSAGE_HANDLE message) +{ + MESSAGE_BODY_TYPE result; + + if (message->body_amqp_value != NULL) + { + result = MESSAGE_BODY_TYPE_VALUE; + } + else if (message->body_amqp_data_count > 0) + { + result = MESSAGE_BODY_TYPE_DATA; + } + else if (message->body_amqp_sequence_count > 0) + { + result = MESSAGE_BODY_TYPE_SEQUENCE; + } + else + { + result = MESSAGE_BODY_TYPE_NONE; + } + + return result; +} + +static void free_all_body_data_items(MESSAGE_HANDLE message) +{ + size_t i; + + for (i = 0; i < message->body_amqp_data_count; i++) + { + if (message->body_amqp_data_items[i].body_data_section_bytes != NULL) + { + free(message->body_amqp_data_items[i].body_data_section_bytes); + } + } + + if (message->body_amqp_data_items != NULL) + { + free(message->body_amqp_data_items); + } + message->body_amqp_data_count = 0; + message->body_amqp_data_items = NULL; +} + +static void free_all_body_sequence_items(MESSAGE_HANDLE message) +{ + size_t i; + + for (i = 0; i < message->body_amqp_sequence_count; i++) + { + if (message->body_amqp_sequence_items[i] != NULL) + { + /* Codes_SRS_MESSAGE_01_137: [ Each sequence shall be freed by calling `amqpvalue_destroy`. ]*/ + amqpvalue_destroy(message->body_amqp_sequence_items[i]); + } + } + + if (message->body_amqp_sequence_items != NULL) + { + free(message->body_amqp_sequence_items); + } + message->body_amqp_sequence_count = 0; + message->body_amqp_sequence_items = NULL; +} + +MESSAGE_HANDLE message_create(void) +{ + MESSAGE_HANDLE result = (MESSAGE_HANDLE)malloc(sizeof(MESSAGE_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_MESSAGE_01_002: [If allocating memory for the message fails, `message_create` shall fail and return NULL.] */ + LogError("Cannot allocate memory for message"); + } + else + { + result->header = NULL; + result->delivery_annotations = NULL; + result->message_annotations = NULL; + result->properties = NULL; + result->application_properties = NULL; + result->footer = NULL; + result->body_amqp_data_items = NULL; + result->body_amqp_data_count = 0; + result->body_amqp_value = NULL; + result->body_amqp_sequence_items = NULL; + result->body_amqp_sequence_count = 0; + + /* Codes_SRS_MESSAGE_01_135: [ By default a message on which `message_set_message_format` was not called shall have message format set to 0. ]*/ + result->message_format = 0; + } + + /* Codes_SRS_MESSAGE_01_001: [`message_create` shall create a new AMQP message instance and on success it shall return a non-NULL handle for the newly created message instance.] */ + return result; +} + +MESSAGE_HANDLE message_clone(MESSAGE_HANDLE source_message) +{ + MESSAGE_HANDLE result; + + if (source_message == NULL) + { + /* Codes_SRS_MESSAGE_01_062: [If `source_message` is NULL, `message_clone` shall fail and return NULL.] */ + LogError("NULL source_message"); + result = NULL; + } + else + { + /* Codes_SRS_MESSAGE_01_003: [`message_clone` shall clone a message entirely and on success return a non-NULL handle to the cloned message.] */ + result = (MESSAGE_HANDLE)message_create(); + if (result == NULL) + { + /* Codes_SRS_MESSAGE_01_004: [If allocating memory for the new cloned message fails, `message_clone` shall fail and return NULL.] */ + LogError("Cannot clone message"); + } + else + { + result->message_format = source_message->message_format; + + if (source_message->header != NULL) + { + /* Codes_SRS_MESSAGE_01_005: [If a header exists on the source message it shall be cloned by using `header_clone`.] */ + result->header = header_clone(source_message->header); + if (result->header == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone message header"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->delivery_annotations != NULL)) + { + /* Codes_SRS_MESSAGE_01_006: [If delivery annotations exist on the source message they shall be cloned by using `annotations_clone`.] */ + result->delivery_annotations = annotations_clone(source_message->delivery_annotations); + if (result->delivery_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone delivery annotations"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->message_annotations != NULL)) + { + /* Codes_SRS_MESSAGE_01_007: [If message annotations exist on the source message they shall be cloned by using `annotations_clone`.] */ + result->message_annotations = annotations_clone(source_message->message_annotations); + if (result->message_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone message annotations"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->properties != NULL)) + { + /* Codes_SRS_MESSAGE_01_008: [If message properties exist on the source message they shall be cloned by using `properties_clone`.] */ + result->properties = properties_clone(source_message->properties); + if (result->properties == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone message properties"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->application_properties != NULL)) + { + /* Codes_SRS_MESSAGE_01_009: [If application properties exist on the source message they shall be cloned by using `amqpvalue_clone`.] */ + result->application_properties = amqpvalue_clone(source_message->application_properties); + if (result->application_properties == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone application annotations"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->footer != NULL)) + { + /* Codes_SRS_MESSAGE_01_010: [If a footer exists on the source message it shall be cloned by using `annotations_clone`.] */ + result->footer = amqpvalue_clone(source_message->footer); + if (result->footer == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone message footer"); + message_destroy(result); + result = NULL; + } + } + + if ((result != NULL) && (source_message->body_amqp_data_count > 0)) + { + size_t i; + + result->body_amqp_data_items = (BODY_AMQP_DATA*)malloc(source_message->body_amqp_data_count * sizeof(BODY_AMQP_DATA)); + if (result->body_amqp_data_items == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for body data sections"); + message_destroy(result); + result = NULL; + } + else + { + for (i = 0; i < source_message->body_amqp_data_count; i++) + { + result->body_amqp_data_items[i].body_data_section_length = source_message->body_amqp_data_items[i].body_data_section_length; + + /* Codes_SRS_MESSAGE_01_011: [If an AMQP data has been set as message body on the source message it shall be cloned by allocating memory for the binary payload.] */ + result->body_amqp_data_items[i].body_data_section_bytes = (unsigned char*)malloc(source_message->body_amqp_data_items[i].body_data_section_length); + if (result->body_amqp_data_items[i].body_data_section_bytes == NULL) + { + LogError("Cannot allocate memory for body data section %u", (unsigned int)i); + break; + } + else + { + (void)memcpy(result->body_amqp_data_items[i].body_data_section_bytes, source_message->body_amqp_data_items[i].body_data_section_bytes, result->body_amqp_data_items[i].body_data_section_length); + } + } + + result->body_amqp_data_count = i; + if (i < source_message->body_amqp_data_count) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + message_destroy(result); + result = NULL; + } + } + } + + if ((result != NULL) && (source_message->body_amqp_sequence_count > 0)) + { + size_t i; + + result->body_amqp_sequence_items = (AMQP_VALUE*)malloc(source_message->body_amqp_sequence_count * sizeof(AMQP_VALUE)); + if (result->body_amqp_sequence_items == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot allocate memory for body AMQP sequences"); + message_destroy(result); + result = NULL; + } + else + { + for (i = 0; i < source_message->body_amqp_sequence_count; i++) + { + /* Codes_SRS_MESSAGE_01_160: [ If AMQP sequences are set as AMQP body they shall be cloned by calling `amqpvalue_clone`. ] */ + result->body_amqp_sequence_items[i] = amqpvalue_clone(source_message->body_amqp_sequence_items[i]); + if (result->body_amqp_sequence_items[i] == NULL) + { + LogError("Cannot clone AMQP sequence %u", (unsigned int)i); + break; + } + } + + result->body_amqp_sequence_count = i; + if (i < source_message->body_amqp_sequence_count) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + message_destroy(result); + result = NULL; + } + } + } + + if ((result != NULL) && (source_message->body_amqp_value != NULL)) + { + /* Codes_SRS_MESSAGE_01_159: [If an AMQP value has been set as message body on the source message it shall be cloned by calling `amqpvalue_clone`. ]*/ + result->body_amqp_value = amqpvalue_clone(source_message->body_amqp_value); + if (result->body_amqp_value == NULL) + { + /* Codes_SRS_MESSAGE_01_012: [ If any cloning operation for the members of the source message fails, then `message_clone` shall fail and return NULL. ]*/ + LogError("Cannot clone body AMQP value"); + message_destroy(result); + result = NULL; + } + } + } + } + + return result; +} + +void message_destroy(MESSAGE_HANDLE message) +{ + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_014: [ If `message` is NULL, `message_destroy` shall do nothing. ]*/ + LogError("NULL message"); + } + else + { + /* Codes_SRS_MESSAGE_01_013: [ `message_destroy` shall free all resources allocated by the message instance identified by the `message` argument. ]*/ + if (message->header != NULL) + { + /* Codes_SRS_MESSAGE_01_015: [ The message header shall be freed by calling `header_destroy`. ]*/ + header_destroy(message->header); + } + + if (message->delivery_annotations != NULL) + { + /* Codes_SRS_MESSAGE_01_016: [ The delivery annotations shall be freed by calling `annotations_destroy`. ]*/ + annotations_destroy(message->delivery_annotations); + } + + if (message->message_annotations != NULL) + { + /* Codes_SRS_MESSAGE_01_017: [ The message annotations shall be freed by calling `annotations_destroy`. ]*/ + annotations_destroy(message->message_annotations); + } + + if (message->properties != NULL) + { + /* Codes_SRS_MESSAGE_01_018: [ The message properties shall be freed by calling `properties_destroy`. ]*/ + properties_destroy(message->properties); + } + + if (message->application_properties != NULL) + { + /* Codes_SRS_MESSAGE_01_019: [ The application properties shall be freed by calling `amqpvalue_destroy`. ]*/ + application_properties_destroy(message->application_properties); + } + + if (message->footer != NULL) + { + /* Codes_SRS_MESSAGE_01_020: [ The message footer shall be freed by calling `annotations_destroy`. ]*/ + annotations_destroy(message->footer); + } + + if (message->body_amqp_value != NULL) + { + /* Codes_SRS_MESSAGE_01_021: [ If the message body is made of an AMQP value, the value shall be freed by calling `amqpvalue_destroy`. ]*/ + amqpvalue_destroy(message->body_amqp_value); + } + + /* Codes_SRS_MESSAGE_01_136: [ If the message body is made of several AMQP data items, they shall all be freed. ]*/ + free_all_body_data_items(message); + + /* Codes_SRS_MESSAGE_01_136: [ If the message body is made of several AMQP sequences, they shall all be freed. ]*/ + free_all_body_sequence_items(message); + free(message); + } +} + +int message_set_header(MESSAGE_HANDLE message, HEADER_HANDLE header) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_024: [ If `message` is NULL, `message_set_header` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + HEADER_HANDLE new_header; + + if (header == NULL) + { + /* Codes_SRS_MESSAGE_01_139: [ If `message_header` is NULL, the previously stored header associated with `message` shall be freed. ]*/ + if (message->header != NULL) + { + header_destroy(message->header); + message->header = NULL; + } + + /* Codes_SRS_MESSAGE_01_023: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_022: [ `message_set_header` shall copy the contents of `message_header` as the header for the message instance identified by message. ]*/ + /* Codes_SRS_MESSAGE_01_025: [ Cloning the header shall be done by calling `header_clone`. ]*/ + new_header = header_clone(header); + if (new_header == NULL) + { + /* Codes_SRS_MESSAGE_01_026: [ If `header_clone` fails, `message_set_header` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_138: [ If setting the header fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->header != NULL) + { + header_destroy(message->header); + } + + message->header = new_header; + + /* Codes_SRS_MESSAGE_01_023: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_header(MESSAGE_HANDLE message, HEADER_HANDLE* header) +{ + int result; + + if ((message == NULL) || + (header == NULL)) + { + /* Codes_SRS_MESSAGE_01_029: [ If `message` or `message_header` is NULL, `message_get_header` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, header = %p", + message, header); + result = __FAILURE__; + } + else + { + if (message->header == NULL) + { + /* Codes_SRS_MESSAGE_01_143: [ If no header has been set, `message_get_header` shall set `message_header` to NULL. ]*/ + *header = NULL; + + /* Codes_SRS_MESSAGE_01_028: [ On success, `message_get_header` shall return 0.]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_027: [ `message_get_header` shall copy the contents of header for the message instance identified by `message` into the argument `message_header`. ]*/ + /* Codes_SRS_MESSAGE_01_030: [ Cloning the header shall be done by calling `header_clone`. ]*/ + *header = header_clone(message->header); + if (*header == NULL) + { + /* Codes_SRS_MESSAGE_01_031: [ If `header_clone` fails, `message_get_header` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message header"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_028: [ On success, `message_get_header` shall return 0.]*/ + result = 0; + } + } + } + + return result; +} + +int message_set_delivery_annotations(MESSAGE_HANDLE message, delivery_annotations annotations) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_034: [ If `message` is NULL, `message_set_delivery_annotations` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + delivery_annotations new_delivery_annotations; + + if (annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_141: [ If `annotations` is NULL, the previously stored delivery annotations associated with `message` shall be freed. ]*/ + if (message->delivery_annotations != NULL) + { + annotations_destroy(message->delivery_annotations); + message->delivery_annotations = NULL; + } + + /* Codes_SRS_MESSAGE_01_033: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_032: [ `message_set_delivery_annotations` shall copy the contents of `annotations` as the delivery annotations for the message instance identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_035: [ Cloning the delivery annotations shall be done by calling `annotations_clone`. ]*/ + new_delivery_annotations = annotations_clone(annotations); + if (new_delivery_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_036: [ If `annotations_clone` fails, `message_set_delivery_annotations` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone delivery annotations"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_140: [** If setting the delivery annotations fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->delivery_annotations != NULL) + { + annotations_destroy(message->delivery_annotations); + } + + message->delivery_annotations = new_delivery_annotations; + + /* Codes_SRS_MESSAGE_01_033: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_delivery_annotations(MESSAGE_HANDLE message, delivery_annotations* annotations) +{ + int result; + + if ((message == NULL) || + (annotations == NULL)) + { + /* Codes_SRS_MESSAGE_01_039: [ If `message` or `annotations` is NULL, `message_get_delivery_annotations` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, annotations = %p", + message, annotations); + result = __FAILURE__; + } + else + { + if (message->delivery_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_142: [ If no delivery annotations have been set, `message_get_delivery_annotations` shall set `annotations` to NULL. ]*/ + *annotations = NULL; + + /* Codes_SRS_MESSAGE_01_038: [ On success, `message_get_delivery_annotations` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_037: [ `message_get_delivery_annotations` shall copy the contents of delivery annotations for the message instance identified by `message` into the argument `annotations`. ]*/ + /* Codes_SRS_MESSAGE_01_040: [ Cloning the delivery annotations shall be done by calling `annotations_clone`. ]*/ + *annotations = annotations_clone(message->delivery_annotations); + if (*annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_041: [ If `annotations_clone` fails, `message_get_delivery_annotations` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone delivery annotations"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_038: [ On success, `message_get_delivery_annotations` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_set_message_annotations(MESSAGE_HANDLE message, message_annotations annotations) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_044: [ If `message` is NULL, `message_set_message_annotations` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + if (annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_145: [ If `annotations` is NULL, the previously stored message annotations associated with `message` shall be freed. ]*/ + if (message->message_annotations != NULL) + { + annotations_destroy(message->message_annotations); + message->message_annotations = NULL; + } + + /* Codes_SRS_MESSAGE_01_043: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + message_annotations new_message_annotations; + + /* Codes_SRS_MESSAGE_01_042: [ `message_set_message_annotations` shall copy the contents of `annotations` as the message annotations for the message instance identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_045: [ Cloning the message annotations shall be done by calling `annotations_clone`. ]*/ + new_message_annotations = annotations_clone(annotations); + if (new_message_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_046: [ If `annotations_clone` fails, `message_set_message_annotations` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message annotations"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_144: [ If setting the message annotations fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->message_annotations != NULL) + { + annotations_destroy(message->message_annotations); + } + + message->message_annotations = new_message_annotations; + + /* Codes_SRS_MESSAGE_01_043: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_message_annotations(MESSAGE_HANDLE message, annotations* message_annotations) +{ + int result; + + if ((message == NULL) || + (message_annotations == NULL)) + { + /* Codes_SRS_MESSAGE_01_049: [ If `message` or `annotations` is NULL, `message_get_message_annotations` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, message_annotations = %p", + message, message_annotations); + result = __FAILURE__; + } + else + { + if (message->message_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_146: [ If no message annotations have been set, `message_get_message_annotations` shall set `annotations` to NULL. ]*/ + *message_annotations = NULL; + + /* Codes_SRS_MESSAGE_01_048: [ On success, `message_get_message_annotations` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_047: [ `message_get_message_annotations` shall copy the contents of message annotations for the message instance identified by `message` into the argument `annotations`. ]*/ + /* Codes_SRS_MESSAGE_01_050: [ Cloning the message annotations shall be done by calling `annotations_clone`. ]*/ + *message_annotations = annotations_clone(message->message_annotations); + if (*message_annotations == NULL) + { + /* Codes_SRS_MESSAGE_01_051: [ If `annotations_clone` fails, `message_get_message_annotations` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message annotations"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_048: [ On success, `message_get_message_annotations` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_set_properties(MESSAGE_HANDLE message, PROPERTIES_HANDLE properties) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_054: [ If `message` is NULL, `message_set_properties` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + if (properties == NULL) + { + /* Codes_SRS_MESSAGE_01_147: [ If `properties` is NULL, the previously stored message properties associated with `message` shall be freed. ]*/ + if (message->properties != NULL) + { + properties_destroy(message->properties); + message->properties = NULL; + } + + /* Codes_SRS_MESSAGE_01_053: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + PROPERTIES_HANDLE new_properties; + + /* Codes_SRS_MESSAGE_01_052: [ `message_set_properties` shall copy the contents of `properties` as the message properties for the message instance identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_055: [ Cloning the message properties shall be done by calling `properties_clone`. ]*/ + new_properties = properties_clone(properties); + if (new_properties == NULL) + { + /* Codes_SRS_MESSAGE_01_056: [ If `properties_clone` fails, `message_set_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_063: [ If setting the message properties fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->properties != NULL) + { + properties_destroy(message->properties); + } + + message->properties = new_properties; + + /* Codes_SRS_MESSAGE_01_053: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_properties(MESSAGE_HANDLE message, PROPERTIES_HANDLE* properties) +{ + int result; + + if ((message == NULL) || + (properties == NULL)) + { + /* Codes_SRS_MESSAGE_01_059: [ If `message` or `properties` is NULL, `message_get_properties` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, properties = %p", + message, properties); + result = __FAILURE__; + } + else + { + if (message->properties == NULL) + { + /* Codes_SRS_MESSAGE_01_148: [ If no message properties have been set, `message_get_properties` shall set `properties` to NULL. ]*/ + *properties = NULL; + + /* Codes_SRS_MESSAGE_01_058: [ On success, `message_get_properties` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_057: [ `message_get_properties` shall copy the contents of message properties for the message instance identified by `message` into the argument `properties`. ]*/ + /* Codes_SRS_MESSAGE_01_060: [ Cloning the message properties shall be done by calling `properties_clone`. ]*/ + *properties = properties_clone(message->properties); + if (*properties == NULL) + { + /* Codes_SRS_MESSAGE_01_061: [ If `properties_clone` fails, `message_get_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_058: [ On success, `message_get_properties` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_set_application_properties(MESSAGE_HANDLE message, AMQP_VALUE application_properties) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_066: [ If `message` is NULL, `message_set_application_properties` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + AMQP_VALUE new_application_properties; + + /* Tests_SRS_MESSAGE_01_149: [ If `application_properties` is NULL, the previously stored application properties associated with `message` shall be freed. ]*/ + if (application_properties == NULL) + { + if (message->application_properties != NULL) + { + amqpvalue_destroy(message->application_properties); + message->application_properties = NULL; + } + + /* Codes_SRS_MESSAGE_01_065: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_064: [ `message_set_application_properties` shall copy the contents of `application_properties` as the application properties for the message instance identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_067: [ Cloning the message properties shall be done by calling `application_properties_clone`. ]*/ + new_application_properties = application_properties_clone(application_properties); + if (new_application_properties == NULL) + { + /* Codes_SRS_MESSAGE_01_068: [ If `application_properties_clone` fails, `message_set_application_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone application properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_069: [ If setting the application properties fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->application_properties != NULL) + { + amqpvalue_destroy(message->application_properties); + } + + message->application_properties = new_application_properties; + + /* Codes_SRS_MESSAGE_01_065: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_application_properties(MESSAGE_HANDLE message, AMQP_VALUE* application_properties) +{ + int result; + + if ((message == NULL) || + (application_properties == NULL)) + { + /* Codes_SRS_MESSAGE_01_072: [ If `message` or `application_properties` is NULL, `message_get_application_properties` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, application_properties = %p", + message, application_properties); + result = __FAILURE__; + } + else + { + if (message->application_properties == NULL) + { + /* Codes_SRS_MESSAGE_01_150: [ If no application properties have been set, `message_get_application_properties` shall set `application_properties` to NULL. ]*/ + *application_properties = NULL; + + /* Codes_SRS_MESSAGE_01_071: [ On success, `message_get_application_properties` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_070: [ `message_get_application_properties` shall copy the contents of application message properties for the message instance identified by `message` into the argument `application_properties`. ]*/ + /* Codes_SRS_MESSAGE_01_073: [ Cloning the application properties shall be done by calling `application_properties_clone`. ]*/ + *application_properties = application_properties_clone(message->application_properties); + if (*application_properties == NULL) + { + /* Codes_SRS_MESSAGE_01_074: [ If `application_properties_clone` fails, `message_get_application_properties` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone application properties"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_071: [ On success, `message_get_application_properties` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_set_footer(MESSAGE_HANDLE message, annotations footer) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_077: [ If `message` is NULL, `message_set_footer` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + if (footer == NULL) + { + /* Codes_SRS_MESSAGE_01_151: [ If `footer` is NULL, the previously stored footer associated with `message` shall be freed. ]*/ + if (message->footer != NULL) + { + annotations_destroy(message->footer); + message->footer = NULL; + } + + /* Codes_SRS_MESSAGE_01_076: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + annotations new_footer; + + /* Codes_SRS_MESSAGE_01_075: [ `message_set_footer` shall copy the contents of `footer` as the footer contents for the message instance identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_078: [ Cloning the footer shall be done by calling `annotations_clone`. ]*/ + new_footer = annotations_clone(footer); + if (new_footer == NULL) + { + /* Codes_SRS_MESSAGE_01_079: [ If `annotations_clone` fails, `message_set_footer` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message footer"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_080: [ If setting the footer fails, the previous value shall be preserved. ]*/ + /* Only do the free of the previous value if we could clone the new one*/ + if (message->footer != NULL) + { + annotations_destroy(message->footer); + } + + message->footer = new_footer; + + /* Codes_SRS_MESSAGE_01_076: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_footer(MESSAGE_HANDLE message, annotations* footer) +{ + int result; + + if ((message == NULL) || + (footer == NULL)) + { + /* Codes_SRS_MESSAGE_01_083: [ If `message` or `footer` is NULL, `message_get_footer` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, footer = %p", + message, footer); + result = __FAILURE__; + } + else + { + if (message->footer == NULL) + { + /* Codes_SRS_MESSAGE_01_152: [ If no footer has been set, `message_get_footer` shall set `footer` to NULL. ]*/ + *footer = NULL; + + /* Codes_SRS_MESSAGE_01_082: [ On success, `message_get_footer` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_MESSAGE_01_081: [ `message_get_footer` shall copy the contents of footer for the message instance identified by `message` into the argument `footer`. ]*/ + /* Codes_SRS_MESSAGE_01_084: [ Cloning the footer shall be done by calling `annotations_clone`. ]*/ + *footer = annotations_clone(message->footer); + if (*footer == NULL) + { + /* Codes_SRS_MESSAGE_01_085: [ If `annotations_clone` fails, `message_get_footer` shall fail and return a non-zero value. ]*/ + LogError("Cannot clone message footer"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_082: [ On success, `message_get_footer` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_add_body_amqp_data(MESSAGE_HANDLE message, BINARY_DATA amqp_data) +{ + int result; + + /* Codes_SRS_MESSAGE_01_088: [ If `message` is NULL, `message_add_body_amqp_data` shall fail and return a non-zero value. ]*/ + if ((message == NULL) || + /* Tests_SRS_MESSAGE_01_089: [ If the `bytes` member of `amqp_data` is NULL and the `size` member is non-zero, `message_add_body_amqp_data` shall fail and return a non-zero value. ]*/ + ((amqp_data.bytes == NULL) && + (amqp_data.length != 0))) + { + LogError("Bad arguments: message = %p, bytes = %p, length = %u", + message, amqp_data.bytes, (unsigned int)amqp_data.length); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if ((body_type == MESSAGE_BODY_TYPE_SEQUENCE) || + (body_type == MESSAGE_BODY_TYPE_VALUE)) + { + /* Codes_SRS_MESSAGE_01_091: [ If the body was already set to an AMQP value or a list of AMQP sequences, `message_add_body_amqp_data` shall fail and return a non-zero value. ]*/ + LogError("Body type already set"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_086: [ `message_add_body_amqp_data` shall add the contents of `amqp_data` to the list of AMQP data values for the body of the message identified by `message`. ]*/ + BODY_AMQP_DATA* new_body_amqp_data_items = (BODY_AMQP_DATA*)realloc(message->body_amqp_data_items, sizeof(BODY_AMQP_DATA) * (message->body_amqp_data_count + 1)); + if (new_body_amqp_data_items == NULL) + { + /* Codes_SRS_MESSAGE_01_153: [ If allocating memory to store the added AMQP data fails, `message_add_body_amqp_data` shall fail and return a non-zero value. ]*/ + LogError("Cannot allocate memory for body AMQP data items"); + result = __FAILURE__; + } + else + { + message->body_amqp_data_items = new_body_amqp_data_items; + + if (amqp_data.length == 0) + { + message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_bytes = NULL; + message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_length = 0; + message->body_amqp_data_count++; + + /* Codes_SRS_MESSAGE_01_087: [ On success it shall return 0. ]*/ + result = 0; + } + else + { + message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_bytes = (unsigned char*)malloc(amqp_data.length); + if (message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_bytes == NULL) + { + /* Codes_SRS_MESSAGE_01_153: [ If allocating memory to store the added AMQP data fails, `message_add_body_amqp_data` shall fail and return a non-zero value. ]*/ + LogError("Cannot allocate memory for body AMQP data to be added"); + result = __FAILURE__; + } + else + { + message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_length = amqp_data.length; + (void)memcpy(message->body_amqp_data_items[message->body_amqp_data_count].body_data_section_bytes, amqp_data.bytes, amqp_data.length); + message->body_amqp_data_count++; + + /* Codes_SRS_MESSAGE_01_087: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + } + } + + return result; +} + +int message_get_body_amqp_data_in_place(MESSAGE_HANDLE message, size_t index, BINARY_DATA* amqp_data) +{ + int result; + + if ((message == NULL) || + (amqp_data == NULL)) + { + /* Codes_SRS_MESSAGE_01_094: [ If `message` or `amqp_data` is NULL, `message_get_body_amqp_data_in_place` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, amqp_data = %p", + message, amqp_data); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if (body_type != MESSAGE_BODY_TYPE_DATA) + { + /* Codes_SRS_MESSAGE_01_096: [ If the body for `message` is not of type `MESSAGE_BODY_TYPE_DATA`, `message_get_body_amqp_data_in_place` shall fail and return a non-zero value. ]*/ + LogError("Body type is not AMQP data"); + result = __FAILURE__; + } + else if (index >= message->body_amqp_data_count) + { + /* Codes_SRS_MESSAGE_01_095: [ If `index` indicates an AMQP data entry that is out of bounds, `message_get_body_amqp_data_in_place` shall fail and return a non-zero value. ]*/ + LogError("Index too high for AMQP data (%u), number of AMQP data entries is %u", + index, message->body_amqp_data_count); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_092: [ `message_get_body_amqp_data_in_place` shall place the contents of the `index`th AMQP data for the message instance identified by `message` into the argument `amqp_data`, without copying the binary payload memory. ]*/ + amqp_data->bytes = message->body_amqp_data_items[index].body_data_section_bytes; + amqp_data->length = message->body_amqp_data_items[index].body_data_section_length; + + /* Codes_SRS_MESSAGE_01_093: [ On success, `message_get_body_amqp_data_in_place` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int message_get_body_amqp_data_count(MESSAGE_HANDLE message, size_t* count) +{ + int result; + + if ((message == NULL) || + (count == NULL)) + { + /* Codes_SRS_MESSAGE_01_099: [ If `message` or `count` is NULL, `message_get_body_amqp_data_count` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, count = %p", + message, count); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if (body_type != MESSAGE_BODY_TYPE_DATA) + { + /* Codes_SRS_MESSAGE_01_100: [ If the body for `message` is not of type `MESSAGE_BODY_TYPE_DATA`, `message_get_body_amqp_data_count` shall fail and return a non-zero value. ]*/ + LogError("Body type is not AMQP data"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_097: [ `message_get_body_amqp_data_count` shall fill in `count` the number of AMQP data chunks that are stored by the message identified by `message`. ]*/ + *count = message->body_amqp_data_count; + + /* Codes_SRS_MESSAGE_01_098: [ On success, `message_get_body_amqp_data_count` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int message_set_body_amqp_value(MESSAGE_HANDLE message, AMQP_VALUE body_amqp_value) +{ + int result; + + if ((message == NULL) || + (body_amqp_value == NULL)) + { + /* Codes_SRS_MESSAGE_01_103: [ If `message` or `body_amqp_value` is NULL, `message_set_body_amqp_value` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, body_amqp_value = %p", + message, body_amqp_value); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if ((body_type == MESSAGE_BODY_TYPE_DATA) || + (body_type == MESSAGE_BODY_TYPE_SEQUENCE)) + { + /* Codes_SRS_MESSAGE_01_105: [ If the body was already set to an AMQP data list or a list of AMQP sequences, `message_set_body_amqp_value` shall fail and return a non-zero value. ]*/ + LogError("Body is already set to another body type"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_154: [ Cloning the amqp value shall be done by calling `amqpvalue_clone`. ]*/ + AMQP_VALUE new_amqp_value = amqpvalue_clone(body_amqp_value); + if (new_amqp_value == NULL) + { + LogError("Cannot clone body AMQP value", + message, body_amqp_value); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_104: [ If setting the body AMQP value fails, the previous value shall be preserved. ]*/ + /* Only free the previous value when cloning is succesfull */ + if (message->body_amqp_value != NULL) + { + amqpvalue_destroy(body_amqp_value); + } + + /* Codes_SRS_MESSAGE_01_101: [ `message_set_body_amqp_value` shall set the contents of body as being the AMQP value indicate by `body_amqp_value`. ]*/ + message->body_amqp_value = new_amqp_value; + + /* Codes_SRS_MESSAGE_01_102: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_body_amqp_value_in_place(MESSAGE_HANDLE message, AMQP_VALUE* body_amqp_value) +{ + int result; + + if ((message == NULL) || + (body_amqp_value == NULL)) + { + /* Codes_SRS_MESSAGE_01_108: [ If `message` or `body_amqp_value` is NULL, `message_get_body_amqp_value_in_place` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, body_amqp_value = %p", + message, body_amqp_value); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if (body_type != MESSAGE_BODY_TYPE_VALUE) + { + /* Codes_SRS_MESSAGE_01_109: [ If the body for `message` is not of type `MESSAGE_BODY_TYPE_VALUE`, `message_get_body_amqp_value_in_place` shall fail and return a non-zero value. ]*/ + LogError("Body is not of type AMQP value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_106: [ `message_get_body_amqp_value_in_place` shall get the body AMQP value for the message instance identified by `message` in place (not cloning) into the `body_amqp_value` argument. ]*/ + *body_amqp_value = message->body_amqp_value; + + /* Codes_SRS_MESSAGE_01_107: [ On success, `message_get_body_amqp_value_in_place` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int message_add_body_amqp_sequence(MESSAGE_HANDLE message, AMQP_VALUE sequence_list) +{ + int result; + + if ((message == NULL) || + (sequence_list == NULL)) + { + /* Codes_SRS_MESSAGE_01_112: [ If `message` or `sequence` is NULL, `message_add_body_amqp_sequence` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, sequence_list = %p", + message, sequence_list); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if ((body_type == MESSAGE_BODY_TYPE_DATA) || + (body_type == MESSAGE_BODY_TYPE_VALUE)) + { + /* Codes_SRS_MESSAGE_01_115: [ If the body was already set to an AMQP data list or an AMQP value, `message_add_body_amqp_sequence` shall fail and return a non-zero value. ]*/ + LogError("Body is already set to another body type"); + result = __FAILURE__; + } + else + { + AMQP_VALUE* new_body_amqp_sequence_items = (AMQP_VALUE*)realloc(message->body_amqp_sequence_items, sizeof(AMQP_VALUE) * (message->body_amqp_sequence_count + 1)); + if (new_body_amqp_sequence_items == NULL) + { + /* Codes_SRS_MESSAGE_01_158: [ If allocating memory in order to store the sequence fails, `message_add_body_amqp_sequence` shall fail and return a non-zero value. ]*/ + LogError("Cannot allocate enough memory for sequence items"); + result = __FAILURE__; + } + else + { + message->body_amqp_sequence_items = new_body_amqp_sequence_items; + + /* Codes_SRS_MESSAGE_01_110: [ `message_add_body_amqp_sequence` shall add the contents of `sequence` to the list of AMQP sequences for the body of the message identified by `message`. ]*/ + /* Codes_SRS_MESSAGE_01_156: [ The AMQP sequence shall be cloned by calling `amqpvalue_clone`. ]*/ + message->body_amqp_sequence_items[message->body_amqp_sequence_count] = amqpvalue_clone(sequence_list); + if (message->body_amqp_sequence_items[message->body_amqp_sequence_count] == NULL) + { + /* Codes_SRS_MESSAGE_01_157: [ If `amqpvalue_clone` fails, `message_add_body_amqp_sequence` shall fail and return a non-zero value. ]*/ + LogError("Cloning sequence failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_114: [ If adding the AMQP sequence fails, the previous value shall be preserved. ]*/ + message->body_amqp_sequence_count++; + + /* Codes_SRS_MESSAGE_01_111: [ On success it shall return 0. ]*/ + result = 0; + } + } + } + } + + return result; +} + +int message_get_body_amqp_sequence_in_place(MESSAGE_HANDLE message, size_t index, AMQP_VALUE* sequence) +{ + int result; + + if ((message == NULL) || + (sequence == NULL)) + { + /* Codes_SRS_MESSAGE_01_118: [ If `message` or `sequence` is NULL, `message_get_body_amqp_sequence_in_place` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, sequence = %p", + message, sequence); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if (body_type != MESSAGE_BODY_TYPE_SEQUENCE) + { + /* Codes_SRS_MESSAGE_01_120: [ If the body for `message` is not of type `MESSAGE_BODY_TYPE_SEQUENCE`, `message_get_body_amqp_sequence_in_place` shall fail and return a non-zero value. ]*/ + LogError("Body is not of type SEQUENCE"); + result = __FAILURE__; + } + else + { + if (index >= message->body_amqp_sequence_count) + { + /* Codes_SRS_MESSAGE_01_119: [ If `index` indicates an AMQP sequence entry that is out of bounds, `message_get_body_amqp_sequence_in_place` shall fail and return a non-zero value. ]*/ + LogError("Index too high for AMQP sequence (%u), maximum is %u", + index, message->body_amqp_sequence_count); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_116: [ `message_get_body_amqp_sequence_in_place` shall return in `sequence` the content of the `index`th AMQP seuquence entry for the message instance identified by `message`. ]*/ + *sequence = message->body_amqp_sequence_items[index]; + + /* Codes_SRS_MESSAGE_01_117: [ On success, `message_get_body_amqp_sequence_in_place` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +int message_get_body_amqp_sequence_count(MESSAGE_HANDLE message, size_t* count) +{ + int result; + + if ((message == NULL) || + (count == NULL)) + { + /* Codes_SRS_MESSAGE_01_123: [ If `message` or `count` is NULL, `message_get_body_amqp_sequence_count` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, count = %p", + message, count); + result = __FAILURE__; + } + else + { + MESSAGE_BODY_TYPE body_type = internal_get_body_type(message); + if (body_type != MESSAGE_BODY_TYPE_SEQUENCE) + { + /* Codes_SRS_MESSAGE_01_124: [ If the body for `message` is not of type `MESSAGE_BODY_TYPE_SEQUENCE`, `message_get_body_amqp_sequence_count` shall fail and return a non-zero value. ]*/ + LogError("Body is not of type SEQUENCE"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_121: [ `message_get_body_amqp_sequence_count` shall fill in `count` the number of AMQP sequences that are stored by the message identified by `message`. ]*/ + *count = message->body_amqp_sequence_count; + + /* Codes_SRS_MESSAGE_01_122: [ On success, `message_get_body_amqp_sequence_count` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int message_get_body_type(MESSAGE_HANDLE message, MESSAGE_BODY_TYPE* body_type) +{ + int result; + + if ((message == NULL) || + (body_type == NULL)) + { + /* Codes_SRS_MESSAGE_01_127: [ If `message` or `body_type` is NULL, `message_get_body_type` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, body_type = %p", + message, body_type); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_125: [ `message_get_body_type` shall fill in `body_type` the AMQP message body type. ]*/ + if (message->body_amqp_value != NULL) + { + *body_type = MESSAGE_BODY_TYPE_VALUE; + } + else if (message->body_amqp_data_count > 0) + { + *body_type = MESSAGE_BODY_TYPE_DATA; + } + else if (message->body_amqp_sequence_count > 0) + { + *body_type = MESSAGE_BODY_TYPE_SEQUENCE; + } + else + { + /* Codes_SRS_MESSAGE_01_128: [ If no body has been set on the message, `body_type` shall be `MESSAGE_BODY_TYPE_NONE`. ]*/ + *body_type = MESSAGE_BODY_TYPE_NONE; + } + + /* Codes_SRS_MESSAGE_01_126: [ On success, `message_get_body_type` shall return 0. ]*/ + result = 0; + } + + return result; +} + +int message_set_message_format(MESSAGE_HANDLE message, uint32_t message_format) +{ + int result; + + if (message == NULL) + { + /* Codes_SRS_MESSAGE_01_131: [ If `message` is NULL, `message_set_message_format` shall fail and return a non-zero value. ]*/ + LogError("NULL message"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_129: [ `message_set_message_format` shall set the message format for the message identified by `message`. ]*/ + message->message_format = message_format; + + /* Codes_SRS_MESSAGE_01_130: [ On success, `message_set_message_format` shall return 0. ]*/ + result = 0; + } + + return result; +} + +int message_get_message_format(MESSAGE_HANDLE message, uint32_t *message_format) +{ + int result; + + if ((message == NULL) || + (message_format == NULL)) + { + /* Codes_SRS_MESSAGE_01_134: [ If `message` or `message_format` is NULL, `message_get_message_format` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: message = %p, message_format = %p", + message, message_format); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MESSAGE_01_132: [ `message_get_message_format` shall get the message format for the message identified by `message` and return it in the `message_fomrat` argument. ]*/ + *message_format = message->message_format; + + /* Codes_SRS_MESSAGE_01_133: [ On success, `message_get_message_format` shall return 0. ]*/ + result = 0; + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/message_receiver.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,530 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> + +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/message_receiver.h" +#include "azure_uamqp_c/amqpvalue.h" + +typedef struct MESSAGE_RECEIVER_INSTANCE_TAG +{ + LINK_HANDLE link; + ON_MESSAGE_RECEIVED on_message_received; + ON_MESSAGE_RECEIVER_STATE_CHANGED on_message_receiver_state_changed; + MESSAGE_RECEIVER_STATE message_receiver_state; + const void* on_message_receiver_state_changed_context; + const void* callback_context; + MESSAGE_HANDLE decoded_message; + bool decode_error; +} MESSAGE_RECEIVER_INSTANCE; + +static void set_message_receiver_state(MESSAGE_RECEIVER_INSTANCE* message_receiver, MESSAGE_RECEIVER_STATE new_state) +{ + MESSAGE_RECEIVER_STATE previous_state = message_receiver->message_receiver_state; + message_receiver->message_receiver_state = new_state; + if (message_receiver->on_message_receiver_state_changed != NULL) + { + message_receiver->on_message_receiver_state_changed(message_receiver->on_message_receiver_state_changed_context, new_state, previous_state); + } +} + +static void decode_message_value_callback(void* context, AMQP_VALUE decoded_value) +{ + MESSAGE_RECEIVER_INSTANCE* message_receiver = (MESSAGE_RECEIVER_INSTANCE*)context; + MESSAGE_HANDLE decoded_message = message_receiver->decoded_message; + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(decoded_value); + + if (is_application_properties_type_by_descriptor(descriptor)) + { + if (message_set_application_properties(decoded_message, decoded_value) != 0) + { + LogError("Error setting application properties on received message"); + message_receiver->decode_error = true; + } + } + else if (is_properties_type_by_descriptor(descriptor)) + { + PROPERTIES_HANDLE properties; + if (amqpvalue_get_properties(decoded_value, &properties) != 0) + { + LogError("Error getting message properties"); + message_receiver->decode_error = true; + } + else + { + if (message_set_properties(decoded_message, properties) != 0) + { + LogError("Error setting message properties on received message"); + message_receiver->decode_error = true; + } + + properties_destroy(properties); + } + } + else if (is_delivery_annotations_type_by_descriptor(descriptor)) + { + annotations delivery_annotations = amqpvalue_get_inplace_described_value(decoded_value); + if (delivery_annotations == NULL) + { + LogError("Error getting delivery annotations"); + message_receiver->decode_error = true; + } + else + { + if (message_set_delivery_annotations(decoded_message, delivery_annotations) != 0) + { + LogError("Error setting delivery annotations on received message"); + message_receiver->decode_error = true; + } + } + } + else if (is_message_annotations_type_by_descriptor(descriptor)) + { + annotations message_annotations = amqpvalue_get_inplace_described_value(decoded_value); + if (message_annotations == NULL) + { + LogError("Error getting message annotations"); + message_receiver->decode_error = true; + } + else + { + if (message_set_message_annotations(decoded_message, message_annotations) != 0) + { + LogError("Error setting message annotations on received message"); + message_receiver->decode_error = true; + } + } + } + else if (is_header_type_by_descriptor(descriptor)) + { + HEADER_HANDLE header; + if (amqpvalue_get_header(decoded_value, &header) != 0) + { + LogError("Error getting message header"); + message_receiver->decode_error = true; + } + else + { + if (message_set_header(decoded_message, header) != 0) + { + LogError("Error setting message header on received message"); + message_receiver->decode_error = true; + } + + header_destroy(header); + } + } + else if (is_footer_type_by_descriptor(descriptor)) + { + annotations footer = amqpvalue_get_inplace_described_value(decoded_value); + if (footer == NULL) + { + LogError("Error getting message footer"); + message_receiver->decode_error = true; + } + else + { + if (message_set_footer(decoded_message, footer) != 0) + { + LogError("Error setting message footer on received message"); + message_receiver->decode_error = true; + } + } + } + else if (is_amqp_value_type_by_descriptor(descriptor)) + { + MESSAGE_BODY_TYPE body_type; + if (message_get_body_type(decoded_message, &body_type) != 0) + { + LogError("Error getting message body type"); + message_receiver->decode_error = true; + } + else + { + if (body_type != MESSAGE_BODY_TYPE_NONE) + { + LogError("Body already set on received message"); + message_receiver->decode_error = true; + } + else + { + AMQP_VALUE body_amqp_value = amqpvalue_get_inplace_described_value(decoded_value); + if (body_amqp_value == NULL) + { + LogError("Error getting body AMQP value"); + message_receiver->decode_error = true; + } + else + { + if (message_set_body_amqp_value(decoded_message, body_amqp_value) != 0) + { + LogError("Error setting body AMQP value on received message"); + message_receiver->decode_error = true; + } + } + } + } + } + else if (is_data_type_by_descriptor(descriptor)) + { + MESSAGE_BODY_TYPE body_type; + if (message_get_body_type(decoded_message, &body_type) != 0) + { + LogError("Error getting message body type"); + message_receiver->decode_error = true; + } + else + { + if ((body_type != MESSAGE_BODY_TYPE_NONE) && + (body_type != MESSAGE_BODY_TYPE_DATA)) + { + LogError("Message body type already set to something different than AMQP DATA"); + message_receiver->decode_error = true; + } + else + { + AMQP_VALUE body_data_value = amqpvalue_get_inplace_described_value(decoded_value); + if (body_data_value == NULL) + { + LogError("Error getting body DATA value"); + message_receiver->decode_error = true; + } + else + { + data data_value; + if (amqpvalue_get_data(body_data_value, &data_value) != 0) + { + LogError("Error getting body DATA AMQP value"); + message_receiver->decode_error = true; + } + else + { + BINARY_DATA binary_data; + binary_data.bytes = (const unsigned char*)data_value.bytes; + binary_data.length = data_value.length; + if (message_add_body_amqp_data(decoded_message, binary_data) != 0) + { + LogError("Error adding body DATA to received message"); + message_receiver->decode_error = true; + } + } + } + } + } + } +} + +static AMQP_VALUE on_transfer_received(void* context, TRANSFER_HANDLE transfer, uint32_t payload_size, const unsigned char* payload_bytes) +{ + AMQP_VALUE result = NULL; + MESSAGE_RECEIVER_INSTANCE* message_receiver = (MESSAGE_RECEIVER_INSTANCE*)context; + + (void)transfer; + if (message_receiver->on_message_received != NULL) + { + MESSAGE_HANDLE message = message_create(); + if (message == NULL) + { + LogError("Cannot create message"); + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + AMQPVALUE_DECODER_HANDLE amqpvalue_decoder = amqpvalue_decoder_create(decode_message_value_callback, message_receiver); + if (amqpvalue_decoder == NULL) + { + LogError("Cannot create AMQP value decoder"); + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + message_receiver->decoded_message = message; + message_receiver->decode_error = false; + if (amqpvalue_decode_bytes(amqpvalue_decoder, payload_bytes, payload_size) != 0) + { + LogError("Cannot decode bytes"); + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + if (message_receiver->decode_error) + { + LogError("Error decoding message"); + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + result = message_receiver->on_message_received(message_receiver->callback_context, message); + } + } + + amqpvalue_decoder_destroy(amqpvalue_decoder); + } + + message_destroy(message); + } + } + + return result; +} + +static void on_link_state_changed(void* context, LINK_STATE new_link_state, LINK_STATE previous_link_state) +{ + MESSAGE_RECEIVER_INSTANCE* message_receiver = (MESSAGE_RECEIVER_INSTANCE*)context; + (void)previous_link_state; + + switch (new_link_state) + { + default: + break; + + case LINK_STATE_ATTACHED: + if (message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_OPENING) + { + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_OPEN); + } + break; + case LINK_STATE_DETACHED: + if ((message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_OPEN) || + (message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_CLOSING)) + { + /* User initiated transition, we should be good */ + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_IDLE); + } + else if (message_receiver->message_receiver_state != MESSAGE_RECEIVER_STATE_IDLE) + { + /* Any other transition must be an error */ + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + break; + case LINK_STATE_ERROR: + if (message_receiver->message_receiver_state != MESSAGE_RECEIVER_STATE_ERROR) + { + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + break; + } +} + +MESSAGE_RECEIVER_HANDLE messagereceiver_create(LINK_HANDLE link, ON_MESSAGE_RECEIVER_STATE_CHANGED on_message_receiver_state_changed, void* context) +{ + MESSAGE_RECEIVER_INSTANCE* message_receiver = (MESSAGE_RECEIVER_INSTANCE*)malloc(sizeof(MESSAGE_RECEIVER_INSTANCE)); + if (message_receiver == NULL) + { + LogError("Error creating message receiver"); + } + else + { + message_receiver->link = link; + message_receiver->on_message_receiver_state_changed = on_message_receiver_state_changed; + message_receiver->on_message_receiver_state_changed_context = context; + message_receiver->message_receiver_state = MESSAGE_RECEIVER_STATE_IDLE; + } + + return message_receiver; +} + +void messagereceiver_destroy(MESSAGE_RECEIVER_HANDLE message_receiver) +{ + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + } + else + { + (void)messagereceiver_close(message_receiver); + free(message_receiver); + } +} + +int messagereceiver_open(MESSAGE_RECEIVER_HANDLE message_receiver, ON_MESSAGE_RECEIVED on_message_received, void* callback_context) +{ + int result; + + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + result = __FAILURE__; + } + else + { + if (message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_IDLE) + { + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_OPENING); + if (link_attach(message_receiver->link, on_transfer_received, on_link_state_changed, NULL, message_receiver) != 0) + { + LogError("Link attach failed"); + result = __FAILURE__; + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + message_receiver->on_message_received = on_message_received; + message_receiver->callback_context = callback_context; + + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +int messagereceiver_close(MESSAGE_RECEIVER_HANDLE message_receiver) +{ + int result; + + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + result = __FAILURE__; + } + else + { + if ((message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_OPENING) || + (message_receiver->message_receiver_state == MESSAGE_RECEIVER_STATE_OPEN)) + { + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_CLOSING); + + if (link_detach(message_receiver->link, true, NULL, NULL, NULL) != 0) + { + LogError("link detach failed"); + result = __FAILURE__; + set_message_receiver_state(message_receiver, MESSAGE_RECEIVER_STATE_ERROR); + } + else + { + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +int messagereceiver_get_link_name(MESSAGE_RECEIVER_HANDLE message_receiver, const char** link_name) +{ + int result; + + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + result = __FAILURE__; + } + else + { + if (link_get_name(message_receiver->link, link_name) != 0) + { + LogError("Getting link name failed"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +int messagereceiver_get_received_message_id(MESSAGE_RECEIVER_HANDLE message_receiver, delivery_number* message_id) +{ + int result; + + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + result = __FAILURE__; + } + else + { + if (link_get_received_message_id(message_receiver->link, message_id) != 0) + { + LogError("Failed getting received message Id"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +int messagereceiver_send_message_disposition(MESSAGE_RECEIVER_HANDLE message_receiver, const char* link_name, delivery_number message_number, AMQP_VALUE delivery_state) +{ + int result; + + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + result = __FAILURE__; + } + else + { + if (message_receiver->message_receiver_state != MESSAGE_RECEIVER_STATE_OPEN) + { + LogError("Message received not open"); + result = __FAILURE__; + } + else + { + const char* my_name; + if (link_get_name(message_receiver->link, &my_name) != 0) + { + LogError("Failed getting link name"); + result = __FAILURE__; + } + else + { + if (strcmp(link_name, my_name) != 0) + { + LogError("Link name does not match"); + result = __FAILURE__; + } + else + { + if (link_send_disposition(message_receiver->link, message_number, delivery_state) != 0) + { + LogError("Seding disposition failed"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + } + } + } + + return result; +} + +void messagereceiver_set_trace(MESSAGE_RECEIVER_HANDLE message_receiver, bool trace_on) +{ + if (message_receiver == NULL) + { + LogError("NULL message_receiver"); + } + else + { + /* No tracing is yet implemented for message receiver */ + (void)trace_on; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/message_sender.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,948 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_uamqp_c/link.h" +#include "azure_uamqp_c/message.h" +#include "azure_uamqp_c/message_sender.h" +#include "azure_uamqp_c/amqpvalue_to_string.h" +#include "azure_uamqp_c/async_operation.h" +#include "azure_uamqp_c/amqp_definitions.h" + +typedef enum MESSAGE_SEND_STATE_TAG +{ + MESSAGE_SEND_STATE_NOT_SENT, + MESSAGE_SEND_STATE_PENDING +} MESSAGE_SEND_STATE; + +typedef enum SEND_ONE_MESSAGE_RESULT_TAG +{ + SEND_ONE_MESSAGE_OK, + SEND_ONE_MESSAGE_ERROR, + SEND_ONE_MESSAGE_BUSY +} SEND_ONE_MESSAGE_RESULT; + +typedef struct MESSAGE_WITH_CALLBACK_TAG +{ + MESSAGE_HANDLE message; + ON_MESSAGE_SEND_COMPLETE on_message_send_complete; + void* context; + MESSAGE_SENDER_HANDLE message_sender; + MESSAGE_SEND_STATE message_send_state; + tickcounter_ms_t timeout; +} MESSAGE_WITH_CALLBACK; + +DEFINE_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK); + +typedef struct MESSAGE_SENDER_INSTANCE_TAG +{ + LINK_HANDLE link; + size_t message_count; + ASYNC_OPERATION_HANDLE* messages; + MESSAGE_SENDER_STATE message_sender_state; + ON_MESSAGE_SENDER_STATE_CHANGED on_message_sender_state_changed; + void* on_message_sender_state_changed_context; + unsigned int is_trace_on : 1; +} MESSAGE_SENDER_INSTANCE; + +static void remove_pending_message_by_index(MESSAGE_SENDER_HANDLE message_sender, size_t index) +{ + ASYNC_OPERATION_HANDLE* new_messages; + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, message_sender->messages[index]); + + if (message_with_callback->message != NULL) + { + message_destroy(message_with_callback->message); + message_with_callback->message = NULL; + } + + async_operation_destroy(message_sender->messages[index]); + + if (message_sender->message_count - index > 1) + { + (void)memmove(&message_sender->messages[index], &message_sender->messages[index + 1], sizeof(ASYNC_OPERATION_HANDLE) * (message_sender->message_count - index - 1)); + } + + message_sender->message_count--; + + if (message_sender->message_count > 0) + { + new_messages = (ASYNC_OPERATION_HANDLE*)realloc(message_sender->messages, sizeof(ASYNC_OPERATION_HANDLE) * (message_sender->message_count)); + if (new_messages != NULL) + { + message_sender->messages = new_messages; + } + } + else + { + free(message_sender->messages); + message_sender->messages = NULL; + } +} + +static void remove_pending_message(MESSAGE_SENDER_INSTANCE* message_sender, ASYNC_OPERATION_HANDLE pending_send) +{ + size_t i; + + for (i = 0; i < message_sender->message_count; i++) + { + if (message_sender->messages[i] == pending_send) + { + remove_pending_message_by_index(message_sender, i); + break; + } + } +} + +static void on_delivery_settled(void* context, delivery_number delivery_no, LINK_DELIVERY_SETTLE_REASON reason, AMQP_VALUE delivery_state) +{ + ASYNC_OPERATION_HANDLE pending_send = (ASYNC_OPERATION_HANDLE)context; + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, pending_send); + MESSAGE_SENDER_INSTANCE* message_sender = (MESSAGE_SENDER_INSTANCE*)message_with_callback->message_sender; + (void)delivery_no; + + if (message_with_callback->on_message_send_complete != NULL) + { + switch (reason) + { + case LINK_DELIVERY_SETTLE_REASON_DISPOSITION_RECEIVED: + if (delivery_state == NULL) + { + LogError("delivery state not provided"); + } + else + { + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(delivery_state); + AMQP_VALUE described = amqpvalue_get_inplace_described_value(delivery_state); + + if (descriptor == NULL) + { + LogError("Error getting descriptor for delivery state"); + } + else if (is_accepted_type_by_descriptor(descriptor)) + { + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_OK, described); + } + else + { + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_ERROR, described); + } + } + + break; + case LINK_DELIVERY_SETTLE_REASON_SETTLED: + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_OK, NULL); + break; + case LINK_DELIVERY_SETTLE_REASON_TIMEOUT: + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_TIMEOUT, NULL); + break; + case LINK_DELIVERY_SETTLE_REASON_NOT_DELIVERED: + default: + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_ERROR, NULL); + break; + } + } + + remove_pending_message(message_sender, pending_send); +} + +static int encode_bytes(void* context, const unsigned char* bytes, size_t length) +{ + PAYLOAD* payload = (PAYLOAD*)context; + (void)memcpy((unsigned char*)payload->bytes + payload->length, bytes, length); + payload->length += length; + return 0; +} + +static void log_message_chunk(MESSAGE_SENDER_INSTANCE* message_sender, const char* name, AMQP_VALUE value) +{ +#ifdef NO_LOGGING + (void)message_sender; + (void)name; + (void)value; +#else + if (xlogging_get_log_function() != NULL && message_sender->is_trace_on == 1) + { + char* value_as_string = NULL; + LOG(AZ_LOG_TRACE, 0, "%s", P_OR_NULL(name)); + LOG(AZ_LOG_TRACE, 0, "%s", ((value_as_string = amqpvalue_to_string(value)), P_OR_NULL(value_as_string))); + if (value_as_string != NULL) + { + free(value_as_string); + } + } +#endif +} + +static SEND_ONE_MESSAGE_RESULT send_one_message(MESSAGE_SENDER_INSTANCE* message_sender, ASYNC_OPERATION_HANDLE pending_send, MESSAGE_HANDLE message) +{ + SEND_ONE_MESSAGE_RESULT result; + + size_t encoded_size; + size_t total_encoded_size = 0; + MESSAGE_BODY_TYPE message_body_type; + message_format message_format; + + if ((message_get_body_type(message, &message_body_type) != 0) || + (message_get_message_format(message, &message_format) != 0)) + { + LogError("Failure getting message body type and/or message format"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + // header + HEADER_HANDLE header = NULL; + AMQP_VALUE header_amqp_value = NULL; + PROPERTIES_HANDLE properties = NULL; + AMQP_VALUE properties_amqp_value = NULL; + AMQP_VALUE application_properties = NULL; + AMQP_VALUE application_properties_value = NULL; + AMQP_VALUE body_amqp_value = NULL; + size_t body_data_count = 0; + AMQP_VALUE msg_annotations = NULL; + bool is_error = false; + + // message header + if ((message_get_header(message, &header) == 0) && + (header != NULL)) + { + header_amqp_value = amqpvalue_create_header(header); + if (header_amqp_value == NULL) + { + LogError("Cannot create header AMQP value"); + is_error = true; + } + else + { + if (amqpvalue_get_encoded_size(header_amqp_value, &encoded_size) != 0) + { + LogError("Cannot obtain header encoded size"); + is_error = true; + } + else + { + total_encoded_size += encoded_size; + } + } + + } + + // message annotations + if ((!is_error) && + (message_get_message_annotations(message, &msg_annotations) == 0) && + (msg_annotations != NULL)) + { + if (amqpvalue_get_encoded_size(msg_annotations, &encoded_size) != 0) + { + LogError("Cannot obtain message annotations encoded size"); + is_error = true; + } + else + { + total_encoded_size += encoded_size; + } + } + + // properties + if ((!is_error) && + (message_get_properties(message, &properties) == 0) && + (properties != NULL)) + { + properties_amqp_value = amqpvalue_create_properties(properties); + if (properties_amqp_value == NULL) + { + LogError("Cannot create message properties AMQP value"); + is_error = true; + } + else + { + if (amqpvalue_get_encoded_size(properties_amqp_value, &encoded_size) != 0) + { + LogError("Cannot obtain message properties encoded size"); + is_error = true; + } + else + { + total_encoded_size += encoded_size; + } + } + } + + // application properties + if ((!is_error) && + (message_get_application_properties(message, &application_properties) == 0) && + (application_properties != NULL)) + { + application_properties_value = amqpvalue_create_application_properties(application_properties); + if (application_properties_value == NULL) + { + LogError("Cannot create application properties AMQP value"); + is_error = true; + } + else + { + if (amqpvalue_get_encoded_size(application_properties_value, &encoded_size) != 0) + { + LogError("Cannot obtain application properties encoded size"); + is_error = true; + } + else + { + total_encoded_size += encoded_size; + } + } + } + + if (is_error) + { + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + result = SEND_ONE_MESSAGE_OK; + + // body - amqp data + switch (message_body_type) + { + default: + LogError("Unknown body type"); + result = SEND_ONE_MESSAGE_ERROR; + break; + + case MESSAGE_BODY_TYPE_VALUE: + { + AMQP_VALUE message_body_amqp_value; + if (message_get_body_amqp_value_in_place(message, &message_body_amqp_value) != 0) + { + LogError("Cannot obtain AMQP value from body"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + body_amqp_value = amqpvalue_create_amqp_value(message_body_amqp_value); + if (body_amqp_value == NULL) + { + LogError("Cannot create body AMQP value"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + if (amqpvalue_get_encoded_size(body_amqp_value, &encoded_size) != 0) + { + LogError("Cannot get body AMQP value encoded size"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + total_encoded_size += encoded_size; + } + } + } + + break; + } + + case MESSAGE_BODY_TYPE_DATA: + { + BINARY_DATA binary_data; + size_t i; + + if (message_get_body_amqp_data_count(message, &body_data_count) != 0) + { + LogError("Cannot get body AMQP data count"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + for (i = 0; i < body_data_count; i++) + { + if (message_get_body_amqp_data_in_place(message, i, &binary_data) != 0) + { + LogError("Cannot get body AMQP data %u", (unsigned int)i); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + AMQP_VALUE body_amqp_data; + amqp_binary binary_value; + binary_value.bytes = binary_data.bytes; + binary_value.length = (uint32_t)binary_data.length; + body_amqp_data = amqpvalue_create_data(binary_value); + if (body_amqp_data == NULL) + { + LogError("Cannot create body AMQP data"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + if (amqpvalue_get_encoded_size(body_amqp_data, &encoded_size) != 0) + { + LogError("Cannot get body AMQP data encoded size"); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + total_encoded_size += encoded_size; + } + + amqpvalue_destroy(body_amqp_data); + } + } + } + } + break; + } + } + + if (result == 0) + { + void* data_bytes = malloc(total_encoded_size); + PAYLOAD payload; + payload.bytes = (const unsigned char*)data_bytes; + payload.length = 0; + result = SEND_ONE_MESSAGE_OK; + + if (header != NULL) + { + if (amqpvalue_encode(header_amqp_value, encode_bytes, &payload) != 0) + { + LogError("Cannot encode header value"); + result = SEND_ONE_MESSAGE_ERROR; + } + + log_message_chunk(message_sender, "Header:", header_amqp_value); + } + + if ((result == SEND_ONE_MESSAGE_OK) && (msg_annotations != NULL)) + { + if (amqpvalue_encode(msg_annotations, encode_bytes, &payload) != 0) + { + LogError("Cannot encode message annotations value"); + result = SEND_ONE_MESSAGE_ERROR; + } + + log_message_chunk(message_sender, "Message Annotations:", msg_annotations); + } + + if ((result == SEND_ONE_MESSAGE_OK) && (properties != NULL)) + { + if (amqpvalue_encode(properties_amqp_value, encode_bytes, &payload) != 0) + { + LogError("Cannot encode message properties value"); + result = SEND_ONE_MESSAGE_ERROR; + } + + log_message_chunk(message_sender, "Properties:", properties_amqp_value); + } + + if ((result == SEND_ONE_MESSAGE_OK) && (application_properties != NULL)) + { + if (amqpvalue_encode(application_properties_value, encode_bytes, &payload) != 0) + { + LogError("Cannot encode application properties value"); + result = SEND_ONE_MESSAGE_ERROR; + } + + log_message_chunk(message_sender, "Application properties:", application_properties_value); + } + + if (result == SEND_ONE_MESSAGE_OK) + { + switch (message_body_type) + { + default: + LogError("Unknown message type"); + result = SEND_ONE_MESSAGE_ERROR; + break; + + case MESSAGE_BODY_TYPE_VALUE: + { + if (amqpvalue_encode(body_amqp_value, encode_bytes, &payload) != 0) + { + LogError("Cannot encode body AMQP value"); + result = SEND_ONE_MESSAGE_ERROR; + } + + log_message_chunk(message_sender, "Body - amqp value:", body_amqp_value); + break; + } + case MESSAGE_BODY_TYPE_DATA: + { + BINARY_DATA binary_data; + size_t i; + + for (i = 0; i < body_data_count; i++) + { + if (message_get_body_amqp_data_in_place(message, i, &binary_data) != 0) + { + LogError("Cannot get AMQP data %u", (unsigned int)i); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + AMQP_VALUE body_amqp_data; + amqp_binary binary_value; + binary_value.bytes = binary_data.bytes; + binary_value.length = (uint32_t)binary_data.length; + body_amqp_data = amqpvalue_create_data(binary_value); + if (body_amqp_data == NULL) + { + LogError("Cannot create body AMQP data %u", (unsigned int)i); + result = SEND_ONE_MESSAGE_ERROR; + } + else + { + if (amqpvalue_encode(body_amqp_data, encode_bytes, &payload) != 0) + { + LogError("Cannot encode body AMQP data %u", (unsigned int)i); + result = SEND_ONE_MESSAGE_ERROR; + break; + } + + amqpvalue_destroy(body_amqp_data); + } + } + } + break; + } + } + } + + if (result == SEND_ONE_MESSAGE_OK) + { + ASYNC_OPERATION_HANDLE transfer_async_operation; + LINK_TRANSFER_RESULT link_transfer_error; + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, pending_send); + message_with_callback->message_send_state = MESSAGE_SEND_STATE_PENDING; + + transfer_async_operation = link_transfer_async(message_sender->link, message_format, &payload, 1, on_delivery_settled, pending_send, &link_transfer_error, message_with_callback->timeout); + if (transfer_async_operation == NULL) + { + if (link_transfer_error == LINK_TRANSFER_BUSY) + { + message_with_callback->message_send_state = MESSAGE_SEND_STATE_NOT_SENT; + result = SEND_ONE_MESSAGE_BUSY; + } + else + { + LogError("Error in link transfer"); + result = SEND_ONE_MESSAGE_ERROR; + } + } + else + { + result = SEND_ONE_MESSAGE_OK; + } + } + + free(data_bytes); + + if (body_amqp_value != NULL) + { + amqpvalue_destroy(body_amqp_value); + } + } + } + + if (header != NULL) + { + header_destroy(header); + } + + if (header_amqp_value != NULL) + { + amqpvalue_destroy(header_amqp_value); + } + + if (msg_annotations != NULL) + { + annotations_destroy(msg_annotations); + } + + if (application_properties != NULL) + { + amqpvalue_destroy(application_properties); + } + + if (application_properties_value != NULL) + { + amqpvalue_destroy(application_properties_value); + } + + if (properties_amqp_value != NULL) + { + amqpvalue_destroy(properties_amqp_value); + } + + if (properties != NULL) + { + properties_destroy(properties); + } + } + + return result; +} + +static void send_all_pending_messages(MESSAGE_SENDER_HANDLE message_sender) +{ + size_t i; + + for (i = 0; i < message_sender->message_count; i++) + { + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, message_sender->messages[i]); + if (message_with_callback->message_send_state == MESSAGE_SEND_STATE_NOT_SENT) + { + switch (send_one_message(message_sender, message_sender->messages[i], message_with_callback->message)) + { + default: + LogError("Invalid send one message result"); + break; + + case SEND_ONE_MESSAGE_ERROR: + { + ON_MESSAGE_SEND_COMPLETE on_message_send_complete = message_with_callback->on_message_send_complete; + void* context = message_with_callback->context; + remove_pending_message_by_index(message_sender, i); + + if (on_message_send_complete != NULL) + { + on_message_send_complete(context, MESSAGE_SEND_ERROR, NULL); + } + + i = message_sender->message_count; + break; + } + case SEND_ONE_MESSAGE_BUSY: + i = message_sender->message_count + 1; + break; + + case SEND_ONE_MESSAGE_OK: + break; + } + + i--; + } + } +} + +static void set_message_sender_state(MESSAGE_SENDER_INSTANCE* message_sender, MESSAGE_SENDER_STATE new_state) +{ + MESSAGE_SENDER_STATE previous_state = message_sender->message_sender_state; + message_sender->message_sender_state = new_state; + if (message_sender->on_message_sender_state_changed != NULL) + { + message_sender->on_message_sender_state_changed(message_sender->on_message_sender_state_changed_context, new_state, previous_state); + } +} + +static void indicate_all_messages_as_error(MESSAGE_SENDER_INSTANCE* message_sender) +{ + size_t i; + + for (i = 0; i < message_sender->message_count; i++) + { + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, message_sender->messages[i]); + if (message_with_callback->on_message_send_complete != NULL) + { + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_ERROR, NULL); + } + + if (message_with_callback->message != NULL) + { + message_destroy(message_with_callback->message); + } + async_operation_destroy(message_sender->messages[i]); + } + + if (message_sender->messages != NULL) + { + message_sender->message_count = 0; + + free(message_sender->messages); + message_sender->messages = NULL; + } +} + +static void on_link_state_changed(void* context, LINK_STATE new_link_state, LINK_STATE previous_link_state) +{ + MESSAGE_SENDER_INSTANCE* message_sender = (MESSAGE_SENDER_INSTANCE*)context; + (void)previous_link_state; + + switch (new_link_state) + { + default: + break; + + case LINK_STATE_ATTACHED: + if (message_sender->message_sender_state == MESSAGE_SENDER_STATE_OPENING) + { + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_OPEN); + } + break; + case LINK_STATE_DETACHED: + if ((message_sender->message_sender_state == MESSAGE_SENDER_STATE_OPEN) || + (message_sender->message_sender_state == MESSAGE_SENDER_STATE_CLOSING)) + { + /* switch to closing so that no more requests should be accepted */ + indicate_all_messages_as_error(message_sender); + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_IDLE); + } + else if (message_sender->message_sender_state != MESSAGE_SENDER_STATE_IDLE) + { + /* Any other transition must be an error */ + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_ERROR); + } + break; + case LINK_STATE_ERROR: + if (message_sender->message_sender_state != MESSAGE_SENDER_STATE_ERROR) + { + indicate_all_messages_as_error(message_sender); + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_ERROR); + } + break; + } +} + +static void on_link_flow_on(void* context) +{ + MESSAGE_SENDER_HANDLE message_sender = (MESSAGE_SENDER_INSTANCE*)context; + send_all_pending_messages(message_sender); +} + +MESSAGE_SENDER_HANDLE messagesender_create(LINK_HANDLE link, ON_MESSAGE_SENDER_STATE_CHANGED on_message_sender_state_changed, void* context) +{ + MESSAGE_SENDER_INSTANCE* message_sender = (MESSAGE_SENDER_INSTANCE*)malloc(sizeof(MESSAGE_SENDER_INSTANCE)); + if (message_sender == NULL) + { + LogError("Failed allocating message sender"); + } + else + { + message_sender->messages = NULL; + message_sender->message_count = 0; + message_sender->link = link; + message_sender->on_message_sender_state_changed = on_message_sender_state_changed; + message_sender->on_message_sender_state_changed_context = context; + message_sender->message_sender_state = MESSAGE_SENDER_STATE_IDLE; + message_sender->is_trace_on = 0; + } + + return message_sender; +} + +void messagesender_destroy(MESSAGE_SENDER_HANDLE message_sender) +{ + if (message_sender == NULL) + { + LogError("NULL message_sender"); + } + else + { + indicate_all_messages_as_error(message_sender); + (void)messagesender_close(message_sender); + + free(message_sender); + } +} + +int messagesender_open(MESSAGE_SENDER_HANDLE message_sender) +{ + int result; + + if (message_sender == NULL) + { + LogError("NULL message_sender"); + result = __FAILURE__; + } + else + { + if (message_sender->message_sender_state == MESSAGE_SENDER_STATE_IDLE) + { + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_OPENING); + if (link_attach(message_sender->link, NULL, on_link_state_changed, on_link_flow_on, message_sender) != 0) + { + LogError("attach link failed"); + result = __FAILURE__; + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_ERROR); + } + else + { + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +int messagesender_close(MESSAGE_SENDER_HANDLE message_sender) +{ + int result; + + if (message_sender == NULL) + { + LogError("NULL message_sender"); + result = __FAILURE__; + } + else + { + if ((message_sender->message_sender_state == MESSAGE_SENDER_STATE_OPENING) || + (message_sender->message_sender_state == MESSAGE_SENDER_STATE_OPEN)) + { + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_CLOSING); + if (link_detach(message_sender->link, true, NULL, NULL, NULL) != 0) + { + LogError("Detaching link failed"); + result = __FAILURE__; + set_message_sender_state(message_sender, MESSAGE_SENDER_STATE_ERROR); + } + else + { + result = 0; + } + } + else + { + result = 0; + } + } + + return result; +} + +static void messagesender_send_cancel_handler(ASYNC_OPERATION_HANDLE send_operation) +{ + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, send_operation); + if (message_with_callback->on_message_send_complete != NULL) + { + message_with_callback->on_message_send_complete(message_with_callback->context, MESSAGE_SEND_CANCELLED, NULL); + } + + remove_pending_message(message_with_callback->message_sender, send_operation); +} + +ASYNC_OPERATION_HANDLE messagesender_send_async(MESSAGE_SENDER_HANDLE message_sender, MESSAGE_HANDLE message, ON_MESSAGE_SEND_COMPLETE on_message_send_complete, void* callback_context, tickcounter_ms_t timeout) +{ + ASYNC_OPERATION_HANDLE result; + + if ((message_sender == NULL) || + (message == NULL)) + { + LogError("Bad parameters: message_sender = %p, message = %p"); + result = NULL; + } + else + { + if (message_sender->message_sender_state == MESSAGE_SENDER_STATE_ERROR) + { + LogError("Message sender in ERROR state"); + result = NULL; + } + else + { + result = CREATE_ASYNC_OPERATION(MESSAGE_WITH_CALLBACK, messagesender_send_cancel_handler); + if (result == NULL) + { + LogError("Failed allocating context for send"); + } + else + { + MESSAGE_WITH_CALLBACK* message_with_callback = GET_ASYNC_OPERATION_CONTEXT(MESSAGE_WITH_CALLBACK, result); + ASYNC_OPERATION_HANDLE* new_messages = (ASYNC_OPERATION_HANDLE*)realloc(message_sender->messages, sizeof(ASYNC_OPERATION_HANDLE) * (message_sender->message_count + 1)); + if (new_messages == NULL) + { + LogError("Failed allocating memory for pending sends"); + async_operation_destroy(result); + result = NULL; + } + else + { + message_with_callback->timeout = timeout; + message_sender->messages = new_messages; + if (message_sender->message_sender_state != MESSAGE_SENDER_STATE_OPEN) + { + message_with_callback->message = message_clone(message); + if (message_with_callback->message == NULL) + { + LogError("Cannot clone message for placing it in the pending sends list"); + async_operation_destroy(result); + result = NULL; + } + + message_with_callback->message_send_state = MESSAGE_SEND_STATE_NOT_SENT; + } + else + { + message_with_callback->message = NULL; + message_with_callback->message_send_state = MESSAGE_SEND_STATE_PENDING; + } + + if (result != NULL) + { + message_with_callback->on_message_send_complete = on_message_send_complete; + message_with_callback->context = callback_context; + message_with_callback->message_sender = message_sender; + + message_sender->messages[message_sender->message_count] = result; + message_sender->message_count++; + + if (message_sender->message_sender_state == MESSAGE_SENDER_STATE_OPEN) + { + switch (send_one_message(message_sender, result, message)) + { + default: + case SEND_ONE_MESSAGE_ERROR: + LogError("Error sending message"); + remove_pending_message_by_index(message_sender, message_sender->message_count - 1); + result = NULL; + break; + + case SEND_ONE_MESSAGE_BUSY: + message_with_callback->message = message_clone(message); + if (message_with_callback->message == NULL) + { + LogError("Error cloning message for placing it in the pending sends list"); + async_operation_destroy(result); + result = NULL; + } + else + { + message_with_callback->message_send_state = MESSAGE_SEND_STATE_NOT_SENT; + } + break; + + case SEND_ONE_MESSAGE_OK: + break; + } + } + } + } + } + } + } + + return result; +} + +void messagesender_set_trace(MESSAGE_SENDER_HANDLE message_sender, bool traceOn) +{ + if (message_sender == NULL) + { + LogError("NULL message_sender"); + } + else + { + message_sender->is_trace_on = traceOn ? 1 : 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/messaging.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions.h" + +AMQP_VALUE messaging_create_source(const char* address) +{ + AMQP_VALUE result; + SOURCE_HANDLE source = source_create(); + + if (source == NULL) + { + LogError("NULL source"); + result = NULL; + } + else + { + AMQP_VALUE address_value = amqpvalue_create_string(address); + if (address_value == NULL) + { + LogError("Cannot create address AMQP string"); + result = NULL; + } + else + { + if (source_set_address(source, address_value) != 0) + { + LogError("Cannot set address on source"); + result = NULL; + } + else + { + result = amqpvalue_create_source(source); + if (result == NULL) + { + LogError("Cannot create source"); + } + else + { + /* all ok */ + } + } + + amqpvalue_destroy(address_value); + } + + source_destroy(source); + } + + return result; +} + +AMQP_VALUE messaging_create_target(const char* address) +{ + AMQP_VALUE result; + TARGET_HANDLE target = target_create(); + + if (target == NULL) + { + LogError("NULL target"); + result = NULL; + } + else + { + AMQP_VALUE address_value = amqpvalue_create_string(address); + if (address_value == NULL) + { + LogError("Cannot create address AMQP string"); + result = NULL; + } + else + { + if (target_set_address(target, address_value) != 0) + { + LogError("Cannot set address on target"); + result = NULL; + } + else + { + result = amqpvalue_create_target(target); + if (result == NULL) + { + LogError("Cannot create target"); + } + else + { + /* all ok */ + } + } + + amqpvalue_destroy(address_value); + } + + target_destroy(target); + } + + return result; +} + +AMQP_VALUE messaging_delivery_received(uint32_t section_number, uint64_t section_offset) +{ + AMQP_VALUE result; + RECEIVED_HANDLE received = received_create(section_number, section_offset); + if (received == NULL) + { + LogError("Cannot create RECEIVED delivery state handle"); + result = NULL; + } + else + { + result = amqpvalue_create_received(received); + if (result == NULL) + { + LogError("Cannot create RECEIVED delivery state AMQP value"); + } + else + { + /* all ok */ + } + + received_destroy(received); + } + + return result; +} + +AMQP_VALUE messaging_delivery_accepted(void) +{ + AMQP_VALUE result; + ACCEPTED_HANDLE accepted = accepted_create(); + if (accepted == NULL) + { + LogError("Cannot create ACCEPTED delivery state handle"); + result = NULL; + } + else + { + result = amqpvalue_create_accepted(accepted); + if (result == NULL) + { + LogError("Cannot create ACCEPTED delivery state AMQP value"); + } + else + { + /* all ok */ + } + + accepted_destroy(accepted); + } + + return result; +} + +AMQP_VALUE messaging_delivery_rejected(const char* error_condition, const char* error_description) +{ + AMQP_VALUE result; + REJECTED_HANDLE rejected = rejected_create(); + if (rejected == NULL) + { + LogError("Cannot create REJECTED delivery state handle"); + result = NULL; + } + else + { + ERROR_HANDLE error_handle = NULL; + bool error_constructing = false; + + if (error_condition != NULL) + { + error_handle = error_create(error_condition); + if (error_handle == NULL) + { + LogError("Cannot create error AMQP value for REJECTED state"); + error_constructing = true; + } + else + { + if ((error_description != NULL) && + (error_set_description(error_handle, error_description) != 0)) + { + LogError("Cannot set error description on error AMQP value for REJECTED state"); + error_constructing = true; + } + else + { + if (rejected_set_error(rejected, error_handle) != 0) + { + LogError("Cannot set error on REJECTED state handle"); + error_constructing = true; + } + } + + error_destroy(error_handle); + } + } + + if (error_constructing) + { + result = NULL; + } + else + { + result = amqpvalue_create_rejected(rejected); + if (result == NULL) + { + LogError("Cannot create REJECTED delivery state AMQP value"); + } + else + { + /* all ok */ + } + } + + rejected_destroy(rejected); + } + + return result; +} + +AMQP_VALUE messaging_delivery_released(void) +{ + AMQP_VALUE result; + RELEASED_HANDLE released = released_create(); + if (released == NULL) + { + LogError("Cannot create RELEASED delivery state handle"); + result = NULL; + } + else + { + result = amqpvalue_create_released(released); + if (result == NULL) + { + LogError("Cannot create RELEASED delivery state AMQP value"); + } + else + { + /* all ok */ + } + + released_destroy(released); + } + + return result; +} + +AMQP_VALUE messaging_delivery_modified(bool delivery_failed, bool undeliverable_here, fields message_annotations) +{ + AMQP_VALUE result; + MODIFIED_HANDLE modified = modified_create(); + if (modified == NULL) + { + LogError("Cannot create MODIFIED delivery state handle"); + result = NULL; + } + else + { + if (modified_set_delivery_failed(modified, delivery_failed) != 0) + { + LogError("Cannot set delivery failed on MODIFIED delivery state"); + result = NULL; + } + else if (modified_set_undeliverable_here(modified, undeliverable_here) != 0) + { + LogError("Cannot set undeliverable here on MODIFIED delivery state"); + result = NULL; + } + else if ((message_annotations != NULL) && (modified_set_message_annotations(modified, message_annotations) != 0)) + { + LogError("Cannot set message annotations on MODIFIED delivery state"); + result = NULL; + } + else + { + result = amqpvalue_create_modified(modified); + if (result == NULL) + { + LogError("Cannot create MODIFIED delivery state AMQP value"); + } + else + { + /* all ok */ + } + } + + modified_destroy(modified); + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_anonymous.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_anonymous.h" + +typedef struct SASL_ANONYMOUS_INSTANCE_TAG +{ + unsigned char dummy; +} SASL_ANONYMOUS_INSTANCE; + +/* Codes_SRS_SASL_ANONYMOUS_01_001: [`saslanonymous_create` shall return on success a non-NULL handle to a new SASL anonymous mechanism.]*/ +static CONCRETE_SASL_MECHANISM_HANDLE saslanonymous_create(void* config) +{ + CONCRETE_SASL_MECHANISM_HANDLE result; + + /* Codes_SRS_SASL_ANONYMOUS_01_003: [Since this is the ANONYMOUS SASL mechanism, `config` shall be ignored.]*/ + (void)config; + + result = malloc(sizeof(SASL_ANONYMOUS_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_SASL_ANONYMOUS_01_002: [If allocating the memory needed for the SASL anonymous instance fails then `saslanonymous_create` shall return NULL.] */ + LogError("Cannot allocate memory for SASL anonymous instance"); + } + + return result; +} + +static void saslanonymous_destroy(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle) +{ + /* Codes_SRS_SASL_ANONYMOUS_01_005: [If the argument `concrete_sasl_mechanism` is NULL, `saslanonymous_destroy` shall do nothing.]*/ + if (sasl_mechanism_concrete_handle == NULL) + { + LogError("NULL sasl_mechanism_concrete_handle"); + } + else + { + /* Codes_SRS_SASL_ANONYMOUS_01_004: [`saslanonymous_destroy` shall free all resources associated with the SASL mechanism.] */ + free(sasl_mechanism_concrete_handle); + } +} + +static int saslanonymous_get_init_bytes(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle, SASL_MECHANISM_BYTES* init_bytes) +{ + int result; + + /* Codes_SRS_SASL_ANONYMOUS_01_007: [If any argument is NULL, `saslanonymous_get_init_bytes` shall return a non-zero value.]*/ + if ((sasl_mechanism_concrete_handle == NULL) || + (init_bytes == NULL)) + { + LogError("Bad arguments: sasl_mechanism_concrete_handle = %p, init_bytes = %p", + sasl_mechanism_concrete_handle, init_bytes); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_ANONYMOUS_01_012: [The bytes field of `init_buffer` shall be set to NULL.] */ + init_bytes->bytes = NULL; + /* Codes_SRS_SASL_ANONYMOUS_01_006: [`saslanonymous_get_init_bytes` shall validate the `concrete_sasl_mechanism` argument and set the length of the `init_bytes` argument to be zero.] */ + init_bytes->length = 0; + + /* Codes_SRS_SASL_ANONYMOUS_01_011: [On success `saslanonymous_get_init_bytes` shall return zero.] */ + result = 0; + } + + return result; +} + +static const char* saslanonymous_get_mechanism_name(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism) +{ + const char* result; + + /* Codes_SRS_SASL_ANONYMOUS_01_009: [If the argument `concrete_sasl_mechanism` is NULL, `saslanonymous_get_mechanism_name` shall return NULL.] */ + if (sasl_mechanism == NULL) + { + LogError("NULL sasl_mechanism"); + result = NULL; + } + else + { + /* Codes_SRS_SASL_ANONYMOUS_01_008: [`saslanonymous_get_mechanism_name` shall validate the argument `concrete_sasl_mechanism` and on success it shall return a pointer to the string `ANONYMOUS`.] */ + result = "ANONYMOUS"; + } + + return result; +} + +static int saslanonymous_challenge(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism, const SASL_MECHANISM_BYTES* challenge_bytes, SASL_MECHANISM_BYTES* response_bytes) +{ + int result; + + (void)challenge_bytes; + + /* Codes_SRS_SASL_ANONYMOUS_01_015: [If the `concrete_sasl_mechanism` or `response_bytes` argument is NULL then `saslanonymous_challenge` shall fail and return a non-zero value.] */ + if ((concrete_sasl_mechanism == NULL) || + (response_bytes == NULL)) + { + LogError("Bad arguments: concrete_sasl_mechanism = %p, response_bytes = %p", + concrete_sasl_mechanism, response_bytes); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_ANONYMOUS_01_013: [`saslanonymous_challenge` shall set the `buffer` field to NULL and `size` to 0 in the `response_bytes` argument as the ANONYMOUS SASL mechanism does not implement challenge/response.] */ + response_bytes->bytes = NULL; + response_bytes->length = 0; + + /* Codes_SRS_SASL_ANONYMOUS_01_014: [On success, `saslanonymous_challenge` shall return 0.] */ + result = 0; + } + + return result; +} + +/* Codes_SRS_SASL_ANONYMOUS_01_010: [`saslanonymous_get_interface` shall return a pointer to a `SASL_MECHANISM_INTERFACE_DESCRIPTION` structure that contains pointers to the functions: `saslanonymous_create`, `saslanonymous_destroy`, `saslanonymous_get_init_bytes`, `saslanonymous_get_mechanism_name`, `saslanonymous_challenge`.] */ +static const SASL_MECHANISM_INTERFACE_DESCRIPTION saslanonymous_interface = +{ + saslanonymous_create, + saslanonymous_destroy, + saslanonymous_get_init_bytes, + saslanonymous_get_mechanism_name, + saslanonymous_challenge +}; + +const SASL_MECHANISM_INTERFACE_DESCRIPTION* saslanonymous_get_interface(void) +{ + return &saslanonymous_interface; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_frame_codec.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,332 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_frame_codec.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions.h" + +/* Requirements implemented by design or by other modules */ +/* Codes_SRS_SASL_FRAME_CODEC_01_011: [A SASL frame has a type code of 0x01.] */ +/* Codes_SRS_SASL_FRAME_CODEC_01_016: [The maximum size of a SASL frame is defined by MIN-MAX-FRAME-SIZE.] */ + +#define MIX_MAX_FRAME_SIZE 512 + +typedef enum SASL_FRAME_DECODE_STATE_TAG +{ + SASL_FRAME_DECODE_FRAME, + SASL_FRAME_DECODE_ERROR +} SASL_FRAME_DECODE_STATE; + +typedef struct SASL_FRAME_CODEC_INSTANCE_TAG +{ + FRAME_CODEC_HANDLE frame_codec; + + /* decode */ + ON_SASL_FRAME_RECEIVED on_sasl_frame_received; + ON_SASL_FRAME_CODEC_ERROR on_sasl_frame_codec_error; + void* callback_context; + AMQPVALUE_DECODER_HANDLE decoder; + SASL_FRAME_DECODE_STATE decode_state; + AMQP_VALUE decoded_sasl_frame_value; +} SASL_FRAME_CODEC_INSTANCE; + +static void amqp_value_decoded(void* context, AMQP_VALUE decoded_value) +{ + SASL_FRAME_CODEC_INSTANCE* sasl_frame_codec_instance = (SASL_FRAME_CODEC_INSTANCE*)context; + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(decoded_value); + + if (descriptor == NULL) + { + LogError("Cannot get frame descriptor"); + sasl_frame_codec_instance->decode_state = SASL_FRAME_DECODE_ERROR; + + /* Codes_SRS_SASL_FRAME_CODEC_01_049: [If any error occurs while decoding a frame, the decoder shall call the on_sasl_frame_codec_error and pass to it the callback_context, both of those being the ones given to sasl_frame_codec_create.] */ + sasl_frame_codec_instance->on_sasl_frame_codec_error(sasl_frame_codec_instance->callback_context); + } + else + { + /* Codes_SRS_SASL_FRAME_CODEC_01_009: [The frame body of a SASL frame MUST contain exactly one AMQP type, whose type encoding MUST have provides="sasl-frame".] */ + if (!is_sasl_mechanisms_type_by_descriptor(descriptor) && + !is_sasl_init_type_by_descriptor(descriptor) && + !is_sasl_challenge_type_by_descriptor(descriptor) && + !is_sasl_response_type_by_descriptor(descriptor) && + !is_sasl_outcome_type_by_descriptor(descriptor)) + { + LogError("Not a SASL frame"); + sasl_frame_codec_instance->decode_state = SASL_FRAME_DECODE_ERROR; + + /* Codes_SRS_SASL_FRAME_CODEC_01_049: [If any error occurs while decoding a frame, the decoder shall call the on_sasl_frame_codec_error and pass to it the callback_context, both of those being the ones given to sasl_frame_codec_create.] */ + sasl_frame_codec_instance->on_sasl_frame_codec_error(sasl_frame_codec_instance->callback_context); + } + else + { + sasl_frame_codec_instance->decoded_sasl_frame_value = decoded_value; + } + } +} + +static void frame_received(void* context, const unsigned char* type_specific, uint32_t type_specific_size, const unsigned char* frame_body, uint32_t frame_body_size) +{ + SASL_FRAME_CODEC_INSTANCE* sasl_frame_codec_instance = (SASL_FRAME_CODEC_INSTANCE*)context; + + /* Codes_SRS_SASL_FRAME_CODEC_01_006: [Bytes 6 and 7 of the header are ignored.] */ + (void)type_specific; + /* Codes_SRS_SASL_FRAME_CODEC_01_007: [The extended header is ignored.] */ + + /* Codes_SRS_SASL_FRAME_CODEC_01_008: [The maximum size of a SASL frame is defined by MIN-MAX-FRAME-SIZE.] */ + if ((type_specific_size + frame_body_size + 6 > MIX_MAX_FRAME_SIZE) || + /* Codes_SRS_SASL_FRAME_CODEC_01_010: [Receipt of an empty frame is an irrecoverable error.] */ + (frame_body_size == 0)) + { + LogError("Bad SASL frame size"); + + /* Codes_SRS_SASL_FRAME_CODEC_01_049: [If any error occurs while decoding a frame, the decoder shall call the on_sasl_frame_codec_error and pass to it the callback_context, both of those being the ones given to sasl_frame_codec_create.] */ + sasl_frame_codec_instance->on_sasl_frame_codec_error(sasl_frame_codec_instance->callback_context); + } + else + { + switch (sasl_frame_codec_instance->decode_state) + { + default: + case SASL_FRAME_DECODE_ERROR: + break; + + case SASL_FRAME_DECODE_FRAME: + sasl_frame_codec_instance->decoded_sasl_frame_value = NULL; + + /* Codes_SRS_SASL_FRAME_CODEC_01_039: [sasl_frame_codec shall decode the sasl-frame value as a described type.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_048: [Receipt of an empty frame is an irrecoverable error.] */ + while ((frame_body_size > 0) && + (sasl_frame_codec_instance->decoded_sasl_frame_value == NULL) && + (sasl_frame_codec_instance->decode_state != SASL_FRAME_DECODE_ERROR)) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_040: [Decoding the sasl-frame type shall be done by feeding the bytes to the decoder create in sasl_frame_codec_create.] */ + if (amqpvalue_decode_bytes(sasl_frame_codec_instance->decoder, frame_body, 1) != 0) + { + LogError("Could not decode SASL frame AMQP value"); + sasl_frame_codec_instance->decode_state = SASL_FRAME_DECODE_ERROR; + } + else + { + frame_body_size--; + frame_body++; + } + } + + /* Codes_SRS_SASL_FRAME_CODEC_01_009: [The frame body of a SASL frame MUST contain exactly one AMQP type, whose type encoding MUST have provides="sasl-frame".] */ + if (frame_body_size > 0) + { + LogError("More than one AMQP value detected in SASL frame"); + sasl_frame_codec_instance->decode_state = SASL_FRAME_DECODE_ERROR; + + /* Codes_SRS_SASL_FRAME_CODEC_01_049: [If any error occurs while decoding a frame, the decoder shall call the on_sasl_frame_codec_error and pass to it the callback_context, both of those being the ones given to sasl_frame_codec_create.] */ + sasl_frame_codec_instance->on_sasl_frame_codec_error(sasl_frame_codec_instance->callback_context); + } + + if (sasl_frame_codec_instance->decode_state != SASL_FRAME_DECODE_ERROR) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_041: [Once the sasl frame is decoded, the callback on_sasl_frame_received shall be called.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_042: [The decoded sasl-frame value and the context passed in sasl_frame_codec_create shall be passed to on_sasl_frame_received.] */ + sasl_frame_codec_instance->on_sasl_frame_received(sasl_frame_codec_instance->callback_context, sasl_frame_codec_instance->decoded_sasl_frame_value); + } + break; + } + } +} + +static int encode_bytes(void* context, const unsigned char* bytes, size_t length) +{ + PAYLOAD* payload = (PAYLOAD*)context; + (void)memcpy((unsigned char*)payload->bytes + payload->length, bytes, length); + payload->length += length; + return 0; +} + +SASL_FRAME_CODEC_HANDLE sasl_frame_codec_create(FRAME_CODEC_HANDLE frame_codec, ON_SASL_FRAME_RECEIVED on_sasl_frame_received, ON_SASL_FRAME_CODEC_ERROR on_sasl_frame_codec_error, void* callback_context) +{ + SASL_FRAME_CODEC_INSTANCE* result; + + /* Codes_SRS_SASL_FRAME_CODEC_01_019: [If any of the arguments frame_codec, on_sasl_frame_received or on_sasl_frame_codec_error is NULL, sasl_frame_codec_create shall return NULL.] */ + if ((frame_codec == NULL) || + (on_sasl_frame_received == NULL) || + (on_sasl_frame_codec_error == NULL)) + { + LogError("Bad arguments: frame_codec = %p, on_sasl_frame_received = %p, on_sasl_frame_codec_error = %p", + frame_codec, on_sasl_frame_received, on_sasl_frame_codec_error); + result = NULL; + } + else + { + /* Codes_SRS_SASL_FRAME_CODEC_01_018: [sasl_frame_codec_create shall create an instance of an sasl_frame_codec and return a non-NULL handle to it.] */ + result = (SASL_FRAME_CODEC_INSTANCE*)malloc(sizeof(SASL_FRAME_CODEC_INSTANCE)); + if (result == NULL) + { + LogError("Cannot allocate memory for SASL frame codec"); + } + else + { + result->frame_codec = frame_codec; + result->on_sasl_frame_received = on_sasl_frame_received; + result->on_sasl_frame_codec_error = on_sasl_frame_codec_error; + result->callback_context = callback_context; + result->decode_state = SASL_FRAME_DECODE_FRAME; + + /* Codes_SRS_SASL_FRAME_CODEC_01_022: [sasl_frame_codec_create shall create a decoder to be used for decoding SASL values.] */ + result->decoder = amqpvalue_decoder_create(amqp_value_decoded, result); + if (result->decoder == NULL) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_023: [If creating the decoder fails, sasl_frame_codec_create shall fail and return NULL.] */ + LogError("Cannot create AMQP value decoder"); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_SASL_FRAME_CODEC_01_020: [sasl_frame_codec_create shall subscribe for SASL frames with the given frame_codec.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_001: [A SASL frame has a type code of 0x01.] */ + if (frame_codec_subscribe(frame_codec, FRAME_TYPE_SASL, frame_received, result) != 0) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_021: [If subscribing for SASL frames fails, sasl_frame_codec_create shall fail and return NULL.] */ + LogError("Cannot subscribe for SASL frames"); + amqpvalue_decoder_destroy(result->decoder); + free(result); + result = NULL; + } + } + } + } + + return result; +} + +void sasl_frame_codec_destroy(SASL_FRAME_CODEC_HANDLE sasl_frame_codec) +{ + /* Codes_SRS_SASL_FRAME_CODEC_01_026: [If sasl_frame_codec is NULL, sasl_frame_codec_destroy shall do nothing.] */ + if (sasl_frame_codec == NULL) + { + LogError("NULL sasl_frame_codec"); + } + else + { + /* Codes_SRS_SASL_FRAME_CODEC_01_025: [sasl_frame_codec_destroy shall free all resources associated with the sasl_frame_codec instance.] */ + SASL_FRAME_CODEC_INSTANCE* sasl_frame_codec_instance = (SASL_FRAME_CODEC_INSTANCE*)sasl_frame_codec; + + /* Codes_SRS_SASL_FRAME_CODEC_01_027: [sasl_frame_codec_destroy shall unsubscribe from receiving SASL frames from the frame_codec that was passed to sasl_frame_codec_create.] */ + (void)frame_codec_unsubscribe(sasl_frame_codec_instance->frame_codec, FRAME_TYPE_SASL); + + /* Codes_SRS_SASL_FRAME_CODEC_01_028: [The decoder created in sasl_frame_codec_create shall be destroyed by sasl_frame_codec_destroy.] */ + amqpvalue_decoder_destroy(sasl_frame_codec_instance->decoder); + free(sasl_frame_codec_instance); + } +} + +/* Codes_SRS_SASL_FRAME_CODEC_01_029: [sasl_frame_codec_encode_frame shall encode the frame header and sasl_frame_value AMQP value in a SASL frame and on success it shall return 0.] */ +int sasl_frame_codec_encode_frame(SASL_FRAME_CODEC_HANDLE sasl_frame_codec, AMQP_VALUE sasl_frame_value, ON_BYTES_ENCODED on_bytes_encoded, void* callback_context) +{ + int result; + SASL_FRAME_CODEC_INSTANCE* sasl_frame_codec_instance = (SASL_FRAME_CODEC_INSTANCE*)sasl_frame_codec; + + /* Codes_SRS_SASL_FRAME_CODEC_01_030: [If sasl_frame_codec or sasl_frame_value is NULL, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + if ((sasl_frame_codec == NULL) || + (sasl_frame_value == NULL)) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Bad arguments: sasl_frame_codec = %p, sasl_frame_value = %p", + sasl_frame_codec, sasl_frame_value); + result = __FAILURE__; + } + else + { + AMQP_VALUE descriptor; + uint64_t sasl_frame_descriptor_ulong; + size_t encoded_size; + + if ((descriptor = amqpvalue_get_inplace_descriptor(sasl_frame_value)) == NULL) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Cannot get SASL frame descriptor AMQP value"); + result = __FAILURE__; + } + else if (amqpvalue_get_ulong(descriptor, &sasl_frame_descriptor_ulong) != 0) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Cannot get SASL frame descriptor ulong"); + result = __FAILURE__; + } + /* Codes_SRS_SASL_FRAME_CODEC_01_047: [The frame body of a SASL frame MUST contain exactly one AMQP type, whose type encoding MUST have provides="sasl-frame".] */ + else if ((sasl_frame_descriptor_ulong < SASL_MECHANISMS) || + (sasl_frame_descriptor_ulong > SASL_OUTCOME)) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Bad SASL frame descriptor"); + result = __FAILURE__; + } + /* Codes_SRS_SASL_FRAME_CODEC_01_032: [The payload frame size shall be computed based on the encoded size of the sasl_frame_value and its fields.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_033: [The encoded size of the sasl_frame_value and its fields shall be obtained by calling amqpvalue_get_encoded_size.] */ + else if (amqpvalue_get_encoded_size(sasl_frame_value, &encoded_size) != 0) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Cannot get SASL frame encoded size"); + result = __FAILURE__; + } + /* Codes_SRS_SASL_FRAME_CODEC_01_016: [The maximum size of a SASL frame is defined by MIN-MAX-FRAME-SIZE.] */ + else if (encoded_size > MIX_MAX_FRAME_SIZE - 8) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("SASL frame encoded size too big"); + result = __FAILURE__; + } + else + { + unsigned char* sasl_frame_bytes = (unsigned char*)malloc(encoded_size); + if (sasl_frame_bytes == NULL) + { + LogError("Cannot allocate SASL frame bytes"); + result = __FAILURE__; + } + else + { + PAYLOAD payload; + + payload.bytes = sasl_frame_bytes; + payload.length = 0; + + if (amqpvalue_encode(sasl_frame_value, encode_bytes, &payload) != 0) + { + LogError("Cannot encode SASL frame value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_FRAME_CODEC_01_031: [sasl_frame_codec_encode_frame shall encode the frame header and its contents by using frame_codec_encode_frame.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_012: [Bytes 6 and 7 of the header are ignored.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_013: [Implementations SHOULD set these to 0x00.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_014: [The extended header is ignored.] */ + /* Codes_SRS_SASL_FRAME_CODEC_01_015: [Implementations SHOULD therefore set DOFF to 0x02.] */ + if (frame_codec_encode_frame(sasl_frame_codec_instance->frame_codec, FRAME_TYPE_SASL, &payload, 1, NULL, 0, on_bytes_encoded, callback_context) != 0) + { + /* Codes_SRS_SASL_FRAME_CODEC_01_034: [If any error occurs during encoding, sasl_frame_codec_encode_frame shall fail and return a non-zero value.] */ + LogError("Cannot encode SASL frame"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + free(sasl_frame_bytes); + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_mechanism.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_mechanism.h" + +typedef struct SASL_MECHANISM_INSTANCE_TAG +{ + const SASL_MECHANISM_INTERFACE_DESCRIPTION* sasl_mechanism_interface_description; + CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism_handle; +} SASL_MECHANISM_INSTANCE; + +SASL_MECHANISM_HANDLE saslmechanism_create(const SASL_MECHANISM_INTERFACE_DESCRIPTION* sasl_mechanism_interface_description, void* sasl_mechanism_create_parameters) +{ + SASL_MECHANISM_HANDLE result; + + /* Codes_SRS_SASL_MECHANISM_01_004: [If the argument `sasl_mechanism_interface_description` is NULL, `saslmechanism_create` shall return NULL.] */ + if (sasl_mechanism_interface_description == NULL) + { + LogError("NULL sasl_mechanism_interface_description"); + result = NULL; + } + /* Codes_SRS_SASL_MECHANISM_01_005: [If any `sasl_mechanism_interface_description` member is NULL, `saslmechanism_create` shall fail and return NULL.] */ + else if ((sasl_mechanism_interface_description->concrete_sasl_mechanism_create == NULL) || + (sasl_mechanism_interface_description->concrete_sasl_mechanism_destroy == NULL) || + (sasl_mechanism_interface_description->concrete_sasl_mechanism_get_init_bytes == NULL) || + (sasl_mechanism_interface_description->concrete_sasl_mechanism_get_mechanism_name == NULL)) + { + LogError("Bad interface, concrete_sasl_mechanism_create = %p, concrete_sasl_mechanism_destroy = %p, concrete_sasl_mechanism_get_init_bytes = %p, concrete_sasl_mechanism_get_mechanism_name = %p", + sasl_mechanism_interface_description->concrete_sasl_mechanism_create, + sasl_mechanism_interface_description->concrete_sasl_mechanism_destroy, + sasl_mechanism_interface_description->concrete_sasl_mechanism_get_init_bytes, + sasl_mechanism_interface_description->concrete_sasl_mechanism_get_mechanism_name); + result = NULL; + } + else + { + result = (SASL_MECHANISM_HANDLE)malloc(sizeof(SASL_MECHANISM_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_SASL_MECHANISM_01_006: [If allocating the memory needed for the SASL mechanism interface fails then `saslmechanism_create` shall fail and return NULL.] */ + LogError("Could not allocate memory for SASL mechanism"); + } + else + { + result->sasl_mechanism_interface_description = sasl_mechanism_interface_description; + + /* Codes_SRS_SASL_MECHANISM_01_002: [In order to instantiate the concrete SASL mechanism implementation the function `concrete_sasl_mechanism_create` from the `sasl_mechanism_interface_description` shall be called, passing the `sasl_mechanism_create_parameters` to it.] */ + result->concrete_sasl_mechanism_handle = result->sasl_mechanism_interface_description->concrete_sasl_mechanism_create((void*)sasl_mechanism_create_parameters); + if (result->concrete_sasl_mechanism_handle == NULL) + { + /* Codes_SRS_SASL_MECHANISM_01_003: [If the underlying `concrete_sasl_mechanism_create` call fails, `saslmechanism_create` shall return NULL.] */ + LogError("concrete_sasl_mechanism_create failed"); + free(result); + result = NULL; + } + } + } + + /* Codes_SRS_SASL_MECHANISM_01_001: [`saslmechanism_create` shall return on success a non-NULL handle to a new SASL mechanism interface.] */ + return result; +} + +void saslmechanism_destroy(SASL_MECHANISM_HANDLE sasl_mechanism) +{ + if (sasl_mechanism == NULL) + { + /* Codes_SRS_SASL_MECHANISM_01_009: [If the argument `sasl_mechanism` is NULL, `saslmechanism_destroy` shall do nothing.] */ + LogError("NULL sasl_mechanism"); + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_008: [`saslmechanism_destroy` shall also call the `concrete_sasl_mechanism_destroy` function that is member of the `sasl_mechanism_interface_description` argument passed to `saslmechanism_create`, while passing as argument to `concrete_sasl_mechanism_destroy` the result of the underlying concrete SASL mechanism handle.] */ + sasl_mechanism->sasl_mechanism_interface_description->concrete_sasl_mechanism_destroy(sasl_mechanism->concrete_sasl_mechanism_handle); + + /* Codes_SRS_SASL_MECHANISM_01_007: [`saslmechanism_destroy` shall free all resources associated with the SASL mechanism handle.] */ + free(sasl_mechanism); + } +} + +int saslmechanism_get_init_bytes(SASL_MECHANISM_HANDLE sasl_mechanism, SASL_MECHANISM_BYTES* init_bytes) +{ + int result; + + /* Codes_SRS_SASL_MECHANISM_01_012: [If the argument `sasl_mechanism` is NULL, `saslmechanism_get_init_bytes` shall fail and return a non-zero value.] */ + if (sasl_mechanism == NULL) + { + LogError("NULL sasl_mechanism"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_010: [`saslmechanism_get_init_bytes` shall call the specific `concrete_sasl_mechanism_get_init_bytes` function specified in `saslmechanism_create`, passing the `init_bytes` argument to it.] */ + if (sasl_mechanism->sasl_mechanism_interface_description->concrete_sasl_mechanism_get_init_bytes(sasl_mechanism->concrete_sasl_mechanism_handle, init_bytes) != 0) + { + /* Codes_SRS_SASL_MECHANISM_01_013: [If the underlying `concrete_sasl_mechanism_get_init_bytes` fails, `saslmechanism_get_init_bytes` shall fail and return a non-zero value.] */ + LogError("concrete_sasl_mechanism_get_init_bytes failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_011: [On success, `saslmechanism_get_init_bytes` shall return 0.] */ + result = 0; + } + } + + return result; +} + +const char* saslmechanism_get_mechanism_name(SASL_MECHANISM_HANDLE sasl_mechanism) +{ + const char* result; + + /* Codes_SRS_SASL_MECHANISM_01_016: [If the argument `sasl_mechanism` is NULL, `saslmechanism_get_mechanism_name` shall fail and return a non-zero value.] */ + if (sasl_mechanism == NULL) + { + LogError("NULL sasl_mechanism"); + result = NULL; + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_014: [`saslmechanism_get_mechanism_name` shall call the specific `concrete_sasl_mechanism_get_mechanism_name` function specified in `saslmechanism_create`.] */ + /* Codes_SRS_SASL_MECHANISM_01_015: [On success, `saslmechanism_get_mechanism_name` shall return a pointer to a string with the mechanism name.] */ + /* Codes_SRS_SASL_MECHANISM_01_017: [If the underlying `concrete_sasl_mechanism_get_mechanism_name` fails, `saslmechanism_get_mechanism_name` shall return NULL.] */ + result = sasl_mechanism->sasl_mechanism_interface_description->concrete_sasl_mechanism_get_mechanism_name(sasl_mechanism->concrete_sasl_mechanism_handle); + if (result == NULL) + { + LogError("concrete_sasl_mechanism_get_mechanism_name failed"); + } + } + + return result; +} + +int saslmechanism_challenge(SASL_MECHANISM_HANDLE sasl_mechanism, const SASL_MECHANISM_BYTES* challenge_bytes, SASL_MECHANISM_BYTES* response_bytes) +{ + int result; + + /* Codes_SRS_SASL_MECHANISM_01_020: [If the argument `sasl_mechanism` is NULL, `saslmechanism_challenge` shall fail and return a non-zero value.] */ + if (sasl_mechanism == NULL) + { + LogError("NULL sasl_mechanism"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_018: [`saslmechanism_challenge` shall call the specific `concrete_sasl_mechanism_challenge` function specified in `saslmechanism_create`, while passing the `challenge_bytes` and `response_bytes` arguments to it.] */ + if (sasl_mechanism->sasl_mechanism_interface_description->concrete_sasl_mechanism_challenge(sasl_mechanism->concrete_sasl_mechanism_handle, challenge_bytes, response_bytes) != 0) + { + /* Codes_SRS_SASL_MECHANISM_01_021: [If the underlying `concrete_sasl_mechanism_challenge` fails, `saslmechanism_challenge` shall fail and return a non-zero value.] */ + LogError("concrete_sasl_mechanism_challenge failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_MECHANISM_01_019: [On success, `saslmechanism_challenge` shall return 0.] */ + result = 0; + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_mssbcbs.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_mssbcbs.h" + +typedef struct SASL_MSSBCBS_INSTANCE_TAG +{ + unsigned char dummy; +} SASL_MSSBCBS_INSTANCE; + +static const SASL_MECHANISM_INTERFACE_DESCRIPTION saslmssbcbs_interface = +{ + saslmssbcbs_create, + saslmssbcbs_destroy, + saslmssbcbs_get_init_bytes, + saslmssbcbs_get_mechanism_name, + saslmssbcbs_challenge +}; + +CONCRETE_SASL_MECHANISM_HANDLE saslmssbcbs_create(void* config) +{ + (void)config; + return malloc(sizeof(SASL_MSSBCBS_INSTANCE)); +} + +void saslmssbcbs_destroy(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle) +{ + if (sasl_mechanism_concrete_handle != NULL) + { + free(sasl_mechanism_concrete_handle); + } +} + +int saslmssbcbs_get_init_bytes(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle, SASL_MECHANISM_BYTES* init_bytes) +{ + int result; + + if (sasl_mechanism_concrete_handle == NULL) + { + result = __FAILURE__; + } + else + { + init_bytes->bytes = NULL; + init_bytes->length = 0; + + result = 0; + } + + return result; +} + +const char* saslmssbcbs_get_mechanism_name(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism) +{ + const char* result; + + if (sasl_mechanism == NULL) + { + result = NULL; + } + else + { + result = "MSSBCBS"; + } + + return result; +} + +int saslmssbcbs_challenge(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism, const SASL_MECHANISM_BYTES* challenge_bytes, SASL_MECHANISM_BYTES* response_bytes) +{ + (void)concrete_sasl_mechanism; + (void)challenge_bytes; + (void)response_bytes; + return 0; +} + +const SASL_MECHANISM_INTERFACE_DESCRIPTION* saslmssbcbs_get_interface(void) +{ + return &saslmssbcbs_interface; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_plain.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_plain.h" + +typedef struct SASL_PLAIN_INSTANCE_TAG +{ + unsigned char* init_bytes; + uint32_t init_bytes_length; +} SASL_PLAIN_INSTANCE; + +CONCRETE_SASL_MECHANISM_HANDLE saslplain_create(void* config) +{ + SASL_PLAIN_INSTANCE* result; + + if (config == NULL) + { + /* Codes_SRS_SASL_PLAIN_01_003: [If the `config` argument is NULL, then `saslplain_create` shall fail and return NULL.] */ + LogError("NULL config"); + result = NULL; + } + else + { + SASL_PLAIN_CONFIG* sasl_plain_config = (SASL_PLAIN_CONFIG*)config; + + /* Codes_SRS_SASL_PLAIN_01_025: [ `authzid` shall be optional. ]*/ + if ((sasl_plain_config->authcid == NULL) || + (sasl_plain_config->passwd == NULL)) + { + /* Codes_SRS_SASL_PLAIN_01_004: [If either the `authcid` or `passwd` member of the `config` structure is NULL, then `saslplain_create` shall fail and return NULL.] */ + LogError("Bad configuration: authcid = %p, passwd = %p", + sasl_plain_config->authcid, sasl_plain_config->passwd); + result = NULL; + } + else + { + size_t authzid_length = sasl_plain_config->authzid == NULL ? 0 : strlen(sasl_plain_config->authzid); + size_t authcid_length = strlen(sasl_plain_config->authcid); + size_t passwd_length = strlen(sasl_plain_config->passwd); + + /* Codes_SRS_SASL_PLAIN_01_020: [ authcid = 1*SAFE ; MUST accept up to 255 octets] */ + if ((authcid_length > 255) || (authcid_length == 0) || + /* Codes_SRS_SASL_PLAIN_01_021: [ authzid = 1*SAFE ; MUST accept up to 255 octets] */ + (authzid_length > 255) || + /* Codes_SRS_SASL_PLAIN_01_022: [ passwd = 1*SAFE ; MUST accept up to 255 octets] */ + (passwd_length > 255) || (passwd_length == 0)) + { + LogError("Bad configuration: authcid length = %u, passwd length = %u", + (unsigned int)authcid_length, (unsigned int)passwd_length); + result = NULL; + } + else + { + /* Codes_SRS_SASL_PLAIN_01_001: [`saslplain_create` shall return on success a non-NULL handle to a new SASL plain mechanism.] */ + result = (SASL_PLAIN_INSTANCE*)malloc(sizeof(SASL_PLAIN_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_SASL_PLAIN_01_002: [If allocating the memory needed for the saslplain instance fails then `saslplain_create` shall return NULL.] */ + LogError("Cannot allocate memory for SASL plain instance"); + } + else + { + /* Ignore UTF8 for now */ + result->init_bytes = (unsigned char*)malloc(authzid_length + authcid_length + passwd_length + 2); + if (result->init_bytes == NULL) + { + /* Codes_SRS_SASL_PLAIN_01_002: [If allocating the memory needed for the saslplain instance fails then `saslplain_create` shall return NULL.] */ + LogError("Cannot allocate init bytes"); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_SASL_PLAIN_01_016: [The mechanism consists of a single message, a string of [UTF-8] encoded [Unicode] characters, from the client to the server.] */ + /* Codes_SRS_SASL_PLAIN_01_017: [The client presents the authorization identity (identity to act as), followed by a NUL (U+0000) character, followed by the authentication identity (identity whose password will be used), followed by a NUL (U+0000) character, followed by the clear-text password.] */ + /* Codes_SRS_SASL_PLAIN_01_019: [ message = [authzid] UTF8NUL authcid UTF8NUL passwd] */ + /* Codes_SRS_SASL_PLAIN_01_023: [The authorization identity (authzid), authentication identity (authcid), password (passwd), and NUL character deliminators SHALL be transferred as [UTF-8] encoded strings of [Unicode] characters.] */ + /* Codes_SRS_SASL_PLAIN_01_024: [As the NUL (U+0000) character is used as a deliminator, the NUL (U+0000) character MUST NOT appear in authzid, authcid, or passwd productions.] */ + + /* Codes_SRS_SASL_PLAIN_01_018: [As with other SASL mechanisms, the client does not provide an authorization identity when it wishes the server to derive an identity from the credentials and use that as the authorization identity.] */ + if (authzid_length > 0) + { + (void)memcpy(result->init_bytes, sasl_plain_config->authzid, authzid_length); + } + + result->init_bytes[authzid_length] = 0; + (void)memcpy(result->init_bytes + authzid_length + 1, sasl_plain_config->authcid, authcid_length); + result->init_bytes[authzid_length + authcid_length + 1] = 0; + (void)memcpy(result->init_bytes + authzid_length + authcid_length + 2, sasl_plain_config->passwd, passwd_length); + result->init_bytes_length = (uint32_t)(authzid_length + authcid_length + passwd_length + 2); + } + } + } + } + } + + return result; +} + +void saslplain_destroy(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle) +{ + if (sasl_mechanism_concrete_handle == NULL) + { + /* Codes_SRS_SASL_PLAIN_01_006: [If the argument `concrete_sasl_mechanism` is NULL, `saslplain_destroy` shall do nothing.] */ + LogError("NULL sasl_mechanism_concrete_handle"); + } + else + { + /* Codes_SRS_SASL_PLAIN_01_005: [`saslplain_destroy` shall free all resources associated with the SASL mechanism.] */ + SASL_PLAIN_INSTANCE* sasl_plain_instance = (SASL_PLAIN_INSTANCE*)sasl_mechanism_concrete_handle; + if (sasl_plain_instance->init_bytes != NULL) + { + free(sasl_plain_instance->init_bytes); + } + + free(sasl_plain_instance); + } +} + +int saslplain_get_init_bytes(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism_concrete_handle, SASL_MECHANISM_BYTES* init_bytes) +{ + int result; + + if ((sasl_mechanism_concrete_handle == NULL) || + (init_bytes == NULL)) + { + /* Codes_SRS_SASL_PLAIN_01_009: [If any argument is NULL, `saslplain_get_init_bytes` shall return a non-zero value.] */ + LogError("Bad arguments: sasl_mechanism_concrete_handle = %p, init_bytes = %p", + sasl_mechanism_concrete_handle, init_bytes); + result = __FAILURE__; + } + else + { + SASL_PLAIN_INSTANCE* sasl_plain_instance = (SASL_PLAIN_INSTANCE*)sasl_mechanism_concrete_handle; + + /* Codes_SRS_SASL_PLAIN_01_007: [`saslplain_get_init_bytes` shall construct the initial bytes per the RFC 4616.] */ + init_bytes->bytes = sasl_plain_instance->init_bytes; + init_bytes->length = sasl_plain_instance->init_bytes_length; + + /* Codes_SRS_SASL_PLAIN_01_008: [On success `saslplain_get_init_bytes` shall return zero.] */ + result = 0; + } + + return result; +} + +const char* saslplain_get_mechanism_name(CONCRETE_SASL_MECHANISM_HANDLE sasl_mechanism) +{ + const char* result; + + if (sasl_mechanism == NULL) + { + /* Codes_SRS_SASL_PLAIN_01_011: [If the argument `concrete_sasl_mechanism` is NULL, `saslplain_get_mechanism_name` shall return NULL.] */ + LogError("NULL sasl_mechanism"); + result = NULL; + } + else + { + /* Codes_SRS_SASL_PLAIN_01_010: [`saslplain_get_mechanism_name` shall validate the argument `concrete_sasl_mechanism` and on success it shall return a pointer to the string "PLAIN".] */ + result = "PLAIN"; + } + + return result; +} + +int saslplain_challenge(CONCRETE_SASL_MECHANISM_HANDLE concrete_sasl_mechanism, const SASL_MECHANISM_BYTES* challenge_bytes, SASL_MECHANISM_BYTES* response_bytes) +{ + int result; + + (void)challenge_bytes; + + /* Codes_SRS_SASL_PLAIN_01_014: [If the `concrete_sasl_mechanism` or `response_bytes` argument is NULL then `saslplain_challenge` shall fail and return a non-zero value.] */ + if ((concrete_sasl_mechanism == NULL) || + (response_bytes == NULL)) + { + LogError("Bad arguments: concrete_sasl_mechanism = %p, response_bytes = %p", + concrete_sasl_mechanism, response_bytes); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_PLAIN_01_012: [`saslplain_challenge` shall set the `response_bytes` buffer to NULL and 0 size as the PLAIN SASL mechanism does not implement challenge/response.] */ + response_bytes->bytes = NULL; + response_bytes->length = 0; + + /* Codes_SRS_SASL_PLAIN_01_013: [On success, `saslplain_challenge` shall return 0.] */ + result = 0; + } + + return result; +} + +static const SASL_MECHANISM_INTERFACE_DESCRIPTION saslplain_interface = +{ + /* Codes_SRS_SASL_PLAIN_01_015: [**`saslplain_get_interface` shall return a pointer to a `SASL_MECHANISM_INTERFACE_DESCRIPTION` structure that contains pointers to the functions: `saslplain_create`, `saslplain_destroy`, `saslplain_get_init_bytes`, `saslplain_get_mechanism_name`, `saslplain_challenge`.] */ + saslplain_create, + saslplain_destroy, + saslplain_get_init_bytes, + saslplain_get_mechanism_name, + saslplain_challenge +}; + +const SASL_MECHANISM_INTERFACE_DESCRIPTION* saslplain_get_interface(void) +{ + return &saslplain_interface; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/sasl_server_mechanism.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/sasl_server_mechanism.h" + +typedef struct SASL_SERVER_MECHANISM_INSTANCE_TAG +{ + const SASL_SERVER_MECHANISM_INTERFACE_DESCRIPTION* sasl_server_mechanism_interface_description; + CONCRETE_SASL_SERVER_MECHANISM_HANDLE concrete_sasl_server_mechanism; +} SASL_SERVER_MECHANISM_INSTANCE; + +SASL_SERVER_MECHANISM_HANDLE sasl_server_mechanism_create(const SASL_SERVER_MECHANISM_INTERFACE_DESCRIPTION* sasl_server_mechanism_interface_description, void* sasl_server_mechanism_create_parameters) +{ + SASL_SERVER_MECHANISM_HANDLE result; + + if (sasl_server_mechanism_interface_description == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_004: [ If the argument `sasl_server_mechanism_interface_description` is NULL, `sasl_server_mechanism_create` shall return NULL.]*/ + LogError("NULL sasl_server_mechanism_interface_description"); + result = NULL; + } + else if ((sasl_server_mechanism_interface_description->create == NULL) || + (sasl_server_mechanism_interface_description->destroy == NULL) || + (sasl_server_mechanism_interface_description->handle_initial_response == NULL) || + (sasl_server_mechanism_interface_description->handle_response == NULL) || + (sasl_server_mechanism_interface_description->get_mechanism_name == NULL)) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_005: [ If any `sasl_server_mechanism_interface_description` member is NULL, `sasl_server_mechanism_create` shall fail and return NULL.]*/ + LogError("Bad interface, create = %p, destroy = %p, handle_initial_response = %p, handle_response = %p, get_mechanism_name = %p", + sasl_server_mechanism_interface_description->create, + sasl_server_mechanism_interface_description->destroy, + sasl_server_mechanism_interface_description->handle_initial_response, + sasl_server_mechanism_interface_description->handle_response, + sasl_server_mechanism_interface_description->get_mechanism_name); + result = NULL; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_001: [`sasl_server_mechanism_create` shall return on success a non-NULL handle to a new SASL server mechanism interface.]*/ + result = (SASL_SERVER_MECHANISM_HANDLE)malloc(sizeof(SASL_SERVER_MECHANISM_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_006: [ If allocating the memory needed for the SASL server mechanism interface fails then `sasl_server_mechanism_create` shall fail and return NULL. ]*/ + LogError("Could not allocate memory for SASL mechanism"); + } + else + { + result->sasl_server_mechanism_interface_description = sasl_server_mechanism_interface_description; + + /* Codes_SRS_SASL_SERVER_MECHANISM_01_002: [ In order to instantiate the concrete SASL server mechanism implementation the function `create` from the `sasl_server_mechanism_interface_description` shall be called, passing the `sasl_server_mechanism_create_parameters` to it.]*/ + result->concrete_sasl_server_mechanism = result->sasl_server_mechanism_interface_description->create(sasl_server_mechanism_create_parameters); + if (result->concrete_sasl_server_mechanism == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_003: [ If the underlying `create` call fails, `sasl_server_mechanism_create` shall return NULL. ]*/ + LogError("concrete sasl server mechanism create failed"); + free(result); + result = NULL; + } + } + } + + return result; +} + +void sasl_server_mechanism_destroy(SASL_SERVER_MECHANISM_HANDLE sasl_server_mechanism) +{ + if (sasl_server_mechanism == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_009: [ If the argument `sasl_server_mechanism` is NULL, `sasl_server_mechanism_destroy` shall do nothing. ]*/ + LogError("NULL sasl_server_mechanism"); + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_008: [ `sasl_server_mechanism_destroy` shall also call the `destroy` function that is member of the `sasl_mechanism_interface_description` argument passed to `sasl_server_mechanism_create`, while passing as argument to `destroy` the result of the underlying concrete SASL mechanism handle. ]*/ + sasl_server_mechanism->sasl_server_mechanism_interface_description->destroy(sasl_server_mechanism->concrete_sasl_server_mechanism); + /* Codes_SRS_SASL_SERVER_MECHANISM_01_007: [ `sasl_server_mechanism_destroy` shall free all resources associated with the SASL mechanism handle. ]*/ + free(sasl_server_mechanism); + } +} + +int sasl_server_mechanism_handle_initial_response(SASL_SERVER_MECHANISM_HANDLE sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES* initial_response_bytes, const char* hostname, bool* send_challenge, SASL_SERVER_MECHANISM_BYTES* challenge_bytes) +{ + int result; + + if (sasl_server_mechanism == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_012: [ If the argument `sasl_server_mechanism` is NULL, `sasl_server_mechanism_handle_initial_response` shall fail and return a non-zero value. ]*/ + LogError("NULL sasl_server_mechanism"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_010: [ `sasl_server_mechanism_handle_initial_response` shall call the specific `handle_initial_response` function specified in `sasl_server_mechanism_create`, passing the `initial_response_bytes`, `hostname`, `send_challenge` and `challenge_bytes` arguments to it. ]*/ + if (sasl_server_mechanism->sasl_server_mechanism_interface_description->handle_initial_response(sasl_server_mechanism->concrete_sasl_server_mechanism, initial_response_bytes, hostname, send_challenge, challenge_bytes) != 0) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_013: [ If the underlying `handle_initial_response` fails, `sasl_server_mechanism_handle_initial_response` shall fail and return a non-zero value. ]*/ + LogError("handle_initial_response_failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_011: [ On success, `sasl_server_mechanism_handle_initial_response` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +int sasl_server_mechanism_handle_response(SASL_SERVER_MECHANISM_HANDLE sasl_server_mechanism, const SASL_SERVER_MECHANISM_BYTES* response_bytes, bool* send_next_challenge, SASL_SERVER_MECHANISM_BYTES* next_challenge_bytes) +{ + int result; + + if (sasl_server_mechanism == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_017: [ If the argument `sasl_server_mechanism` is NULL, `sasl_server_mechanism_handle_response` shall fail and return a non-zero value. ]*/ + LogError("NULL sasl_server_mechanism"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_014: [ `sasl_server_mechanism_handle_response` shall call the specific `handle_response` function specified in `sasl_server_mechanism_create`, passing the `response_bytes`, `send_next_challenge` and `next_challenge_bytes` arguments to it. ]*/ + if (sasl_server_mechanism->sasl_server_mechanism_interface_description->handle_response(sasl_server_mechanism->concrete_sasl_server_mechanism, response_bytes, send_next_challenge, next_challenge_bytes) != 0) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_018: [ If the underlying `handle_response` fails, `sasl_server_mechanism_handle_response` shall fail and return a non-zero value. ]*/ + LogError("handle_response_failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_016: [ On success, `sasl_server_mechanism_handle_response` shall return 0. ]*/ + result = 0; + } + } + + return result; +} + +const char* sasl_server_mechanism_get_mechanism_name(SASL_SERVER_MECHANISM_HANDLE sasl_server_mechanism) +{ + const char* result; + + if (sasl_server_mechanism == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_021: [ If the argument `sasl_server_mechanism` is NULL, `sasl_server_mechanism_get_mechanism_name` shall fail and return a non-zero value. ]*/ + LogError("NULL sasl_server_mechanism"); + result = NULL; + } + else + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_019: [ `sasl_server_mechanism_get_mechanism_name` shall call the specific `get_mechanism_name` function specified in `sasl_server_mechanism_create`. ]*/ + /* Codes_SRS_SASL_SERVER_MECHANISM_01_020: [ On success, `sasl_server_mechanism_get_mechanism_name` shall return a pointer to a string with the mechanism name. ]*/ + result = sasl_server_mechanism->sasl_server_mechanism_interface_description->get_mechanism_name(); + if (result == NULL) + { + /* Codes_SRS_SASL_SERVER_MECHANISM_01_022: [ If the underlying `get_mechanism_name` fails, `sasl_server_mechanism_get_mechanism_name` shall return NULL. ]*/ + LogError("concrete_sasl_mechanism_get_mechanism_name failed"); + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/saslclientio.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1347 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_uamqp_c/saslclientio.h" +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_uamqp_c/frame_codec.h" +#include "azure_uamqp_c/sasl_frame_codec.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include "azure_uamqp_c/amqpvalue_to_string.h" + +typedef enum IO_STATE_TAG +{ + IO_STATE_NOT_OPEN, + IO_STATE_OPENING_UNDERLYING_IO, + IO_STATE_SASL_HANDSHAKE, + IO_STATE_OPEN, + IO_STATE_CLOSING, + IO_STATE_ERROR +} IO_STATE; + +#define SASL_HEADER_EXCHANGE_STATE_VALUES \ + SASL_HEADER_EXCHANGE_IDLE, \ + SASL_HEADER_EXCHANGE_HEADER_SENT, \ + SASL_HEADER_EXCHANGE_HEADER_RCVD, \ + SASL_HEADER_EXCHANGE_HEADER_EXCH + +// Suppress unused function warning for SASL_HEADER_EXCHANGE_STATEstrings +#ifdef NO_LOGGING +#define ENUM_TO_STRING_UNUSED +#include "azure_c_shared_utility/macro_utils.h" +#endif + +DEFINE_LOCAL_ENUM(SASL_HEADER_EXCHANGE_STATE, SASL_HEADER_EXCHANGE_STATE_VALUES) + +#define SASL_CLIENT_NEGOTIATION_STATE_VALUES \ + SASL_CLIENT_NEGOTIATION_NOT_STARTED, \ + SASL_CLIENT_NEGOTIATION_MECH_RCVD, \ + SASL_CLIENT_NEGOTIATION_INIT_SENT, \ + SASL_CLIENT_NEGOTIATION_CHALLENGE_RCVD, \ + SASL_CLIENT_NEGOTIATION_RESPONSE_SENT, \ + SASL_CLIENT_NEGOTIATION_OUTCOME_RCVD, \ + SASL_CLIENT_NEGOTIATION_ERROR + +DEFINE_LOCAL_ENUM(SASL_CLIENT_NEGOTIATION_STATE, SASL_CLIENT_NEGOTIATION_STATE_VALUES) + +typedef struct SASL_CLIENT_IO_INSTANCE_TAG +{ + XIO_HANDLE underlying_io; + ON_BYTES_RECEIVED on_bytes_received; + ON_IO_OPEN_COMPLETE on_io_open_complete; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + ON_IO_ERROR on_io_error; + void* on_bytes_received_context; + void* on_io_open_complete_context; + void* on_io_close_complete_context; + void* on_io_error_context; + SASL_HEADER_EXCHANGE_STATE sasl_header_exchange_state; + SASL_CLIENT_NEGOTIATION_STATE sasl_client_negotiation_state; + size_t header_bytes_received; + SASL_FRAME_CODEC_HANDLE sasl_frame_codec; + FRAME_CODEC_HANDLE frame_codec; + IO_STATE io_state; + SASL_MECHANISM_HANDLE sasl_mechanism; + unsigned int is_trace_on : 1; + unsigned int is_trace_on_set : 1; +} SASL_CLIENT_IO_INSTANCE; + +/* Codes_SRS_SASLCLIENTIO_01_002: [The protocol header consists of the upper case ASCII letters "AMQP" followed by a protocol id of three, followed by three unsigned bytes representing the major, minor, and revision of the specification version (currently 1 (SASL-MAJOR), 0 (SASLMINOR), 0 (SASL-REVISION)).] */ +/* Codes_SRS_SASLCLIENTIO_01_124: [SASL-MAJOR 1 major protocol version.] */ +/* Codes_SRS_SASLCLIENTIO_01_125: [SASL-MINOR 0 minor protocol version.] */ +/* Codes_SRS_SASLCLIENTIO_01_126: [SASL-REVISION 0 protocol revision.] */ +static const unsigned char sasl_header[] = { 'A', 'M', 'Q', 'P', 3, 1, 0, 0 }; + +static void indicate_error(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance) +{ + if (sasl_client_io_instance->on_io_error != NULL) + { + sasl_client_io_instance->on_io_error(sasl_client_io_instance->on_io_error_context); + } +} + +static void indicate_open_complete(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance, IO_OPEN_RESULT open_result) +{ + if (sasl_client_io_instance->on_io_open_complete != NULL) + { + sasl_client_io_instance->on_io_open_complete(sasl_client_io_instance->on_io_open_complete_context, open_result); + } +} + +static void indicate_close_complete(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance) +{ + if (sasl_client_io_instance->on_io_close_complete != NULL) + { + sasl_client_io_instance->on_io_close_complete(sasl_client_io_instance->on_io_close_complete_context); + } +} + +static void on_underlying_io_close_complete(void* context) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + switch (sasl_client_io_instance->io_state) + { + default: + break; + + case IO_STATE_OPENING_UNDERLYING_IO: + case IO_STATE_SASL_HANDSHAKE: + sasl_client_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(sasl_client_io_instance, IO_OPEN_ERROR); + break; + + case IO_STATE_CLOSING: + sasl_client_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_close_complete(sasl_client_io_instance); + break; + } +} + +static void handle_error(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance) +{ + switch (sasl_client_io_instance->io_state) + { + default: + case IO_STATE_NOT_OPEN: + break; + + case IO_STATE_OPENING_UNDERLYING_IO: + case IO_STATE_SASL_HANDSHAKE: + if (xio_close(sasl_client_io_instance->underlying_io, on_underlying_io_close_complete, sasl_client_io_instance) != 0) + { + sasl_client_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(sasl_client_io_instance, IO_OPEN_ERROR); + } + break; + + case IO_STATE_OPEN: + sasl_client_io_instance->io_state = IO_STATE_ERROR; + indicate_error(sasl_client_io_instance); + break; + } +} + +// This callback usage needs to be either verified and commented or integrated into +// the state machine. +static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + (void)context; + (void)send_result; +} + +static int send_sasl_header(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance) +{ + int result; + + /* Codes_SRS_SASLCLIENTIO_01_078: [SASL client IO shall start the header exchange by sending the SASL header.] */ + /* Codes_SRS_SASLCLIENTIO_01_095: [Sending the header shall be done by using `xio_send`.]*/ + if (xio_send(sasl_client_io_instance->underlying_io, sasl_header, sizeof(sasl_header), unchecked_on_send_complete, NULL) != 0) + { + LogError("Sending SASL header failed"); + result = __FAILURE__; + } + else + { + if (sasl_client_io_instance->is_trace_on != 0) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "-> Header (AMQP 3.1.0.0)"); + } + + result = 0; + } + + return result; +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + switch (sasl_client_io_instance->io_state) + { + default: + LogError("Open complete received in unexpected state"); + break; + + case IO_STATE_SASL_HANDSHAKE: + /* Codes_SRS_SASLCLIENTIO_01_110: [raise ERROR] */ + case IO_STATE_OPEN: + /* Codes_SRS_SASLCLIENTIO_01_106: [raise error] */ + handle_error(sasl_client_io_instance); + break; + + case IO_STATE_OPENING_UNDERLYING_IO: + if (open_result == IO_OPEN_OK) + { + sasl_client_io_instance->io_state = IO_STATE_SASL_HANDSHAKE; + if (sasl_client_io_instance->sasl_header_exchange_state != SASL_HEADER_EXCHANGE_IDLE) + { + /* Codes_SRS_SASLCLIENTIO_01_116: [If the underlying IO indicates another open while the after the header exchange has been started an error shall be indicated by calling `on_io_error`.]*/ + handle_error(sasl_client_io_instance); + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_105: [start header exchange] */ + /* Codes_SRS_SASLCLIENTIO_01_001: [To establish a SASL layer, each peer MUST start by sending a protocol header.] */ + if (send_sasl_header(sasl_client_io_instance) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_073: [If the handshake fails (i.e. the outcome is an error) the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + /* Codes_SRS_SASLCLIENTIO_01_077: [If sending the SASL header fails, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + handle_error(sasl_client_io_instance); + } + else + { + sasl_client_io_instance->sasl_header_exchange_state = SASL_HEADER_EXCHANGE_HEADER_SENT; + } + } + } + else + { + handle_error(sasl_client_io_instance); + } + + break; + } +} + +static void on_underlying_io_error(void* context) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + switch (sasl_client_io_instance->io_state) + { + default: + LogError("Error callback received in unexpected state"); + break; + + case IO_STATE_OPENING_UNDERLYING_IO: + case IO_STATE_SASL_HANDSHAKE: + /* Codes_SRS_SASLCLIENTIO_01_101: [`on_open_complete` with `IO_OPEN_ERROR`]*/ + if (xio_close(sasl_client_io_instance->underlying_io, on_underlying_io_close_complete, sasl_client_io_instance) != 0) + { + sasl_client_io_instance->io_state = IO_STATE_NOT_OPEN; + indicate_open_complete(sasl_client_io_instance, IO_OPEN_ERROR); + } + + break; + + case IO_STATE_OPEN: + sasl_client_io_instance->io_state = IO_STATE_ERROR; + indicate_error(sasl_client_io_instance); + break; + } +} + +#ifndef NO_LOGGING +static const char* get_frame_type_as_string(AMQP_VALUE descriptor) +{ + const char* result; + + if (is_sasl_mechanisms_type_by_descriptor(descriptor)) + { + result = "[SASL MECHANISMS]"; + } + else if (is_sasl_init_type_by_descriptor(descriptor)) + { + result = "[SASL INIT]"; + } + else if (is_sasl_challenge_type_by_descriptor(descriptor)) + { + result = "[SASL CHALLENGE]"; + } + else if (is_sasl_response_type_by_descriptor(descriptor)) + { + result = "[SASL RESPONSE]"; + } + else if (is_sasl_outcome_type_by_descriptor(descriptor)) + { + result = "[SASL OUTCOME]"; + } + else + { + result = "[Unknown]"; + } + + return result; +} +#endif // NO_LOGGING + +static void log_incoming_frame(AMQP_VALUE performative) +{ +#ifdef NO_LOGGING + UNUSED(performative); +#else + if (xlogging_get_log_function() != NULL) + { + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + if (descriptor != NULL) + { + char* performative_as_string; + LOG(AZ_LOG_TRACE, 0, "<- "); + LOG(AZ_LOG_TRACE, 0, (char*)get_frame_type_as_string(descriptor)); + performative_as_string = NULL; + LOG(AZ_LOG_TRACE, LOG_LINE, (performative_as_string = amqpvalue_to_string(performative))); + if (performative_as_string != NULL) + { + free(performative_as_string); + } + } + } +#endif +} + +static void log_outgoing_frame(AMQP_VALUE performative) +{ +#ifdef NO_LOGGING + UNUSED(performative); +#else + if (xlogging_get_log_function() != NULL) + { + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + if (descriptor != NULL) + { + char* performative_as_string; + LOG(AZ_LOG_TRACE, 0, "-> "); + LOG(AZ_LOG_TRACE, 0, (char*)get_frame_type_as_string(descriptor)); + performative_as_string = NULL; + LOG(AZ_LOG_TRACE, LOG_LINE, (performative_as_string = amqpvalue_to_string(performative))); + if (performative_as_string != NULL) + { + free(performative_as_string); + } + } + } +#endif +} + +static int saslclientio_receive_byte(SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance, unsigned char b) +{ + int result; + + switch (sasl_client_io_instance->sasl_header_exchange_state) + { + default: + LogError("Byte being received in unexpected state: %s", ENUM_TO_STRING(SASL_HEADER_EXCHANGE_STATE, sasl_client_io_instance->sasl_header_exchange_state)); + result = __FAILURE__; + break; + + case SASL_HEADER_EXCHANGE_HEADER_EXCH: + switch (sasl_client_io_instance->sasl_client_negotiation_state) + { + case SASL_CLIENT_NEGOTIATION_ERROR: + LogError("Byte being received in unexpected state: %s", ENUM_TO_STRING(SASL_CLIENT_NEGOTIATION_STATE, SASL_CLIENT_NEGOTIATION_ERROR)); + result = __FAILURE__; + break; + + default: + /* Codes_SRS_SASLCLIENTIO_01_068: [During the SASL frame exchange that constitutes the handshake the received bytes from the underlying IO shall be fed to the frame codec instance created in `saslclientio_create` by calling `frame_codec_receive_bytes`.]*/ + if (frame_codec_receive_bytes(sasl_client_io_instance->frame_codec, &b, 1) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_088: [If `frame_codec_receive_bytes` fails, the `on_io_error` callback shall be triggered.]*/ + result = __FAILURE__; + } + else + { + result = 0; + } + + break; + + case SASL_CLIENT_NEGOTIATION_OUTCOME_RCVD: + sasl_client_io_instance->on_bytes_received(sasl_client_io_instance->on_bytes_received_context, &b, 1); + result = 0; + break; + } + + break; + + /* Codes_SRS_SASLCLIENTIO_01_003: [Other than using a protocol id of three, the exchange of SASL layer headers follows the same rules specified in the version negotiation section of the transport specification (See Part 2: section 2.2).] */ + case SASL_HEADER_EXCHANGE_IDLE: + case SASL_HEADER_EXCHANGE_HEADER_SENT: + if (b != sasl_header[sasl_client_io_instance->header_bytes_received]) + { + LogError("Mismatched SASL header"); + result = __FAILURE__; + } + else + { + sasl_client_io_instance->header_bytes_received++; + if (sasl_client_io_instance->header_bytes_received == sizeof(sasl_header)) + { + if (sasl_client_io_instance->is_trace_on != 0) + { + LOG(AZ_LOG_TRACE, LOG_LINE, "<- Header (AMQP 3.1.0.0)"); + } + + switch (sasl_client_io_instance->sasl_header_exchange_state) + { + default: + LogError("Invalid SASL header exchange state: %s", ENUM_TO_STRING(SASL_HEADER_EXCHANGE_STATE, sasl_client_io_instance->sasl_header_exchange_state)); + result = __FAILURE__; + break; + + case SASL_HEADER_EXCHANGE_HEADER_SENT: + /* from this point on we need to decode SASL frames */ + sasl_client_io_instance->sasl_header_exchange_state = SASL_HEADER_EXCHANGE_HEADER_EXCH; + result = 0; + break; + + case SASL_HEADER_EXCHANGE_IDLE: + sasl_client_io_instance->sasl_header_exchange_state = SASL_HEADER_EXCHANGE_HEADER_RCVD; + if (send_sasl_header(sasl_client_io_instance) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_077: [If sending the SASL header fails, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not send SASL header"); + result = __FAILURE__; + } + else + { + result = 0; + } + + break; + } + } + else + { + result = 0; + } + } + + break; + } + + return result; +} + +static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + /* Codes_SRS_SASLCLIENTIO_01_028: [If `buffer` is NULL or `size` is zero, nothing should be indicated as received, the state shall be switched to ERROR and the `on_io_error` callback shall be triggered.]*/ + if ((buffer == NULL) || + (size == 0)) + { + LogError("Bad buffer received from the underlying IO, buffer = %p, size = %u", + buffer, (unsigned int)size); + handle_error(sasl_client_io_instance); + } + else + { + switch (sasl_client_io_instance->io_state) + { + default: + break; + + case IO_STATE_OPEN: + /* Codes_SRS_SASLCLIENTIO_01_027: [When the `on_underlying_io_bytes_received` callback passed to the underlying IO is called and the SASL client IO state is `IO_STATE_OPEN`, the bytes shall be indicated to the user of SASL client IO by calling the `on_bytes_received` callback that was passed in `saslclientio_open`.]*/ + /* Codes_SRS_SASLCLIENTIO_01_029: [The `context` argument for `on_io_error` shall be set to the `on_io_error_context` passed in `saslclientio_open`.]*/ + sasl_client_io_instance->on_bytes_received(sasl_client_io_instance->on_bytes_received_context, buffer, size); + break; + + case IO_STATE_SASL_HANDSHAKE: + { + size_t i; + + /* Codes_SRS_SASLCLIENTIO_01_030: [If bytes are received when the SASL client IO state is `IO_STATE_OPENING`, the bytes shall be consumed by the SASL client IO to satisfy the SASL handshake.]*/ + for (i = 0; i < size; i++) + { + if (saslclientio_receive_byte(sasl_client_io_instance, buffer[i]) != 0) + { + break; + } + } + + if (i < size) + { + /* Codes_SRS_SASLCLIENTIO_01_073: [If the handshake fails (i.e. the outcome is an error) the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + handle_error(sasl_client_io_instance); + } + + break; + } + + case IO_STATE_ERROR: + /* Codes_SRS_SASLCLIENTIO_01_031: [If bytes are received when the SASL client IO state is `IO_STATE_ERROR`, SASL client IO shall do nothing.]*/ + break; + } + } +} + +static void on_bytes_encoded(void* context, const unsigned char* bytes, size_t length, bool encode_complete) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + (void)encode_complete; + + /* Codes_SRS_SASLCLIENTIO_01_120: [When SASL client IO is notified by `sasl_frame_codec` of bytes that have been encoded via the `on_bytes_encoded` callback and SASL client IO is in the state OPENING, SASL client IO shall send these bytes by using `xio_send`.]*/ + if (xio_send(sasl_client_io_instance->underlying_io, bytes, length, unchecked_on_send_complete, NULL) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_121: [If `xio_send` fails, the `on_io_error` callback shall be triggered.]*/ + LogError("xio_send failed"); + handle_error(sasl_client_io_instance); + } +} + +static int send_sasl_init(SASL_CLIENT_IO_INSTANCE* sasl_client_io, const char* sasl_mechanism_name) +{ + int result; + + SASL_INIT_HANDLE sasl_init; + SASL_MECHANISM_BYTES init_bytes; + + init_bytes.length = 0; + init_bytes.bytes = NULL; + + /* Codes_SRS_SASLCLIENTIO_01_045: [The name of the SASL mechanism used for the SASL exchange.] */ + sasl_init = sasl_init_create(sasl_mechanism_name); + if (sasl_init == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not create sasl_init"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_048: [The contents of this data are defined by the SASL security mechanism.] */ + if (saslmechanism_get_init_bytes(sasl_client_io->sasl_mechanism, &init_bytes) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not get SASL init bytes"); + result = __FAILURE__; + } + else + { + amqp_binary creds; + creds.bytes = init_bytes.bytes; + creds.length = init_bytes.length; + if ((init_bytes.length > 0) && + /* Codes_SRS_SASLCLIENTIO_01_047: [A block of opaque data passed to the security mechanism.] */ + (sasl_init_set_initial_response(sasl_init, creds) != 0)) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not set initial response"); + result = __FAILURE__; + } + else + { + AMQP_VALUE sasl_init_value = amqpvalue_create_sasl_init(sasl_init); + if (sasl_init_value == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not create SASL init"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_070: [When a frame needs to be sent as part of the SASL handshake frame exchange, the send shall be done by calling `sasl_frame_codec_encode_frame`.]*/ + if (sasl_frame_codec_encode_frame(sasl_client_io->sasl_frame_codec, sasl_init_value, on_bytes_encoded, sasl_client_io) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_071: [If `sasl_frame_codec_encode_frame` fails, then the `on_io_error` callback shall be triggered.]*/ + LogError("Could not encode SASL init value"); + result = __FAILURE__; + } + else + { + if (sasl_client_io->is_trace_on != 0) + { + log_outgoing_frame(sasl_init_value); + } + + result = 0; + } + + amqpvalue_destroy(sasl_init_value); + } + } + } + + sasl_init_destroy(sasl_init); + } + + return result; +} + +static int send_sasl_response(SASL_CLIENT_IO_INSTANCE* sasl_client_io, SASL_MECHANISM_BYTES sasl_response) +{ + int result; + + SASL_RESPONSE_HANDLE sasl_response_handle; + amqp_binary response_binary_value; + + response_binary_value.bytes = sasl_response.bytes; + response_binary_value.length = sasl_response.length; + + /* Codes_SRS_SASLCLIENTIO_01_055: [Send the SASL response data as defined by the SASL specification.] */ + /* Codes_SRS_SASLCLIENTIO_01_056: [A block of opaque data passed to the security mechanism.] */ + if ((sasl_response_handle = sasl_response_create(response_binary_value)) == NULL) + { + LogError("Could not create SASL response"); + result = __FAILURE__; + } + else + { + AMQP_VALUE sasl_response_value = amqpvalue_create_sasl_response(sasl_response_handle); + if (sasl_response_value == NULL) + { + LogError("Could not create SASL response AMQP value"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_070: [When a frame needs to be sent as part of the SASL handshake frame exchange, the send shall be done by calling `sasl_frame_codec_encode_frame`.]*/ + if (sasl_frame_codec_encode_frame(sasl_client_io->sasl_frame_codec, sasl_response_value, on_bytes_encoded, sasl_client_io) != 0) + { + LogError("Could not encode SASL response in the frame"); + result = __FAILURE__; + } + else + { + if (sasl_client_io->is_trace_on != 0) + { + log_outgoing_frame(sasl_response_value); + } + + result = 0; + } + + amqpvalue_destroy(sasl_response_value); + } + + sasl_response_destroy(sasl_response_handle); + } + + return result; +} + +static void on_sasl_frame_received_callback(void* context, AMQP_VALUE sasl_frame) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + /* Codes_SRS_SASLCLIENTIO_01_067: [The SASL frame exchange shall be started as soon as the SASL header handshake is done.] */ + switch (sasl_client_io_instance->io_state) + { + default: + LogError("SASL frame received while in state %d", (int)sasl_client_io_instance->io_state); + break; + + case IO_STATE_OPEN: + case IO_STATE_OPENING_UNDERLYING_IO: + case IO_STATE_CLOSING: + /* Codes_SRS_SASLCLIENTIO_01_117: [If `on_sasl_frame_received_callback` is called when the state of the IO is OPEN then the `on_io_error` callback shall be triggered.]*/ + handle_error(sasl_client_io_instance); + break; + + case IO_STATE_SASL_HANDSHAKE: + if (sasl_client_io_instance->sasl_header_exchange_state != SASL_HEADER_EXCHANGE_HEADER_EXCH) + { + /* Codes_SRS_SASLCLIENTIO_01_118: [If `on_sasl_frame_received_callback` is called in the OPENING state but the header exchange has not yet been completed, then the `on_io_error` callback shall be triggered.]*/ + handle_error(sasl_client_io_instance); + } + else + { + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(sasl_frame); + if (descriptor == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not obtain SASL frame descriptor"); + handle_error(sasl_client_io_instance); + } + else + { + if (sasl_client_io_instance->is_trace_on != 0) + { + log_incoming_frame(sasl_frame); + } + + /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */ + /* Codes_SRS_SASLCLIENTIO_01_040: [The peer playing the role of the SASL client and the peer playing the role of the SASL server MUST correspond to the TCP client and server respectively.] */ + /* Codes_SRS_SASLCLIENTIO_01_034: [<-- SASL-MECHANISMS] */ + if (is_sasl_mechanisms_type_by_descriptor(descriptor)) + { + switch (sasl_client_io_instance->sasl_client_negotiation_state) + { + default: + LogError("SASL mechanisms frame received in %s state", ENUM_TO_STRING(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state)); + handle_error(sasl_client_io_instance); + break; + + case SASL_CLIENT_NEGOTIATION_NOT_STARTED: + { + SASL_MECHANISMS_HANDLE sasl_mechanisms_handle; + + if (amqpvalue_get_sasl_mechanisms(sasl_frame, &sasl_mechanisms_handle) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not get SASL mechanisms"); + handle_error(sasl_client_io_instance); + } + else + { + AMQP_VALUE sasl_server_mechanisms; + uint32_t mechanisms_count; + + if ((sasl_mechanisms_get_sasl_server_mechanisms(sasl_mechanisms_handle, &sasl_server_mechanisms) != 0) || + (amqpvalue_get_array_item_count(sasl_server_mechanisms, &mechanisms_count) != 0) || + (mechanisms_count == 0)) + { + /* Codes_SRS_SASLCLIENTIO_01_042: [It is invalid for this list to be null or empty.] */ + LogError("Invalid SASL mechanisms list"); + handle_error(sasl_client_io_instance); + } + else + { + const char* sasl_mechanism_name = saslmechanism_get_mechanism_name(sasl_client_io_instance->sasl_mechanism); + if (sasl_mechanism_name == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Cannot get the mechanism name"); + handle_error(sasl_client_io_instance); + } + else + { + uint32_t i; + + for (i = 0; i < mechanisms_count; i++) + { + AMQP_VALUE sasl_server_mechanism; + sasl_server_mechanism = amqpvalue_get_array_item(sasl_server_mechanisms, i); + if (sasl_server_mechanism == NULL) + { + LogError("Cannot get SASL mechanisms array item for index %u", (unsigned int)i); + i = mechanisms_count; + } + else + { + const char* sasl_server_mechanism_name; + if (amqpvalue_get_symbol(sasl_server_mechanism, &sasl_server_mechanism_name) != 0) + { + LogError("Error getting server SASL mechanism from array item"); + i = mechanisms_count; + } + else + { + if (strcmp(sasl_mechanism_name, sasl_server_mechanism_name) == 0) + { + amqpvalue_destroy(sasl_server_mechanism); + break; + } + } + + amqpvalue_destroy(sasl_server_mechanism); + } + } + + if (i == mechanisms_count) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not find desired SASL mechanism in the list presented by server"); + handle_error(sasl_client_io_instance); + } + else + { + sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_MECH_RCVD; + + /* Codes_SRS_SASLCLIENTIO_01_035: [SASL-INIT -->] */ + /* Codes_SRS_SASLCLIENTIO_01_033: [The partner MUST then choose one of the supported mechanisms and initiate a sasl exchange.] */ + /* Codes_SRS_SASLCLIENTIO_01_054: [Selects the sasl mechanism and provides the initial response if needed.] */ + if (send_sasl_init(sasl_client_io_instance, sasl_mechanism_name) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Could not send SASL init"); + handle_error(sasl_client_io_instance); + } + else + { + sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_INIT_SENT; + } + } + } + } + + sasl_mechanisms_destroy(sasl_mechanisms_handle); + } + + break; + } + } + } + /* Codes_SRS_SASLCLIENTIO_01_052: [Send the SASL challenge data as defined by the SASL specification.] */ + /* Codes_SRS_SASLCLIENTIO_01_036: [<-- SASL-CHALLENGE *] */ + /* Codes_SRS_SASLCLIENTIO_01_039: [the SASL challenge/response step can occur zero or more times depending on the details of the SASL mechanism chosen.] */ + else if (is_sasl_challenge_type_by_descriptor(descriptor)) + { + /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */ + if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) && + (sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT)) + { + LogError("SASL challenge received in a bad state: %s", ENUM_TO_STRING(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state)); + handle_error(sasl_client_io_instance); + } + else + { + SASL_CHALLENGE_HANDLE sasl_challenge_handle; + + if (amqpvalue_get_sasl_challenge(sasl_frame, &sasl_challenge_handle) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Cannot get SASL challenge values"); + handle_error(sasl_client_io_instance); + } + else + { + amqp_binary challenge_binary_value; + + challenge_binary_value.bytes = NULL; + challenge_binary_value.length = 0; + + /* Codes_SRS_SASLCLIENTIO_01_053: [Challenge information, a block of opaque binary data passed to the security mechanism.] */ + if (sasl_challenge_get_challenge(sasl_challenge_handle, &challenge_binary_value) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Cannot get SASL challenge binary value"); + handle_error(sasl_client_io_instance); + } + else + { + SASL_MECHANISM_BYTES challenge; + SASL_MECHANISM_BYTES response_bytes; + + challenge.bytes = challenge_binary_value.bytes; + challenge.length = challenge_binary_value.length; + response_bytes.bytes = NULL; + response_bytes.length = 0; + + /* Codes_SRS_SASLCLIENTIO_01_057: [The contents of this data are defined by the SASL security mechanism.] */ + /* Codes_SRS_SASLCLIENTIO_01_037: [SASL-RESPONSE -->] */ + if (saslmechanism_challenge(sasl_client_io_instance->sasl_mechanism, &challenge, &response_bytes) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("SASL Challenge failed"); + handle_error(sasl_client_io_instance); + } + else if (send_sasl_response(sasl_client_io_instance, response_bytes) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + LogError("Cannot send SASL reponse"); + handle_error(sasl_client_io_instance); + } + } + + sasl_challenge_destroy(sasl_challenge_handle); + } + } + } + /* Codes_SRS_SASLCLIENTIO_01_058: [This frame indicates the outcome of the SASL dialog.] */ + /* Codes_SRS_SASLCLIENTIO_01_038: [<-- SASL-OUTCOME] */ + else if (is_sasl_outcome_type_by_descriptor(descriptor)) + { + /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */ + if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) && + (sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT)) + { + LogError("SASL outcome received in a bad state: %s", ENUM_TO_STRING(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state)); + handle_error(sasl_client_io_instance); + } + else + { + SASL_OUTCOME_HANDLE sasl_outcome; + + sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_OUTCOME_RCVD; + + if (amqpvalue_get_sasl_outcome(sasl_frame, &sasl_outcome) != 0) + { + LogError("Cannot get SASL outcome"); + handle_error(sasl_client_io_instance); + } + else + { + sasl_code sasl_code; + + /* Codes_SRS_SASLCLIENTIO_01_060: [A reply-code indicating the outcome of the SASL dialog.] */ + if (sasl_outcome_get_code(sasl_outcome, &sasl_code) != 0) + { + LogError("Cannot get SASL outcome code"); + handle_error(sasl_client_io_instance); + } + else + { + switch (sasl_code) + { + default: + case sasl_code_auth: + /* Codes_SRS_SASLCLIENTIO_01_063: [1 Connection authentication failed due to an unspecified problem with the supplied credentials.] */ + case sasl_code_sys: + /* Codes_SRS_SASLCLIENTIO_01_064: [2 Connection authentication failed due to a system error.] */ + case sasl_code_sys_perm: + /* Codes_SRS_SASLCLIENTIO_01_065: [3 Connection authentication failed due to a system error that is unlikely to be corrected without intervention.] */ + case sasl_code_sys_temp: + /* Codes_SRS_SASLCLIENTIO_01_066: [4 Connection authentication failed due to a transient system error.] */ + LogError("SASL handshake failed with code %02X", (unsigned char)sasl_code); + handle_error(sasl_client_io_instance); + break; + + case sasl_code_ok: + /* Codes_SRS_SASLCLIENTIO_01_059: [Upon successful completion of the SASL dialog the security layer has been established] */ + /* Codes_SRS_SASLCLIENTIO_01_062: [0 Connection authentication succeeded.] */ + sasl_client_io_instance->io_state = IO_STATE_OPEN; + + /* Codes_SRS_SASLCLIENTIO_01_072: [When the SASL handshake is complete, if the handshake is successful, the SASL client IO state shall be switched to `IO_STATE_OPEN` and the `on_io_open_complete` callback shall be called with `IO_OPEN_OK`.]*/ + indicate_open_complete(sasl_client_io_instance, IO_OPEN_OK); + break; + } + } + + sasl_outcome_destroy(sasl_outcome); + } + } + } + else + { + LogError("Bad SASL frame"); + } + } + } + break; + } +} + +static void on_frame_codec_error(void* context) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + /* Codes_SRS_SASLCLIENTIO_01_122: [When `on_frame_codec_error` is called while in the OPENING state the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/ + /* Codes_SRS_SASLCLIENTIO_01_143: [ When `on_frame_codec_error` is called while in the OPEN state the `on_io_error` callback shall be triggered. ]*/ + /* Codes_SRS_SASLCLIENTIO_01_123: [When `on_frame_codec_error` is called in the ERROR state nothing shall be done.]*/ + LogError("Error encoding frame (on_frame_codec_error)"); + handle_error(sasl_client_io_instance); +} + +static void on_sasl_frame_codec_error(void* context) +{ + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context; + + /* Codes_SRS_SASLCLIENTIO_01_141: [ When `on_sasl_frame_codec_error` is called while in the OPENING state the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`. ]*/ + /* Codes_SRS_SASLCLIENTIO_01_144: [ When `on_sasl_frame_codec_error` is called while OPEN state the `on_io_error` callback shall be triggered. ]*/ + /* Codes_SRS_SASLCLIENTIO_01_142: [ When `on_sasl_frame_codec_error` is called in the ERROR state nothing shall be done. ]*/ + LogError("Error encoding SASL frame (on_sasl_frame_codec_error)"); + handle_error(sasl_client_io_instance); +} + +CONCRETE_IO_HANDLE saslclientio_create(void* io_create_parameters) +{ + SASLCLIENTIO_CONFIG* sasl_client_io_config = (SASLCLIENTIO_CONFIG*)io_create_parameters; + SASL_CLIENT_IO_INSTANCE* result; + + /* Codes_SRS_SASLCLIENTIO_01_005: [If `io_create_parameters` is NULL, `saslclientio_create` shall fail and return NULL.] */ + if (sasl_client_io_config == NULL) + { + LogError("NULL io_create_parameters"); + result = NULL; + } + /* Codes_SRS_SASLCLIENTIO_01_092: [If any of the `sasl_mechanism` or `underlying_io` members of the configuration structure are NULL, `saslclientio_create` shall fail and return NULL.] */ + else if ((sasl_client_io_config->underlying_io == NULL) || + (sasl_client_io_config->sasl_mechanism == NULL)) + { + LogError("Bad parameters: underlying_io = %p, sasl_mechanism = %p", + sasl_client_io_config->underlying_io, sasl_client_io_config->sasl_mechanism); + result = NULL; + } + else + { + result = (SASL_CLIENT_IO_INSTANCE*)malloc(sizeof(SASL_CLIENT_IO_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_006: [If memory cannot be allocated for the new instance, `saslclientio_create` shall fail and return NULL.] */ + LogError("Cannot allocate sasl client IO instance"); + } + else + { + result->underlying_io = sasl_client_io_config->underlying_io; + /* Codes_SRS_SASLCLIENTIO_01_089: [`saslclientio_create` shall create a frame codec to be used for encoding/decoding frames by calling `frame_codec_create` and passing `on_frame_codec_error` and a context as arguments.] */ + result->frame_codec = frame_codec_create(on_frame_codec_error, result); + if (result->frame_codec == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_090: [If `frame_codec_create` fails, then `saslclientio_create` shall fail and return NULL.] */ + LogError("frame_codec_create failed"); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_084: [`saslclientio_create` shall create a SASL frame codec to be used for SASL frame encoding/decoding by calling `sasl_frame_codec_create` and passing the just created frame codec as argument.] */ + result->sasl_frame_codec = sasl_frame_codec_create(result->frame_codec, on_sasl_frame_received_callback, on_sasl_frame_codec_error, result); + if (result->sasl_frame_codec == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_085: [If `sasl_frame_codec_create` fails, then `saslclientio_create` shall fail and return NULL.] */ + LogError("sasl_frame_codec_create failed"); + frame_codec_destroy(result->frame_codec); + free(result); + result = NULL; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_004: [`saslclientio_create` shall return on success a non-NULL handle to a new SASL client IO instance.] */ + result->on_bytes_received = NULL; + result->on_io_open_complete = NULL; + result->on_io_error = NULL; + result->on_io_close_complete = NULL; + result->on_bytes_received_context = NULL; + result->on_io_open_complete_context = NULL; + result->on_io_close_complete_context = NULL; + result->on_io_error_context = NULL; + result->sasl_mechanism = sasl_client_io_config->sasl_mechanism; + + result->io_state = IO_STATE_NOT_OPEN; + } + } + } + } + + return result; +} + +void saslclientio_destroy(CONCRETE_IO_HANDLE sasl_client_io) +{ + if (sasl_client_io == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_008: [If the argument `sasl_client_io` is NULL, `saslclientio_destroy` shall do nothing.] */ + LogError("NULL sasl_client_io"); + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + /* Codes_SRS_SASLCLIENTIO_01_007: [`saslclientio_destroy` shall free all resources associated with the SASL client IO handle.] */ + /* Codes_SRS_SASLCLIENTIO_01_086: [`saslclientio_destroy` shall destroy the SASL frame codec created in `saslclientio_create` by calling `sasl_frame_codec_destroy`.] */ + sasl_frame_codec_destroy(sasl_client_io_instance->sasl_frame_codec); + + /* Codes_SRS_SASLCLIENTIO_01_091: [`saslclientio_destroy` shall destroy the frame codec created in `saslclientio_create` by calling `frame_codec_destroy`.] */ + frame_codec_destroy(sasl_client_io_instance->frame_codec); + free(sasl_client_io); + } +} + +int saslclientio_open_async(CONCRETE_IO_HANDLE sasl_client_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) +{ + int result = 0; + + if ((sasl_client_io == NULL) || + (on_io_open_complete == NULL) || + (on_bytes_received == NULL) || + (on_io_error == NULL)) + { + /* Codes_SRS_SASLCLIENTIO_01_011: [If any of the `sasl_client_io`, `on_io_open_complete`, `on_bytes_received` or `on_io_error` arguments is NULL, `saslclientio_open` shall fail and return a non-zero value.] */ + LogError("Bad arguments: sasl_client_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error = %p", + sasl_client_io, on_io_open_complete, on_bytes_received, on_io_error); + result = __FAILURE__; + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + if (sasl_client_io_instance->io_state != IO_STATE_NOT_OPEN) + { + LogError("Open called while already OPEN"); + result = __FAILURE__; + } + else + { + sasl_client_io_instance->on_bytes_received = on_bytes_received; + sasl_client_io_instance->on_io_open_complete = on_io_open_complete; + sasl_client_io_instance->on_io_error = on_io_error; + sasl_client_io_instance->on_bytes_received_context = on_bytes_received_context; + sasl_client_io_instance->on_io_open_complete_context = on_io_open_complete_context; + sasl_client_io_instance->on_io_error_context = on_io_error_context; + sasl_client_io_instance->sasl_header_exchange_state = SASL_HEADER_EXCHANGE_IDLE; + sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_NOT_STARTED; + sasl_client_io_instance->header_bytes_received = 0; + sasl_client_io_instance->io_state = IO_STATE_OPENING_UNDERLYING_IO; + sasl_client_io_instance->is_trace_on = 0; + sasl_client_io_instance->is_trace_on_set = 0; + + /* Codes_SRS_SASLCLIENTIO_01_009: [`saslclientio_open` shall call `xio_open` on the `underlying_io` passed to `saslclientio_create`.] */ + /* Codes_SRS_SASLCLIENTIO_01_013: [`saslclientio_open_async` shall pass to `xio_open` the `on_underlying_io_open_complete` as `on_io_open_complete` argument, `on_underlying_io_bytes_received` as `on_bytes_received` argument and `on_underlying_io_error` as `on_io_error` argument.] */ + if (xio_open(sasl_client_io_instance->underlying_io, on_underlying_io_open_complete, sasl_client_io_instance, on_underlying_io_bytes_received, sasl_client_io_instance, on_underlying_io_error, sasl_client_io_instance) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_012: [If the open of the `underlying_io` fails, `saslclientio_open_async` shall fail and return non-zero value.] */ + LogError("xio_open failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_010: [On success, `saslclientio_open_async` shall return 0.]*/ + result = 0; + } + } + } + + return result; +} + +int saslclientio_close_async(CONCRETE_IO_HANDLE sasl_client_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) +{ + int result = 0; + + /* Codes_SRS_SASLCLIENTIO_01_017: [If `sasl_client_io` is NULL, `saslclientio_close_async` shall fail and return a non-zero value.] */ + if (sasl_client_io == NULL) + { + LogError("NULL saslclientio_close"); + result = __FAILURE__; + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + /* Codes_SRS_SASLCLIENTIO_01_098: [`saslclientio_close_async` shall only perform the close if the state is OPEN, OPENING or ERROR.] */ + if ((sasl_client_io_instance->io_state == IO_STATE_NOT_OPEN) || + (sasl_client_io_instance->io_state == IO_STATE_CLOSING)) + { + /* Codes_SRS_SASLCLIENTIO_01_097: [If `saslclientio_close_async` is called when the IO is in the `IO_STATE_NOT_OPEN` state, `saslclientio_close_async` shall fail and return a non zero value.] */ + LogError("saslclientio_close called while not open"); + result = __FAILURE__; + } + else + { + sasl_client_io_instance->io_state = IO_STATE_CLOSING; + + sasl_client_io_instance->on_io_close_complete = on_io_close_complete; + sasl_client_io_instance->on_io_close_complete_context = on_io_close_complete_context; + + /* Codes_SRS_SASLCLIENTIO_01_015: [`saslclientio_close_async` shall close the underlying io handle passed in `saslclientio_create` by calling `xio_close`.] */ + if (xio_close(sasl_client_io_instance->underlying_io, on_underlying_io_close_complete, sasl_client_io_instance) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_018: [If `xio_close` fails, then `saslclientio_close_async` shall return a non-zero value.] */ + LogError("xio_close failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_016: [On success, `saslclientio_close_async` shall return 0.] */ + result = 0; + } + } + } + + return result; +} + +int saslclientio_send_async(CONCRETE_IO_HANDLE sasl_client_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_SRS_SASLCLIENTIO_01_022: [If the `sasl_client_io` or `buffer` argument is NULL, `saslclientio_send_async` shall fail and return a non-zero value.]*/ + /* Codes_SRS_SASLCLIENTIO_01_127: [ `on_send_complete` shall be allowed to be NULL. ]*/ + if ((sasl_client_io == NULL) || + (buffer == NULL) || + /* Codes_SRS_SASLCLIENTIO_01_023: [If `size` is 0, `saslclientio_send_async` shall fail and return a non-zero value.]*/ + (size == 0)) + { + /* Invalid arguments */ + LogError("Bad arguments: sasl_client_io = %p, buffer = %p, size = %u", + sasl_client_io, buffer, (unsigned int)size); + result = __FAILURE__; + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + /* Codes_SRS_SASLCLIENTIO_01_019: [If `saslclientio_send_async` is called while the SASL client IO state is not `IO_STATE_OPEN`, `saslclientio_send_async` shall fail and return a non-zero value.]*/ + if (sasl_client_io_instance->io_state != IO_STATE_OPEN) + { + LogError("send called while not open"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_020: [If the SASL client IO state is `IO_STATE_OPEN`, `saslclientio_send_async` shall call `xio_send` on the `underlying_io` passed to `saslclientio_create`, while passing as arguments the `buffer`,`size`, `on_send_complete` and `callback_context`.]*/ + if (xio_send(sasl_client_io_instance->underlying_io, buffer, size, on_send_complete, callback_context) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_024: [If the call to `xio_send` fails, then `saslclientio_send_async` shall fail and return a non-zero value.]*/ + LogError("xio_send failed"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_021: [On success, `saslclientio_send_async` shall return 0.]*/ + result = 0; + } + } + } + + return result; +} + +void saslclientio_dowork(CONCRETE_IO_HANDLE sasl_client_io) +{ + /* Codes_SRS_SASLCLIENTIO_01_026: [If the `sasl_client_io` argument is NULL, `saslclientio_dowork` shall do nothing.]*/ + if (sasl_client_io == NULL) + { + LogError("NULL sasl_client_io"); + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + /* Codes_SRS_SASLCLIENTIO_01_099: [If the state of the IO is `IO_NOT_OPEN`, `saslclientio_dowork` shall make no calls to the underlying IO.]*/ + if (sasl_client_io_instance->io_state != IO_STATE_NOT_OPEN) + { + /* Codes_SRS_SASLCLIENTIO_01_025: [`saslclientio_dowork` shall call the `xio_dowork` on the `underlying_io` passed in `saslclientio_create`.]*/ + xio_dowork(sasl_client_io_instance->underlying_io); + } + } +} + +int saslclientio_setoption(CONCRETE_IO_HANDLE sasl_client_io, const char* option_name, const void* value) +{ + int result; + + if ((sasl_client_io == NULL) || + (option_name == NULL)) + { + /* Codes_SRS_SASLCLIENTIO_01_130: [ If `sasl_client_io` or `option_name` is NULL, `saslclientio_setoption` shall fail and return a non-zero value. ]*/ + LogError("Bad arguments: sasl_client_io = %p, option_name = %p", + sasl_client_io, option_name); + result = __FAILURE__; + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + if (sasl_client_io_instance->underlying_io == NULL) + { + LogError("NULL underlying_io"); + result = __FAILURE__; + } + /* Codes_SRS_SASLCLIENTIO_01_131: [ SASL client IO shall handle the following options: ]*/ + /* Codes_SRS_SASLCLIENTIO_01_132: [ - logtrace - bool. ]*/ + else if (strcmp("logtrace", option_name) == 0) + { + sasl_client_io_instance->is_trace_on = *((bool*)value) == true ? 1 : 0; + sasl_client_io_instance->is_trace_on_set = 1; + + /* Codes_SRS_SASLCLIENTIO_01_128: [ On success, `saslclientio_setoption` shall return 0. ]*/ + result = 0; + } + else + { + /* Codes_SRS_SASLCLIENTIO_03_001: [`saslclientio_setoption` shall forward all unhandled options to underlying io by calling `xio_setoption`.]*/ + if (xio_setoption(sasl_client_io_instance->underlying_io, option_name, value) != 0) + { + LogError("Error executing xio_setoption"); + result = __FAILURE__; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_128: [ On success, `saslclientio_setoption` shall return 0. ]*/ + result = 0; + } + } + } + + return result; +} + +/*this function will clone an option given by name and value*/ +static void* saslclientio_clone_option(const char* name, const void* value) +{ + (void)name; + (void)value; + return NULL; +} + +/*this function destroys an option previously created*/ +static void saslclientio_destroy_option(const char* name, const void* value) +{ + (void)name; + (void)value; +} + +static OPTIONHANDLER_HANDLE saslclientio_retrieveoptions(CONCRETE_IO_HANDLE sasl_client_io) +{ + OPTIONHANDLER_HANDLE result; + + if (sasl_client_io == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_139: [ When `saslclientio_retrieveoptions` is called with NULL `sasl_client_io` it shall fail and return NULL. ]*/ + result = NULL; + } + else + { + /* Codes_SRS_SASLCLIENTIO_01_133: [ `saslclientio_retrieveoptions` shall create an option handler by calling `OptionHandler_Create`. ]*/ + result = OptionHandler_Create(saslclientio_clone_option, saslclientio_destroy_option, saslclientio_setoption); + if (result == NULL) + { + /* Codes_SRS_SASLCLIENTIO_01_138: [ If `OptionHandler_AddOption` or `OptionHandler_Create` fails then `saslclientio_retrieveoptions` shall fail and return NULL. ]*/ + LogError("unable to OptionHandler_Create"); + /*return as is*/ + } + else + { + SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)sasl_client_io; + + /*insert here work to add the options to "result" handle*/ + if (sasl_client_io_instance->is_trace_on_set) + { + bool logtrace = sasl_client_io_instance->is_trace_on ? true : false; + /* Codes_SRS_SASLCLIENTIO_01_137: [ The options shall be added by calling `OptionHandler_AddOption`. ]*/ + if (OptionHandler_AddOption(result, "logtrace", &logtrace) != 0) + { + /* Codes_SRS_SASLCLIENTIO_01_138: [ If `OptionHandler_AddOption` or `OptionHandler_Create` fails then `saslclientio_retrieveoptions` shall fail and return NULL. ]*/ + LogError("unable to add logtrace option"); + OptionHandler_Destroy(result); + result = NULL; + } + } + } + } + + return result; +} + +static const IO_INTERFACE_DESCRIPTION sasl_client_io_interface_description = +{ + saslclientio_retrieveoptions, + saslclientio_create, + saslclientio_destroy, + saslclientio_open_async, + saslclientio_close_async, + saslclientio_send_async, + saslclientio_dowork, + saslclientio_setoption +}; + +/* Codes_SRS_SASLCLIENTIO_01_087: [`saslclientio_get_interface_description` shall return a pointer to an `IO_INTERFACE_DESCRIPTION` structure that contains pointers to the functions: `saslclientio_create`, `saslclientio_destroy`, `saslclientio_open_async`, `saslclientio_close_async`, `saslclientio_send_async`, `saslclientio_setoption`, `saslclientio_retrieveoptions` and `saslclientio_dowork`.]*/ +const IO_INTERFACE_DESCRIPTION* saslclientio_get_interface_description(void) +{ + return &sasl_client_io_interface_description; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/src/session.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1645 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <string.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_uamqp_c/session.h" +#include "azure_uamqp_c/connection.h" +#include "azure_uamqp_c/amqp_definitions.h" + +typedef enum LINK_ENDPOINT_STATE_TAG +{ + LINK_ENDPOINT_STATE_NOT_ATTACHED, + LINK_ENDPOINT_STATE_ATTACHED, + LINK_ENDPOINT_STATE_DETACHING +} LINK_ENDPOINT_STATE; + +typedef struct LINK_ENDPOINT_INSTANCE_TAG +{ + char* name; + handle input_handle; + handle output_handle; + ON_ENDPOINT_FRAME_RECEIVED frame_received_callback; + ON_SESSION_STATE_CHANGED on_session_state_changed; + ON_SESSION_FLOW_ON on_session_flow_on; + void* callback_context; + SESSION_HANDLE session; + LINK_ENDPOINT_STATE link_endpoint_state; +} LINK_ENDPOINT_INSTANCE; + +typedef struct SESSION_INSTANCE_TAG +{ + ON_ENDPOINT_FRAME_RECEIVED frame_received_callback; + void* frame_received_callback_context; + SESSION_STATE session_state; + SESSION_STATE previous_session_state; + CONNECTION_HANDLE connection; + ENDPOINT_HANDLE endpoint; + LINK_ENDPOINT_INSTANCE** link_endpoints; + uint32_t link_endpoint_count; + + ON_LINK_ATTACHED on_link_attached; + void* on_link_attached_callback_context; + + /* Codes_S_R_S_SESSION_01_016: [next-outgoing-id The next-outgoing-id is the transfer-id to assign to the next transfer frame.] */ + transfer_number next_outgoing_id; + transfer_number next_incoming_id; + uint32_t desired_incoming_window; + uint32_t incoming_window; + uint32_t outgoing_window; + handle handle_max; + uint32_t remote_incoming_window; + uint32_t remote_outgoing_window; + unsigned int is_underlying_connection_open : 1; +} SESSION_INSTANCE; + +#define UNDERLYING_CONNECTION_NOT_OPEN 0 +#define UNDERLYING_CONNECTION_OPEN 1 + +static void remove_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint) +{ + if (link_endpoint != NULL) + { + LINK_ENDPOINT_INSTANCE* endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + SESSION_INSTANCE* session_instance = endpoint_instance->session; + uint32_t i; + + for (i = 0; i < session_instance->link_endpoint_count; i++) + { + if (session_instance->link_endpoints[i] == link_endpoint) + { + break; + } + } + + if (i < session_instance->link_endpoint_count) + { + LINK_ENDPOINT_INSTANCE** new_endpoints; + + if (i < (session_instance->link_endpoint_count - 1)) + { + (void)memmove(&session_instance->link_endpoints[i], &session_instance->link_endpoints[i + 1], (session_instance->link_endpoint_count - (uint32_t)i - 1) * sizeof(LINK_ENDPOINT_INSTANCE*)); + } + + session_instance->link_endpoint_count--; + + if (session_instance->link_endpoint_count == 0) + { + free(session_instance->link_endpoints); + session_instance->link_endpoints = NULL; + } + else + { + new_endpoints = (LINK_ENDPOINT_INSTANCE**)realloc(session_instance->link_endpoints, sizeof(LINK_ENDPOINT_INSTANCE*) * session_instance->link_endpoint_count); + if (new_endpoints != NULL) + { + session_instance->link_endpoints = new_endpoints; + } + } + } + } +} + +static void free_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint) +{ + if (link_endpoint->name != NULL) + { + free(link_endpoint->name); + } + + free(link_endpoint); +} + +static void session_set_state(SESSION_INSTANCE* session_instance, SESSION_STATE session_state) +{ + uint32_t i; + + session_instance->previous_session_state = session_instance->session_state; + session_instance->session_state = session_state; + + for (i = 0; i < session_instance->link_endpoint_count; i++) + { + if (session_instance->link_endpoints[i]->on_session_state_changed != NULL) + { + if (session_instance->link_endpoints[i]->link_endpoint_state != LINK_ENDPOINT_STATE_DETACHING) + { + session_instance->link_endpoints[i]->on_session_state_changed(session_instance->link_endpoints[i]->callback_context, session_state, session_instance->previous_session_state); + } + } + } +} + +static int send_end_frame(SESSION_INSTANCE* session_instance, ERROR_HANDLE error_handle) +{ + int result; + END_HANDLE end_performative; + + end_performative = end_create(); + if (end_performative == NULL) + { + result = __FAILURE__; + } + else + { + if ((error_handle != NULL) && + (end_set_error(end_performative, error_handle) != 0)) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE end_performative_value = amqpvalue_create_end(end_performative); + if (end_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (connection_encode_frame(session_instance->endpoint, end_performative_value, NULL, 0, NULL, NULL) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(end_performative_value); + } + } + + end_destroy(end_performative); + } + + return result; +} + +static void end_session_with_error(SESSION_INSTANCE* session_instance, const char* condition_value, const char* description) +{ + ERROR_HANDLE error_handle = error_create(condition_value); + if (error_handle == NULL) + { + /* fatal error */ + session_set_state(session_instance, SESSION_STATE_DISCARDING); + (void)connection_close(session_instance->connection, "amqp:internal-error", "Cannot allocate error handle to end session", NULL); + } + else + { + if ((error_set_description(error_handle, description) != 0) || + (send_end_frame(session_instance, error_handle) != 0)) + { + /* fatal error */ + session_set_state(session_instance, SESSION_STATE_DISCARDING); + (void)connection_close(session_instance->connection, "amqp:internal-error", "Cannot allocate error handle to end session", NULL); + } + else + { + session_set_state(session_instance, SESSION_STATE_DISCARDING); + } + + error_destroy(error_handle); + } +} + +static int send_begin(SESSION_INSTANCE* session_instance) +{ + int result; + BEGIN_HANDLE begin = begin_create(session_instance->next_outgoing_id, session_instance->incoming_window, session_instance->outgoing_window); + + if (begin == NULL) + { + result = __FAILURE__; + } + else + { + uint16_t remote_channel; + if (begin_set_handle_max(begin, session_instance->handle_max) != 0) + { + result = __FAILURE__; + } + else if ((session_instance->session_state == SESSION_STATE_BEGIN_RCVD) && + ((connection_endpoint_get_incoming_channel(session_instance->endpoint, &remote_channel) != 0) || + (begin_set_remote_channel(begin, remote_channel) != 0))) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE begin_performative_value = amqpvalue_create_begin(begin); + if (begin_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (connection_encode_frame(session_instance->endpoint, begin_performative_value, NULL, 0, NULL, NULL) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(begin_performative_value); + } + } + + begin_destroy(begin); + } + + return result; +} + +static int send_flow(SESSION_INSTANCE* session) +{ + int result; + if (session == NULL) + { + result = __FAILURE__; + } + else + { + FLOW_HANDLE flow = flow_create(session->incoming_window, session->next_outgoing_id, session->outgoing_window); + + if (flow == NULL) + { + result = __FAILURE__; + } + else + { + if (flow_set_next_incoming_id(flow, session->next_incoming_id) != 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE flow_performative_value = amqpvalue_create_flow(flow); + if (flow_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (connection_encode_frame(session->endpoint, flow_performative_value, NULL, 0, NULL, NULL) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(flow_performative_value); + } + } + + flow_destroy(flow); + } + } + + return result; +} + +static LINK_ENDPOINT_INSTANCE* find_link_endpoint_by_name(SESSION_INSTANCE* session, const char* name) +{ + uint32_t i; + LINK_ENDPOINT_INSTANCE* result; + + for (i = 0; i < session->link_endpoint_count; i++) + { + if (strcmp(session->link_endpoints[i]->name, name) == 0) + { + break; + } + } + + if (i == session->link_endpoint_count) + { + result = NULL; + } + else + { + result = session->link_endpoints[i]; + } + + return result; +} + +static LINK_ENDPOINT_INSTANCE* find_link_endpoint_by_input_handle(SESSION_INSTANCE* session, handle input_handle) +{ + uint32_t i; + LINK_ENDPOINT_INSTANCE* result; + + for (i = 0; i < session->link_endpoint_count; i++) + { + if (session->link_endpoints[i]->input_handle == input_handle) + { + break; + } + } + + if (i == session->link_endpoint_count) + { + result = NULL; + } + else + { + result = session->link_endpoints[i]; + } + + return result; +} + +static void on_connection_state_changed(void* context, CONNECTION_STATE new_connection_state, CONNECTION_STATE previous_connection_state) +{ + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)context; + + /* Codes_S_R_S_SESSION_01_060: [If the previous connection state is not OPENED and the new connection state is OPENED, the BEGIN frame shall be sent out and the state shall be switched to BEGIN_SENT.] */ + if ((new_connection_state == CONNECTION_STATE_OPENED) && (previous_connection_state != CONNECTION_STATE_OPENED) && (session_instance->session_state == SESSION_STATE_UNMAPPED)) + { + if (send_begin(session_instance) == 0) + { + session_set_state(session_instance, SESSION_STATE_BEGIN_SENT); + } + } + /* Codes_S_R_S_SESSION_01_061: [If the previous connection state is OPENED and the new connection state is not OPENED anymore, the state shall be switched to DISCARDING.] */ + else if ((new_connection_state == CONNECTION_STATE_CLOSE_RCVD) || (new_connection_state == CONNECTION_STATE_END)) + { + session_set_state(session_instance, SESSION_STATE_DISCARDING); + } + /* Codes_S_R_S_SESSION_09_001: [If the new connection state is ERROR, the state shall be switched to ERROR.] */ + else if (new_connection_state == CONNECTION_STATE_ERROR) + { + session_set_state(session_instance, SESSION_STATE_ERROR); + } +} + +static void on_frame_received(void* context, AMQP_VALUE performative, uint32_t payload_size, const unsigned char* payload_bytes) +{ + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)context; + AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(performative); + + if (is_begin_type_by_descriptor(descriptor)) + { + BEGIN_HANDLE begin_handle; + + if (amqpvalue_get_begin(performative, &begin_handle) != 0) + { + connection_close(session_instance->connection, "amqp:decode-error", "Cannot decode BEGIN frame", NULL); + } + else + { + if ((begin_get_incoming_window(begin_handle, &session_instance->remote_incoming_window) != 0) || + (begin_get_next_outgoing_id(begin_handle, &session_instance->next_incoming_id) != 0)) + { + /* error */ + begin_destroy(begin_handle); + session_set_state(session_instance, SESSION_STATE_DISCARDING); + connection_close(session_instance->connection, "amqp:decode-error", "Cannot get incoming windows and next outgoing id", NULL); + } + else + { + begin_destroy(begin_handle); + + if (session_instance->session_state == SESSION_STATE_BEGIN_SENT) + { + session_set_state(session_instance, SESSION_STATE_MAPPED); + } + else if(session_instance->session_state == SESSION_STATE_UNMAPPED) + { + session_set_state(session_instance, SESSION_STATE_BEGIN_RCVD); + if (send_begin(session_instance) != 0) + { + connection_close(session_instance->connection, "amqp:internal-error", "Failed sending BEGIN frame", NULL); + session_set_state(session_instance, SESSION_STATE_DISCARDING); + } + else + { + session_set_state(session_instance, SESSION_STATE_MAPPED); + } + } + } + } + } + else if (is_attach_type_by_descriptor(descriptor)) + { + const char* name = NULL; + ATTACH_HANDLE attach_handle; + + if (amqpvalue_get_attach(performative, &attach_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode ATTACH frame"); + } + else + { + role role; + AMQP_VALUE source; + AMQP_VALUE target; + + if (attach_get_name(attach_handle, &name) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get link name from ATTACH frame"); + } + else if (attach_get_role(attach_handle, &role) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get link role from ATTACH frame"); + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint; + + if (attach_get_source(attach_handle, &source) != 0) + { + source = NULL; + } + if (attach_get_target(attach_handle, &target) != 0) + { + target = NULL; + } + + link_endpoint = find_link_endpoint_by_name(session_instance, name); + if (link_endpoint == NULL) + { + /* new link attach */ + if (session_instance->on_link_attached != NULL) + { + LINK_ENDPOINT_HANDLE new_link_endpoint = session_create_link_endpoint(session_instance, name); + if (new_link_endpoint == NULL) + { + end_session_with_error(session_instance, "amqp:internal-error", "Cannot create link endpoint"); + } + else if (attach_get_handle(attach_handle, &new_link_endpoint->input_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get input handle from ATTACH frame"); + } + else + { + new_link_endpoint->link_endpoint_state = LINK_ENDPOINT_STATE_ATTACHED; + + if (!session_instance->on_link_attached(session_instance->on_link_attached_callback_context, new_link_endpoint, name, role, source, target)) + { + remove_link_endpoint(new_link_endpoint); + free_link_endpoint(new_link_endpoint); + new_link_endpoint = NULL; + } + else + { + if (new_link_endpoint->frame_received_callback != NULL) + { + new_link_endpoint->frame_received_callback(new_link_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + } + } + } + else + { + if (attach_get_handle(attach_handle, &link_endpoint->input_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get input handle from ATTACH frame"); + } + else + { + link_endpoint->link_endpoint_state = LINK_ENDPOINT_STATE_ATTACHED; + + link_endpoint->frame_received_callback(link_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + } + + attach_destroy(attach_handle); + } + } + else if (is_detach_type_by_descriptor(descriptor)) + { + DETACH_HANDLE detach_handle; + + if (amqpvalue_get_detach(performative, &detach_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode DETACH frame"); + } + else + { + uint32_t remote_handle; + if (detach_get_handle(detach_handle, &remote_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get handle from DETACH frame"); + + detach_destroy(detach_handle); + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint; + detach_destroy(detach_handle); + + link_endpoint = find_link_endpoint_by_input_handle(session_instance, remote_handle); + if (link_endpoint == NULL) + { + end_session_with_error(session_instance, "amqp:session:unattached-handle", ""); + } + else + { + if (link_endpoint->link_endpoint_state != LINK_ENDPOINT_STATE_DETACHING) + { + link_endpoint->link_endpoint_state = LINK_ENDPOINT_STATE_DETACHING; + link_endpoint->frame_received_callback(link_endpoint->callback_context, performative, payload_size, payload_bytes); + } + else + { + /* remove the link endpoint */ + remove_link_endpoint(link_endpoint); + free_link_endpoint(link_endpoint); + } + } + } + } + } + else if (is_flow_type_by_descriptor(descriptor)) + { + FLOW_HANDLE flow_handle; + + if (amqpvalue_get_flow(performative, &flow_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode FLOW frame"); + } + else + { + uint32_t remote_handle; + transfer_number flow_next_incoming_id; + uint32_t flow_incoming_window; + + if (flow_get_next_incoming_id(flow_handle, &flow_next_incoming_id) != 0) + { + /* + If the next-incoming-id field of the flow frame is not set, + then remote-incomingwindow is computed as follows: + initial-outgoing-id(endpoint) + incoming-window(flow) - next-outgoing-id(endpoint) + */ + flow_next_incoming_id = session_instance->next_outgoing_id; + } + + if ((flow_get_next_outgoing_id(flow_handle, &session_instance->next_incoming_id) != 0) || + (flow_get_incoming_window(flow_handle, &flow_incoming_window) != 0)) + { + flow_destroy(flow_handle); + + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode FLOW frame"); + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = NULL; + size_t i; + + session_instance->remote_incoming_window = flow_next_incoming_id + flow_incoming_window - session_instance->next_outgoing_id; + + if (flow_get_handle(flow_handle, &remote_handle) == 0) + { + link_endpoint_instance = find_link_endpoint_by_input_handle(session_instance, remote_handle); + } + + flow_destroy(flow_handle); + + if (link_endpoint_instance != NULL) + { + if (link_endpoint_instance->link_endpoint_state != LINK_ENDPOINT_STATE_DETACHING) + { + link_endpoint_instance->frame_received_callback(link_endpoint_instance->callback_context, performative, payload_size, payload_bytes); + } + } + + i = 0; + while ((session_instance->remote_incoming_window > 0) && (i < session_instance->link_endpoint_count)) + { + /* notify the caller that it can send here */ + if (session_instance->link_endpoints[i]->on_session_flow_on != NULL) + { + session_instance->link_endpoints[i]->on_session_flow_on(session_instance->link_endpoints[i]->callback_context); + } + + i++; + } + } + } + } + else if (is_transfer_type_by_descriptor(descriptor)) + { + TRANSFER_HANDLE transfer_handle; + + if (amqpvalue_get_transfer(performative, &transfer_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode TRANSFER frame"); + } + else + { + uint32_t remote_handle; + delivery_number delivery_id; + + transfer_get_delivery_id(transfer_handle, &delivery_id); + if (transfer_get_handle(transfer_handle, &remote_handle) != 0) + { + transfer_destroy(transfer_handle); + end_session_with_error(session_instance, "amqp:decode-error", "Cannot get handle from TRANSFER frame"); + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint; + transfer_destroy(transfer_handle); + + session_instance->next_incoming_id++; + session_instance->remote_outgoing_window--; + session_instance->incoming_window--; + + link_endpoint = find_link_endpoint_by_input_handle(session_instance, remote_handle); + if (link_endpoint == NULL) + { + end_session_with_error(session_instance, "amqp:session:unattached-handle", ""); + } + else + { + if (link_endpoint->link_endpoint_state != LINK_ENDPOINT_STATE_DETACHING) + { + link_endpoint->frame_received_callback(link_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + + if (session_instance->incoming_window == 0) + { + session_instance->incoming_window = session_instance->desired_incoming_window; + send_flow(session_instance); + } + } + } + } + else if (is_disposition_type_by_descriptor(descriptor)) + { + uint32_t i; + + for (i = 0; i < session_instance->link_endpoint_count; i++) + { + LINK_ENDPOINT_INSTANCE* link_endpoint = session_instance->link_endpoints[i]; + if (link_endpoint->link_endpoint_state != LINK_ENDPOINT_STATE_DETACHING) + { + link_endpoint->frame_received_callback(link_endpoint->callback_context, performative, payload_size, payload_bytes); + } + } + } + else if (is_end_type_by_descriptor(descriptor)) + { + END_HANDLE end_handle; + + if (amqpvalue_get_end(performative, &end_handle) != 0) + { + end_session_with_error(session_instance, "amqp:decode-error", "Cannot decode END frame"); + } + else + { + end_destroy(end_handle); + if ((session_instance->session_state != SESSION_STATE_END_RCVD) && + (session_instance->session_state != SESSION_STATE_DISCARDING)) + { + session_set_state(session_instance, SESSION_STATE_END_RCVD); + if (send_end_frame(session_instance, NULL) != 0) + { + /* fatal error */ + (void)connection_close(session_instance->connection, "amqp:internal-error", "Cannot send END frame.", NULL); + } + + session_set_state(session_instance, SESSION_STATE_DISCARDING); + } + } + } +} + +SESSION_HANDLE session_create(CONNECTION_HANDLE connection, ON_LINK_ATTACHED on_link_attached, void* callback_context) +{ + SESSION_INSTANCE* result; + + if (connection == NULL) + { + /* Codes_S_R_S_SESSION_01_031: [If connection is NULL, session_create shall fail and return NULL.] */ + result = NULL; + } + else + { + /* Codes_S_R_S_SESSION_01_030: [session_create shall create a new session instance and return a non-NULL handle to it.] */ + result = (SESSION_INSTANCE*)malloc(sizeof(SESSION_INSTANCE)); + /* Codes_S_R_S_SESSION_01_042: [If allocating memory for the session fails, session_create shall fail and return NULL.] */ + if (result != NULL) + { + result->connection = connection; + result->link_endpoints = NULL; + result->link_endpoint_count = 0; + result->handle_max = 4294967295u; + + /* Codes_S_R_S_SESSION_01_057: [The delivery ids shall be assigned starting at 0.] */ + /* Codes_S_R_S_SESSION_01_017: [The nextoutgoing-id MAY be initialized to an arbitrary value ] */ + result->next_outgoing_id = 0; + + result->desired_incoming_window = 1; + result->incoming_window = 1; + result->outgoing_window = 1; + result->handle_max = 4294967295u; + result->remote_incoming_window = 0; + result->remote_outgoing_window = 0; + result->previous_session_state = SESSION_STATE_UNMAPPED; + result->is_underlying_connection_open = UNDERLYING_CONNECTION_NOT_OPEN; + result->session_state = SESSION_STATE_UNMAPPED; + result->on_link_attached = on_link_attached; + result->on_link_attached_callback_context = callback_context; + + /* Codes_S_R_S_SESSION_01_032: [session_create shall create a new session endpoint by calling connection_create_endpoint.] */ + result->endpoint = connection_create_endpoint(connection); + if (result->endpoint == NULL) + { + /* Codes_S_R_S_SESSION_01_033: [If connection_create_endpoint fails, session_create shall fail and return NULL.] */ + free(result); + result = NULL; + } + else + { + session_set_state(result, SESSION_STATE_UNMAPPED); + } + } + } + + return result; +} + +SESSION_HANDLE session_create_from_endpoint(CONNECTION_HANDLE connection, ENDPOINT_HANDLE endpoint, ON_LINK_ATTACHED on_link_attached, void* callback_context) +{ + SESSION_INSTANCE* result; + + if (endpoint == NULL) + { + result = NULL; + } + else + { + result = (SESSION_INSTANCE*)malloc(sizeof(SESSION_INSTANCE)); + if (result != NULL) + { + result->connection = connection; + result->link_endpoints = NULL; + result->link_endpoint_count = 0; + result->handle_max = 4294967295u; + + result->next_outgoing_id = 0; + + result->desired_incoming_window = 1; + result->incoming_window = 1; + result->outgoing_window = 1; + result->handle_max = 4294967295u; + result->remote_incoming_window = 0; + result->remote_outgoing_window = 0; + result->previous_session_state = SESSION_STATE_UNMAPPED; + result->is_underlying_connection_open = UNDERLYING_CONNECTION_NOT_OPEN; + result->session_state = SESSION_STATE_UNMAPPED; + result->on_link_attached = on_link_attached; + result->on_link_attached_callback_context = callback_context; + + result->endpoint = endpoint; + session_set_state(result, SESSION_STATE_UNMAPPED); + } + } + + return result; +} + +void session_destroy(SESSION_HANDLE session) +{ + /* Codes_S_R_S_SESSION_01_036: [If session is NULL, session_destroy shall do nothing.] */ + if (session != NULL) + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + session_end(session, NULL, NULL); + + /* Codes_S_R_S_SESSION_01_034: [session_destroy shall free all resources allocated by session_create.] */ + /* Codes_S_R_S_SESSION_01_035: [The endpoint created in session_create shall be freed by calling connection_destroy_endpoint.] */ + connection_destroy_endpoint(session_instance->endpoint); + if (session_instance->link_endpoints != NULL) + { + free(session_instance->link_endpoints); + } + + free(session); + } +} + +int session_begin(SESSION_HANDLE session) +{ + int result; + + if (session == NULL) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + if (connection_start_endpoint(session_instance->endpoint, on_frame_received, on_connection_state_changed, session_instance) != 0) + { + result = __FAILURE__; + } + else + { + if (!session_instance->is_underlying_connection_open) + { + if (connection_open(session_instance->connection) != 0) + { + session_instance->is_underlying_connection_open = UNDERLYING_CONNECTION_NOT_OPEN; + result = __FAILURE__; + } + else + { + session_instance->is_underlying_connection_open = UNDERLYING_CONNECTION_OPEN; + result = 0; + } + } + else + { + result = 0; + } + } + } + + return result; +} + +int session_end(SESSION_HANDLE session, const char* condition_value, const char* description) +{ + int result; + + if (session == NULL) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + size_t i; + + if ((session_instance->session_state != SESSION_STATE_UNMAPPED) && + (session_instance->session_state != SESSION_STATE_DISCARDING)) + { + ERROR_HANDLE error_handle = NULL; + result = 0; + + if (condition_value != NULL) + { + error_handle = error_create(condition_value); + if (error_handle == NULL) + { + result = __FAILURE__; + } + else + { + if (error_set_description(error_handle, description) != 0) + { + result = __FAILURE__; + } + } + } + + if (result == 0) + { + if (send_end_frame(session_instance, error_handle) != 0) + { + result = __FAILURE__; + } + else + { + session_set_state(session_instance, SESSION_STATE_DISCARDING); + result = 0; + } + } + + if (error_handle != NULL) + { + error_destroy(error_handle); + } + } + else + { + result = 0; + } + + // all link endpoints are destroyed when the session end happens + for (i = 0; i < session_instance->link_endpoint_count; i++) + { + free_link_endpoint(session_instance->link_endpoints[i]); + } + + session_instance->link_endpoint_count = 0; + } + + return result; +} + +int session_set_incoming_window(SESSION_HANDLE session, uint32_t incoming_window) +{ + int result; + + if (session == NULL) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + session_instance->desired_incoming_window = incoming_window; + session_instance->incoming_window = incoming_window; + + result = 0; + } + + return result; +} + +int session_get_incoming_window(SESSION_HANDLE session, uint32_t* incoming_window) +{ + int result; + + if ((session == NULL) || + (incoming_window == NULL)) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + *incoming_window = session_instance->incoming_window; + + result = 0; + } + + return result; +} + +int session_set_outgoing_window(SESSION_HANDLE session, uint32_t outgoing_window) +{ + int result; + + if (session == NULL) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + session_instance->outgoing_window = outgoing_window; + + result = 0; + } + + return result; +} + +int session_get_outgoing_window(SESSION_HANDLE session, uint32_t* outgoing_window) +{ + int result; + + if ((session == NULL) || + (outgoing_window == NULL)) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + *outgoing_window = session_instance->outgoing_window; + + result = 0; + } + + return result; +} + +int session_set_handle_max(SESSION_HANDLE session, handle handle_max) +{ + int result; + + if (session == NULL) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + session_instance->handle_max = handle_max; + + result = 0; + } + + return result; +} + +int session_get_handle_max(SESSION_HANDLE session, handle* handle_max) +{ + int result; + + if ((session == NULL) || + (handle_max == NULL)) + { + result = __FAILURE__; + } + else + { + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + *handle_max = session_instance->handle_max; + + result = 0; + } + + return result; +} + +LINK_ENDPOINT_HANDLE session_create_link_endpoint(SESSION_HANDLE session, const char* name) +{ + LINK_ENDPOINT_INSTANCE* result; + + /* Codes_S_R_S_SESSION_01_044: [If session, name or frame_received_callback is NULL, session_create_link_endpoint shall fail and return NULL.] */ + if ((session == NULL) || + (name == NULL)) + { + result = NULL; + } + else + { + /* Codes_S_R_S_SESSION_01_043: [session_create_link_endpoint shall create a link endpoint associated with a given session and return a non-NULL handle to it.] */ + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)session; + + result = (LINK_ENDPOINT_INSTANCE*)malloc(sizeof(LINK_ENDPOINT_INSTANCE)); + /* Codes_S_R_S_SESSION_01_045: [If allocating memory for the link endpoint fails, session_create_link_endpoint shall fail and return NULL.] */ + if (result != NULL) + { + /* Codes_S_R_S_SESSION_01_046: [An unused handle shall be assigned to the link endpoint.] */ + handle selected_handle = 0; + size_t i; + size_t name_length; + + for (i = 0; i < session_instance->link_endpoint_count; i++) + { + if (session_instance->link_endpoints[i]->output_handle > selected_handle) + { + break; + } + + selected_handle++; + } + + result->on_session_state_changed = NULL; + result->on_session_flow_on = NULL; + result->frame_received_callback = NULL; + result->callback_context = NULL; + result->output_handle = selected_handle; + result->input_handle = 0xFFFFFFFF; + result->link_endpoint_state = LINK_ENDPOINT_STATE_NOT_ATTACHED; + name_length = strlen(name); + result->name = (char*)malloc(name_length + 1); + if (result->name == NULL) + { + /* Codes_S_R_S_SESSION_01_045: [If allocating memory for the link endpoint fails, session_create_link_endpoint shall fail and return NULL.] */ + free(result); + result = NULL; + } + else + { + LINK_ENDPOINT_INSTANCE** new_link_endpoints; + (void)memcpy(result->name, name, name_length + 1); + result->session = session; + + new_link_endpoints = (LINK_ENDPOINT_INSTANCE**)realloc(session_instance->link_endpoints, sizeof(LINK_ENDPOINT_INSTANCE*) * (session_instance->link_endpoint_count + 1)); + if (new_link_endpoints == NULL) + { + /* Codes_S_R_S_SESSION_01_045: [If allocating memory for the link endpoint fails, session_create_link_endpoint shall fail and return NULL.] */ + free(result->name); + free(result); + result = NULL; + } + else + { + session_instance->link_endpoints = new_link_endpoints; + + if (session_instance->link_endpoint_count - selected_handle > 0) + { + (void)memmove(&session_instance->link_endpoints[selected_handle + 1], &session_instance->link_endpoints[selected_handle], (session_instance->link_endpoint_count - selected_handle) * sizeof(LINK_ENDPOINT_INSTANCE*)); + } + + session_instance->link_endpoints[selected_handle] = result; + session_instance->link_endpoint_count++; + } + } + } + } + + return result; +} + +void session_destroy_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint) +{ + if (link_endpoint != NULL) + { + LINK_ENDPOINT_INSTANCE* endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + + if (endpoint_instance->link_endpoint_state == LINK_ENDPOINT_STATE_ATTACHED) + { + endpoint_instance->link_endpoint_state = LINK_ENDPOINT_STATE_DETACHING; + } + else + { + remove_link_endpoint(link_endpoint); + free_link_endpoint(link_endpoint); + } + } +} + +int session_start_link_endpoint(LINK_ENDPOINT_HANDLE link_endpoint, ON_ENDPOINT_FRAME_RECEIVED frame_received_callback, ON_SESSION_STATE_CHANGED on_session_state_changed, ON_SESSION_FLOW_ON on_session_flow_on, void* context) +{ + int result; + + if ((link_endpoint == NULL) || + (frame_received_callback == NULL)) + { + result = __FAILURE__; + } + else + { + link_endpoint->frame_received_callback = frame_received_callback; + link_endpoint->on_session_state_changed = on_session_state_changed; + link_endpoint->on_session_flow_on = on_session_flow_on; + link_endpoint->callback_context = context; + link_endpoint->link_endpoint_state = LINK_ENDPOINT_STATE_NOT_ATTACHED; + + if (link_endpoint->on_session_state_changed != NULL) + { + link_endpoint->on_session_state_changed(link_endpoint->callback_context, link_endpoint->session->session_state, link_endpoint->session->previous_session_state); + } + + result = 0; + } + + return result; +} + +static int encode_frame(LINK_ENDPOINT_HANDLE link_endpoint, AMQP_VALUE performative, PAYLOAD* payloads, size_t payload_count) +{ + int result; + + if ((link_endpoint == NULL) || + (performative == NULL)) + { + result = __FAILURE__; + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)link_endpoint_instance->session; + + if (connection_encode_frame(session_instance->endpoint, performative, payloads, payload_count, NULL, NULL) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + } + + return result; +} + +int session_send_flow(LINK_ENDPOINT_HANDLE link_endpoint, FLOW_HANDLE flow) +{ + int result; + + if ((link_endpoint == NULL) || + (flow == NULL)) + { + result = __FAILURE__; + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)link_endpoint_instance->session; + + result = 0; + + if (session_instance->session_state == SESSION_STATE_BEGIN_RCVD) + { + if (flow_set_next_incoming_id(flow, session_instance->next_incoming_id) != 0) + { + result = __FAILURE__; + } + } + + if (result == 0) + { + if ((flow_set_next_incoming_id(flow, session_instance->next_incoming_id) != 0) || + (flow_set_incoming_window(flow, session_instance->incoming_window) != 0) || + (flow_set_next_outgoing_id(flow, session_instance->next_outgoing_id) != 0) || + (flow_set_outgoing_window(flow, session_instance->outgoing_window) != 0) || + (flow_set_handle(flow, link_endpoint_instance->output_handle) != 0)) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE flow_performative_value = amqpvalue_create_flow(flow); + if (flow_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (encode_frame(link_endpoint, flow_performative_value, NULL, 0) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(flow_performative_value); + } + } + } + } + + return result; +} + +int session_send_attach(LINK_ENDPOINT_HANDLE link_endpoint, ATTACH_HANDLE attach) +{ + int result; + + if ((link_endpoint == NULL) || + (attach == NULL)) + { + result = __FAILURE__; + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + + if (attach_set_handle(attach, link_endpoint_instance->output_handle) != 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE attach_performative_value = amqpvalue_create_attach(attach); + if (attach_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (encode_frame(link_endpoint, attach_performative_value, NULL, 0) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(attach_performative_value); + } + } + } + + return result; +} + +int session_send_disposition(LINK_ENDPOINT_HANDLE link_endpoint, DISPOSITION_HANDLE disposition) +{ + int result; + + if ((link_endpoint == NULL) || + (disposition == NULL)) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE disposition_performative_value = amqpvalue_create_disposition(disposition); + if (disposition_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (encode_frame(link_endpoint, disposition_performative_value, NULL, 0) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(disposition_performative_value); + } + } + + return result; +} + +int session_send_detach(LINK_ENDPOINT_HANDLE link_endpoint, DETACH_HANDLE detach) +{ + int result; + + if ((link_endpoint == NULL) || + (detach == NULL)) + { + result = __FAILURE__; + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + + if (detach_set_handle(detach, link_endpoint_instance->output_handle) != 0) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE detach_performative_value = amqpvalue_create_detach(detach); + if (detach_performative_value == NULL) + { + result = __FAILURE__; + } + else + { + if (encode_frame(link_endpoint, detach_performative_value, NULL, 0) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(detach_performative_value); + } + } + } + + return result; +} + +/* Codes_S_R_S_SESSION_01_051: [session_send_transfer shall send a transfer frame with the performative indicated in the transfer argument.] */ +SESSION_SEND_TRANSFER_RESULT session_send_transfer(LINK_ENDPOINT_HANDLE link_endpoint, TRANSFER_HANDLE transfer, PAYLOAD* payloads, size_t payload_count, delivery_number* delivery_id, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + SESSION_SEND_TRANSFER_RESULT result; + + /* Codes_S_R_S_SESSION_01_054: [If link_endpoint or transfer is NULL, session_send_transfer shall fail and return a non-zero value.] */ + if ((link_endpoint == NULL) || + (transfer == NULL)) + { + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + LINK_ENDPOINT_INSTANCE* link_endpoint_instance = (LINK_ENDPOINT_INSTANCE*)link_endpoint; + SESSION_INSTANCE* session_instance = (SESSION_INSTANCE*)link_endpoint_instance->session; + + /* Codes_S_R_S_SESSION_01_059: [When session_send_transfer is called while the session is not in the MAPPED state, session_send_transfer shall fail and return a non-zero value.] */ + if (session_instance->session_state != SESSION_STATE_MAPPED) + { + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + size_t payload_size = 0; + size_t i; + + for (i = 0; i < payload_count; i++) + { + if ((payloads[i].length > UINT32_MAX) || + (payload_size + payloads[i].length < payload_size)) + { + break; + } + + payload_size += payloads[i].length; + } + + if ((i < payload_count) || + (payload_size > UINT32_MAX)) + { + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + if (session_instance->remote_incoming_window == 0) + { + result = SESSION_SEND_TRANSFER_BUSY; + } + else + { + /* Codes_S_R_S_SESSION_01_012: [The session endpoint assigns each outgoing transfer frame an implicit transfer-id from a session scoped sequence.] */ + /* Codes_S_R_S_SESSION_01_027: [sending a transfer Upon sending a transfer, the sending endpoint will increment its next-outgoing-id] */ + *delivery_id = session_instance->next_outgoing_id; + if ((transfer_set_handle(transfer, link_endpoint_instance->output_handle) != 0) || + (transfer_set_delivery_id(transfer, *delivery_id) != 0) || + (transfer_set_more(transfer, false) != 0)) + { + /* Codes_S_R_S_SESSION_01_058: [When any other error occurs, session_send_transfer shall fail and return a non-zero value.] */ + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + AMQP_VALUE transfer_value; + + transfer_value = amqpvalue_create_transfer(transfer); + if (transfer_value == NULL) + { + /* Codes_S_R_S_SESSION_01_058: [When any other error occurs, session_send_transfer shall fail and return a non-zero value.] */ + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + uint32_t available_frame_size; + size_t encoded_size; + + if ((connection_get_remote_max_frame_size(session_instance->connection, &available_frame_size) != 0) || + (amqpvalue_get_encoded_size(transfer_value, &encoded_size) != 0)) + { + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + payload_size = 0; + + for (i = 0; i < payload_count; i++) + { + payload_size += payloads[i].length; + } + + available_frame_size -= (uint32_t)encoded_size; + available_frame_size -= 8; + + if (available_frame_size >= payload_size) + { + /* Codes_S_R_S_SESSION_01_055: [The encoding of the frame shall be done by calling connection_encode_frame and passing as arguments: the connection handle associated with the session, the transfer performative and the payload chunks passed to session_send_transfer.] */ + if (connection_encode_frame(session_instance->endpoint, transfer_value, payloads, payload_count, on_send_complete, callback_context) != 0) + { + /* Codes_S_R_S_SESSION_01_056: [If connection_encode_frame fails then session_send_transfer shall fail and return a non-zero value.] */ + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + /* Codes_S_R_S_SESSION_01_018: [is incremented after each successive transfer according to RFC-1982 [RFC1982] serial number arithmetic.] */ + session_instance->next_outgoing_id++; + session_instance->remote_incoming_window--; + session_instance->outgoing_window--; + + /* Codes_S_R_S_SESSION_01_053: [On success, session_send_transfer shall return 0.] */ + result = SESSION_SEND_TRANSFER_OK; + } + } + else + { + size_t current_payload_index = 0; + uint32_t current_payload_pos = 0; + + /* break it down into different deliveries */ + while (payload_size > 0) + { + uint32_t transfer_frame_payload_count = 0; + uint32_t current_transfer_frame_payload_size = (uint32_t)payload_size; + uint32_t byte_counter; + size_t temp_current_payload_index = current_payload_index; + uint32_t temp_current_payload_pos = current_payload_pos; + AMQP_VALUE multi_transfer_amqp_value; + PAYLOAD* transfer_frame_payloads; + bool more; + + if (current_transfer_frame_payload_size > available_frame_size) + { + current_transfer_frame_payload_size = available_frame_size; + } + + if (available_frame_size >= payload_size) + { + more = false; + } + else + { + more = true; + } + + if (transfer_set_more(transfer, more) != 0) + { + break; + } + + multi_transfer_amqp_value = amqpvalue_create_transfer(transfer); + if (multi_transfer_amqp_value == NULL) + { + break; + } + + byte_counter = current_transfer_frame_payload_size; + while (byte_counter > 0) + { + if (payloads[temp_current_payload_index].length - temp_current_payload_pos >= byte_counter) + { + /* more data than we need */ + temp_current_payload_pos += byte_counter; + byte_counter = 0; + } + else + { + byte_counter -= (uint32_t)payloads[temp_current_payload_index].length - temp_current_payload_pos; + temp_current_payload_index++; + temp_current_payload_pos = 0; + } + } + + transfer_frame_payload_count = (uint32_t)(temp_current_payload_index - current_payload_index + 1); + transfer_frame_payloads = (PAYLOAD*)malloc(transfer_frame_payload_count * sizeof(PAYLOAD)); + if (transfer_frame_payloads == NULL) + { + amqpvalue_destroy(multi_transfer_amqp_value); + break; + } + + /* copy data */ + byte_counter = current_transfer_frame_payload_size; + transfer_frame_payload_count = 0; + + while (byte_counter > 0) + { + if (payloads[current_payload_index].length - current_payload_pos > byte_counter) + { + /* more data than we need */ + transfer_frame_payloads[transfer_frame_payload_count].bytes = payloads[current_payload_index].bytes + current_payload_pos; + transfer_frame_payloads[transfer_frame_payload_count].length = byte_counter; + current_payload_pos += byte_counter; + byte_counter = 0; + } + else + { + /* copy entire payload and move to the next */ + transfer_frame_payloads[transfer_frame_payload_count].bytes = payloads[current_payload_index].bytes + current_payload_pos; + transfer_frame_payloads[transfer_frame_payload_count].length = payloads[current_payload_index].length - current_payload_pos; + byte_counter -= (uint32_t)payloads[current_payload_index].length - current_payload_pos; + current_payload_index++; + current_payload_pos = 0; + } + + transfer_frame_payload_count++; + } + + if (connection_encode_frame(session_instance->endpoint, multi_transfer_amqp_value, transfer_frame_payloads, transfer_frame_payload_count, on_send_complete, callback_context) != 0) + { + free(transfer_frame_payloads); + amqpvalue_destroy(multi_transfer_amqp_value); + break; + } + + free(transfer_frame_payloads); + amqpvalue_destroy(multi_transfer_amqp_value); + payload_size -= current_transfer_frame_payload_size; + } + + if (payload_size > 0) + { + result = SESSION_SEND_TRANSFER_ERROR; + } + else + { + /* Codes_S_R_S_SESSION_01_018: [is incremented after each successive transfer according to RFC-1982 [RFC1982] serial number arithmetic.] */ + session_instance->next_outgoing_id++; + session_instance->remote_incoming_window--; + session_instance->outgoing_window--; + + result = SESSION_SEND_TRANSFER_OK; + } + } + } + + amqpvalue_destroy(transfer_value); + } + } + } + } + } + } + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/App.config Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> + </startup> +</configuration> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/Program.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,219 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; + +namespace amqplib_generator +{ + class Program + { + private static type _currentTypeObject; + public static type CurrentTypeObject + { + get { return _currentTypeObject; } + } + + private static List<type> types; + + public static amqp LoadAMQPTypes() + { + XmlSerializer serializer = new XmlSerializer(typeof(amqp)); + StreamReader reader = new StreamReader("../../amqp_definitions.xml"); + amqp amqp = (amqp)serializer.Deserialize(reader); + reader.Close(); + return amqp; + } + + public static string GetCType(string amqp_type, bool multiple) + { + string result; + + if (multiple) + { + result = "AMQP_VALUE"; + } + else + { + switch (amqp_type) + { + default: + result = amqp_type.ToLower().Replace('-', '_').Replace(':', '_'); + break; + + case "binary": + result = "amqp_binary"; + break; + + case "boolean": + result = "bool"; + break; + + case "uint": + result = "uint32_t"; + break; + + case "ubyte": + result = "uint8_t"; + break; + + case "ushort": + result = "uint16_t"; + break; + + case "ulong": + result = "uint64_t"; + break; + + case "string": + result = "const char*"; + break; + + case "symbol": + result = "const char*"; + break; + + case "map": + result = "AMQP_VALUE"; + break; + + case "list": + result = "AMQP_VALUE"; + break; + + case "*": + result = "AMQP_VALUE"; + break; + } + } + + return result; + } + + public static type GetTypeByName(string type_name) + { + type result; + IEnumerable<type> result_query = types.Where(t => t.name == type_name); + + if (result_query.Count() != 1) + { + result = null; + } + else + { + result = result_query.First(); + } + + return result; + } + + public static descriptor GetDescriptor(type type) + { + descriptor result; + IEnumerable<descriptor> result_query = type.Items.Where(t => t is descriptor).Cast<descriptor>(); + if (result_query.Count() != 1) + { + result = null; + } + else + { + result = result_query.First(); + } + + return result; + } + + public static UInt64 GetDescriptorCode(descriptor descriptor) + { + UInt64 result; + string[] strings = descriptor.code.Split(new char[] { ':' }); + result = (Convert.ToUInt64(strings[0], 16) << 32) + Convert.ToUInt64(strings[1], 16); + return result; + } + + public static string GetMandatoryArgList(type type) + { + string result = string.Empty; + + foreach(field field in type.Items.Where(item => (item is field) && ((item as field).mandatory == "true"))) + { + if (result.Length > 0) + { + result += ", "; + } + + result += GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_') + " " + field.name.Replace('-', '_').Replace(':', '_') + "_value"; + } + + if (string.IsNullOrEmpty(result)) + { + result = "void"; + } + + return result; + } + + public static string GetMandatoryArgListMock(type type) + { + string result = string.Empty; + + foreach (field field in type.Items.Where(item => (item is field) && ((item as field).mandatory == "true"))) + { + result += ", "; + result += GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_') + ", " + field.name.Replace('-', '_').Replace(':', '_') + "_value"; + } + + return result; + } + + public static ICollection<KeyValuePair<field, int>> GetMandatoryArgs(type type) + { + List<KeyValuePair<field, int>> mandatory_args = new List<KeyValuePair<field, int>>(); + int fieldCount = 0; + for (int i = 0; i < type.Items.Length; i++) + { + if (type.Items[i] is field) + { + if ((type.Items[i] as field).mandatory == "true") + { + mandatory_args.Add(new KeyValuePair<field, int>(type.Items[i] as field, fieldCount)); + } + + fieldCount++; + } + } + return mandatory_args; + } + + static void Main(string[] args) + { + amqp amqp = LoadAMQPTypes(); + + foreach (section section in amqp.Items.Where(item => item is section)) + { + types = new List<type>(); + types.AddRange(section.Items.Where(item => item is type).Cast<type>()); + foreach (type type in types) + { + amqp_definitions_type_h amqp_definitions_type_h = new amqp_definitions_type_h(); + + _currentTypeObject = type; + System.IO.File.WriteAllText($"../../../inc/azure_uamqp_c/amqp_definitions_{type.name.Replace("-","_")}.h", amqp_definitions_type_h.TransformText()); + + } + } + + amqp_definitions_h amqp_definitions_h = new amqp_definitions_h(); + System.IO.File.WriteAllText("../../../inc/azure_uamqp_c/amqp_definitions.h", amqp_definitions_h.TransformText()); + + amqp_definitions_c amqp_definitions_c = new amqp_definitions_c(); + System.IO.File.WriteAllText("../../../src/amqp_definitions.c", amqp_definitions_c.TransformText()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/Properties/AssemblyInfo.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("amqplib_generator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("amqplib_generator")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5234fa1c-13a3-4da4-adb6-d656524556dd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,658 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +using System.Xml.Serialization; + +// +// This source code was auto-generated by xsd, Version=4.0.30319.33440. +// + + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class amqp { + + private object[] itemsField; + + private string nameField; + + private string labelField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc", typeof(doc))] + [System.Xml.Serialization.XmlElementAttribute("section", typeof(section))] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class doc { + + private string nameField; + + private string titleField; + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string title { + get { + return this.titleField; + } + set { + this.titleField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class section { + + private object[] itemsField; + + private string nameField; + + private string titleField; + + private string labelField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("definition", typeof(definition))] + [System.Xml.Serialization.XmlElementAttribute("doc", typeof(doc))] + [System.Xml.Serialization.XmlElementAttribute("type", typeof(type))] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string title { + get { + return this.titleField; + } + set { + this.titleField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class definition { + + private doc[] docField; + + private string nameField; + + private string valueField; + + private string labelField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc")] + public doc[] doc { + get { + return this.docField; + } + set { + this.docField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class type { + + private object[] itemsField; + + private string nameField; + + private typeClass classField; + + private string sourceField; + + private string providesField; + + private string labelField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("choice", typeof(choice))] + [System.Xml.Serialization.XmlElementAttribute("descriptor", typeof(descriptor))] + [System.Xml.Serialization.XmlElementAttribute("doc", typeof(doc))] + [System.Xml.Serialization.XmlElementAttribute("encoding", typeof(encoding))] + [System.Xml.Serialization.XmlElementAttribute("field", typeof(field))] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public typeClass @class { + get { + return this.classField; + } + set { + this.classField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string source { + get { + return this.sourceField; + } + set { + this.sourceField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string provides { + get { + return this.providesField; + } + set { + this.providesField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class choice { + + private doc[] docField; + + private string nameField; + + private string valueField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc")] + public doc[] doc { + get { + return this.docField; + } + set { + this.docField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class descriptor { + + private doc[] docField; + + private string nameField; + + private string codeField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc")] + public doc[] doc { + get { + return this.docField; + } + set { + this.docField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string code { + get { + return this.codeField; + } + set { + this.codeField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class encoding { + + private doc[] docField; + + private string nameField; + + private string labelField; + + private string codeField; + + private encodingCategory categoryField; + + private string widthField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc")] + public doc[] doc { + get { + return this.docField; + } + set { + this.docField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string code { + get { + return this.codeField; + } + set { + this.codeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public encodingCategory category { + get { + return this.categoryField; + } + set { + this.categoryField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string width { + get { + return this.widthField; + } + set { + this.widthField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +public enum encodingCategory { + + /// <remarks/> + @fixed, + + /// <remarks/> + variable, + + /// <remarks/> + compound, + + /// <remarks/> + array, +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/amqp_definitions", IsNullable=false)] +public partial class field { + + private doc[] docField; + + private string nameField; + + private string typeField; + + private string requiresField; + + private string defaultField; + + private string labelField; + + private string mandatoryField; + + private string multipleField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute("doc")] + public doc[] doc { + get { + return this.docField; + } + set { + this.docField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string requires { + get { + return this.requiresField; + } + set { + this.requiresField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string @default { + get { + return this.defaultField; + } + set { + this.defaultField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string label { + get { + return this.labelField; + } + set { + this.labelField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string mandatory { + get { + return this.mandatoryField; + } + set { + this.mandatoryField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string multiple { + get { + return this.multipleField; + } + set { + this.multipleField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/amqp_definitions")] +public enum typeClass { + + /// <remarks/> + primitive, + + /// <remarks/> + composite, + + /// <remarks/> + restricted, + + /// <remarks/> + union, +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions.dtd Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,351 @@ +<?xml version="1.0" encoding="UTF-8"?> + + <!-- + Copyright OASIS Open 2012. All Rights Reserved. + + + All capitalized terms in the following text have the meanings assigned to them in the OASIS + Intellectual Property Rights Policy (the "OASIS IPR Policy"). The full Policy may be found at the + OASIS website. + + + This document and translations of it may be copied and furnished to others, and derivative works + that comment on or otherwise explain it or assist in its implementation may be prepared, copied, + published, and distributed, in whole or in part, without restriction of any kind, provided that + the above copyright notice and this section are included on all such copies and derivative works. + However, this document itself may not be modified in any way, including by removing the copyright + notice or references to OASIS, except as needed for the purpose of developing any document or + deliverable produced by an OASIS Technical Committee (in which case the rules applicable to + copyrights, as set forth in the OASIS IPR Policy, must be followed) or as required to translate + it into languages other than English. + + + The limited permissions granted above are perpetual and will not be revoked by OASIS or its + successors or assigns. + + + This document and the information contained herein is provided on an "AS IS" basis and OASIS + DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE + USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + OASIS requests that any OASIS Party or any other party that believes it has patent claims that + would necessarily be infringed by implementations of this OASIS Committee Specification or OASIS + Standard, to notify OASIS TC Administrator and provide an indication of its willingness to grant + patent licenses to such patent claims in a manner consistent with the IPR Mode of the OASIS + Technical Committee that produced this specification. + + + OASIS invites any party to contact the OASIS TC Administrator if it is aware of a claim of + ownership of any patent claims that would necessarily be infringed by implementations of this + specification by a patent holder that is not willing to provide a license to such patent claims in + a manner consistent with the IPR Mode of the OASIS Technical Committee that produced this + specification. OASIS may include such claims on its website, but disclaims any obligation to do + so. + + + OASIS takes no position regarding the validity or scope of any intellectual property or other + rights that might be claimed to pertain to the implementation or use of the technology described + in this document or the extent to which any license under such rights might or might not be + available; neither does it represent that it has made any effort to identify any such rights. + Information on OASIS' procedures with respect to rights in any document or deliverable produced + by an OASIS Technical Committee can be found on the OASIS website. Copies of claims of rights + made available for publication and any assurances of licenses to be made available, or the result + of an attempt made to obtain a general license or permission for the use of such proprietary + rights by implementers or users of this OASIS Committee Specification or OASIS Standard, can be + obtained from the OASIS TC Administrator. OASIS makes no representation that any information or + list of intellectual property rights will at any time be complete, or that any claims in such list + are, in fact, Essential Claims. + + + The name "OASIS" is a trademark of OASIS, the owner and developer of this specification, and + should be used only to refer to the organization and its official outputs. OASIS welcomes + reference to, and implementation and use of, specifications, while reserving the right to enforce + its marks against misleading uses. Please see http://www.oasis-open.org/who/trademark.php for above guidance. + --> + + <!-- The AMQP specification is defined by a collection of XML source + files each conforming to the DTD defined below. Each file + specifies a distinct "part" of the specification. + --> + + <!-- The amqp element is the root element for each source file and + thus identifies a distinct part of the AMQP specification. The + amqp element may contain any number of doc or section elements + and has the following attributes: + + name - The name of the part of the specification defined within + this element. + label - A short label for the part of the specication defined + within this element + --> + <!ELEMENT amqp (doc|section)*> + <!ATTLIST amqp + xmlns CDATA #IMPLIED + name CDATA #REQUIRED + label CDATA #IMPLIED + > + + <!-- The section element identifies a section within a part of the + AMQP specification. Sections provide a logical grouping of + content and definitions within a part of the specification. The + section element may contain any number of doc, definition, or + type elements and has the following attributes: + + name - The name of the section. + title - A title for the section. + label - A short label for the section. + --> + <!ELEMENT section (doc|definition|type)*> + <!ATTLIST section + name CDATA #REQUIRED + title CDATA #IMPLIED + label CDATA #IMPLIED + > + + <!-- The definition element formally defines a named constant. The + definition element may contain any number of doc elements and has + the following attributes: + + name - The name of the constant. + value - The value of the constant. + label - A short label for the constant. + --> + <!ELEMENT definition (doc)*> + <!ATTLIST definition + name CDATA #REQUIRED + value CDATA #REQUIRED + label CDATA #IMPLIED + > + + <!-- The type element formally defines a semantic type used to + exchange data on the wire. Every type definition may have the + following attributes: + + name - The name of the type. + label - A short description of the type. + class - A string identifying what class of type is being + defined. + provides - A comma separated list of archetypes (see field + element). + + There are four different classes of types identified by the + "class" attribute: primitive, composite, restricted, and union. + All classes of types may contain doc elements. + + A "primitive" type will contain one or more encoding elements + that describe how the data is formatted on the wire. Primitive + types do not contain descriptor, field, or choice elements. + + A "composite" type definition specifies a new kind of record + type, i.e. a type composed of a fixed number of fields whose + values are each of a specific type. A composite type does not + have a new encoding, but is sent on the wire as a list annotated + by a specific descriptor value that identifies it as a + representation of the defined composite type. A composite type + definition will contain a descriptor and zero or more field + elements. Composite types do not contain encoding or choice + elements. The source attribute of a composite type will always be + "list", other values are reserved for future use. + + A "restricted" type definition specifies a new kind of type whose + values are restricted to a subset of the values that may be + represented with another type. The source attribute identifies + the base type from which a restricted type is derived. A + restricted type may have a descriptor element in which case it is + identified by a descriptor on the wire. The values permitted by a + restricted type may be specified either via documentation, or + directly limited to a fixed number of values by the choice + elements contained within the definition. + + The "union" class of types is currently reserved. + --> + <!ELEMENT type (encoding|descriptor|field|choice|doc)*> + <!ATTLIST type + name CDATA #REQUIRED + class (primitive|composite|restricted|union) #REQUIRED + source CDATA #IMPLIED + provides CDATA #IMPLIED + label CDATA #IMPLIED + > + + <!-- The encoding element defines how a primitive type is encoded. The + specification defines 4 general categories of encodings: fixed, + variable, compound, or array. A specific encoding provides + further details of how data is formatted within its general + category. + + name - The name of the encoding. + label - A short description of the encoding. + code - The numeric value that prefixes the encoded data on + the wire. + category - The category of the encoding: "fixed", "variable", + "compound", or "array". + width - The width of the encoding or the size/count + prefixes depending on the category. + --> + <!ELEMENT encoding (doc)*> + <!ATTLIST encoding + name CDATA #IMPLIED + label CDATA #IMPLIED + code CDATA #REQUIRED + category (fixed|variable|compound|array) #REQUIRED + width CDATA #IMPLIED + > + + <!-- The descriptor element specifies what annotation is used to + identify encoded values as representations of a described type. + + name - A symbolic name for the representation. + code - The numeric value. + --> + <!ELEMENT descriptor (doc)*> + <!ATTLIST descriptor + name CDATA #IMPLIED + code CDATA #IMPLIED + > + + <!-- The field element identifies a field within a composite type. + Every field has the following attributes: + + name - A symbolic name that uniquely identifies the field + within the type. + type - The type of the field. This attribute defines the + range of values that are permitted to appear in + this field. It may name a specific type, in which + case the values are restricted to that type, or it + may be the special character "*", in which case a + value of any type is permitted. In the latter case + the range of values may be further restricted by + the requires attribute. + requires - A comma separated list of archetypes. Field values + are restricted to types providing at least one of + the specified archetypes. + default - A default value for the field if no value is encoded. + label - A short description of the field. + mandatory - "true" iff a non null value for the field is always encoded. + multiple - "true" iff the field may have multiple values of its specified type. + --> + <!ELEMENT field (doc)*> + <!ATTLIST field + name CDATA #REQUIRED + type CDATA #IMPLIED + requires CDATA #IMPLIED + default CDATA #IMPLIED + label CDATA #IMPLIED + mandatory CDATA #IMPLIED + multiple CDATA #IMPLIED + > + + <!-- The choice element identifies a legal value for a restricted + type. The choice element has the following attributes: + + name - A symbolic name for the value. + value - The permitted value. + --> + <!ELEMENT choice (doc)*> + <!ATTLIST choice + name CDATA #REQUIRED + value CDATA #REQUIRED + > + + <!-- The doc element identifies the basic unit of documentation that + may appear at nearly any point within the specification. A doc + element may optionally have a symbolic name and a title for cross + reference: + + name - The symbolic name of the doc element. + title - The title of the doc element. + + A doc element may contain any number of the following + presentational sub elements: + + doc - nested doc elements + p - paragraphs + ul - unordered lists + ol - ordered lists + dl - definition lists + picture - preformatted ascii art diagrams + --> + + <!ELEMENT doc (doc|p|ul|ol|dl|picture)*> + <!ATTLIST doc + name CDATA #IMPLIED + title CDATA #IMPLIED + > + + <!-- A paragraph element may be optionally titled and contains + freeform text with the following markup elements: + + anchor - a reference point + xref - a cross reference + b - bold text + i - italic text + term - a formal term + sup - superscript + sub - subscript + br - line break + --> + <!ELEMENT p (#PCDATA|anchor|xref|b|i|term|sup|sub|br)*> + <!ATTLIST p + title CDATA #IMPLIED + > + + <!-- A cross reference. --> + <!ELEMENT xref (#PCDATA)> + <!ATTLIST xref + type CDATA #IMPLIED + name CDATA #REQUIRED + choice CDATA #IMPLIED + > + + <!-- A reference point. --> + <!ELEMENT anchor (#PCDATA)> + <!ATTLIST anchor + name CDATA #REQUIRED + > + + <!-- A line break. --> + <!ELEMENT br EMPTY> + + <!-- A span of boldface markup. --> + <!ELEMENT b (#PCDATA|sub|sup|i|br|anchor)*> + <!-- A span of italic markup. --> + <!ELEMENT i (#PCDATA|sub|sup|b|br|anchor)*> + <!-- A formal term. --> + <!ELEMENT term (#PCDATA)> + <!-- A span of superscript markup. --> + <!ELEMENT sup (#PCDATA|sup|sub|b|i)*> + <!-- A span of subscript markup. --> + <!ELEMENT sub (#PCDATA|sup|sub|b|i)*> + + <!-- An unordered list. --> + <!ELEMENT ul (li)*> + <!ATTLIST ul + title CDATA #IMPLIED + > + <!-- An ordered list. --> + <!ELEMENT ol (li)*> + <!ATTLIST ol + title CDATA #IMPLIED + > + + <!-- An item in an ordered or unordered list. --> + <!ELEMENT li (#PCDATA|p|ul|dl)*> + + <!-- A definition list. --> + <!ELEMENT dl (dt, dd)*> + <!ATTLIST dl + title CDATA #IMPLIED + > + <!-- A definition term. --> + <!ELEMENT dt (#PCDATA)> + <!-- A definition description --> + <!ELEMENT dd (p|picture|ul|ol)*> + + <!-- A preformatted ascii art diagram. --> + <!ELEMENT picture (#PCDATA)> + <!ATTLIST picture + title CDATA #IMPLIED + > \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions.xml Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,299 @@ +<?xml version="1.0" encoding="utf-8" ?> +<amqp xmlns="http://tempuri.org/amqp_definitions" name="root"> + <section name="1"> + <type name="role" class="restricted" source="boolean"> + <choice name="sender" value="false"/> + <choice name="receiver" value="true"/> + </type> + <type name="sender-settle-mode" class="restricted" source="ubyte"> + <choice name="unsettled" value="0"/> + <choice name="settled" value="1"/> + <choice name="mixed" value="2"/> + </type> + <type name="receiver-settle-mode" class="restricted" source="ubyte"> + <choice name="first" value="0"/> + <choice name="second" value="1"/> + </type> + <type name="handle" class="restricted" source="uint"/> + <type name="seconds" class="restricted" source="uint"/> + <type name="milliseconds" class="restricted" source="uint"/> + <type name="delivery-tag" class="restricted" source="binary"/> + <type name="sequence-no" class="restricted" source="uint"/> + <type name="delivery-number" class="restricted" source="sequence-no"/> + <type name="transfer-number" class="restricted" source="sequence-no"/> + <type name="message-format" class="restricted" source="uint"/> + <type name="ietf-language-tag" class="restricted" source="symbol"/> + <type name="fields" class="restricted" source="map"/> + <type name="error" class="composite" source="list"> + <descriptor name="amqp:error:list" code="0x00000000:0x0000001d"/> + <field name="condition" type="symbol" requires="error-condition" mandatory="true"/> + <field name="description" type="string"/> + <field name="info" type="fields"/> + </type> + <type name="amqp-error" class="restricted" source="symbol" provides="error-condition"> + <choice name="internal-error" value="amqp:internal-error"/> + <choice name="not-found" value="amqp:not-found"/> + <choice name="unauthorized-access" value="amqp:unauthorized-access"/> + <choice name="decode-error" value="amqp:decode-error"/> + <choice name="resource-limit-exceeded" value="amqp:resource-limit-exceeded"/> + <choice name="not-allowed" value="amqp:not-allowed"/> + <choice name="invalid-field" value="amqp:invalid-field"/> + <choice name="not-implemented" value="amqp:not-implemented"/> + <choice name="resource-locked" value="amqp:resource-locked"/> + <choice name="precondition-failed" value="amqp:precondition-failed"/> + <choice name="resource-deleted" value="amqp:resource-deleted"/> + <choice name="illegal-state" value="amqp:illegal-state"/> + <choice name="frame-size-too-small" value="amqp:frame-size-too-small"/> + </type> + <type name="connection-error" class="restricted" source="symbol" provides="error-condition"> + <choice name="connection-forced" value="amqp:connection:forced"/> + <choice name="framing-error" value="amqp:connection:framing-error"/> + <choice name="redirect" value="amqp:connection:redirect"/> + </type> + <type name="session-error" class="restricted" source="symbol" provides="error-condition"> + <choice name="window-violation" value="amqp:session:window-violation"/> + <choice name="errant-link" value="amqp:session:errant-link"/> + <choice name="handle-in-use" value="amqp:session:handle-in-use"/> + <choice name="unattached-handle" value="amqp:session:unattached-handle"/> + </type> + <type name="link-error" class="restricted" source="symbol" provides="error-condition"> + <choice name="detach-forced" value="amqp:link:detach-forced"/> + <choice name="transfer-limit-exceeded" value="amqp:link:transfer-limit-exceeded"/> + <choice name="message-size-exceeded" value="amqp:link:message-size-exceeded"/> + <choice name="redirect" value="amqp:link:redirect"/> + <choice name="stolen" value="amqp:link:stolen"/> + </type> + + <type name="open" class="composite" source="list" provides="frame"> + <descriptor name="amqp:open:list" code="0x00000000:0x00000010"/> + <field name="container-id" type="string" mandatory="true"/> + <field name="hostname" type="string"/> + <field name="max-frame-size" type="uint" default="4294967295u"/> + <field name="channel-max" type="ushort" default="65535"/> + <field name="idle-time-out" type="milliseconds"/> + <field name="outgoing-locales" type="ietf-language-tag" multiple="true"/> + <field name="incoming-locales" type="ietf-language-tag" multiple="true"/> + <field name="offered-capabilities" type="symbol" multiple="true"/> + <field name="desired-capabilities" type="symbol" multiple="true"/> + <field name="properties" type="fields"/> + </type> + <type name="begin" class="composite" source="list" provides="frame"> + <descriptor name="amqp:begin:list" code="0x00000000:0x00000011"/> + <field name="remote-channel" type="ushort"/> + <field name="next-outgoing-id" type="transfer-number" mandatory="true"/> + <field name="incoming-window" type="uint" mandatory="true"/> + <field name="outgoing-window" type="uint" mandatory="true"/> + <field name="handle-max" type="handle" default="4294967295u"/> + <field name="offered-capabilities" type="symbol" multiple="true"/> + <field name="desired-capabilities" type="symbol" multiple="true"/> + <field name="properties" type="fields"/> + </type> + <type name="attach" class="composite" source="list" provides="frame"> + <descriptor name="amqp:attach:list" code="0x00000000:0x00000012"/> + <field name="name" type="string" mandatory="true"/> + <field name="handle" type="handle" mandatory="true"/> + <field name="role" type="role" mandatory="true"/> + <field name="snd-settle-mode" type="sender-settle-mode" default="mixed"/> + <field name="rcv-settle-mode" type="receiver-settle-mode" default="first"/> + <field name="source" type="*" requires="source"/> + <field name="target" type="*" requires="target"/> + <field name="unsettled" type="map"/> + <field name="incomplete-unsettled" type="boolean" default="false"/> + <field name="initial-delivery-count" type="sequence-no"/> + <field name="max-message-size" type="ulong"/> + <field name="offered-capabilities" type="symbol" multiple="true"/> + <field name="desired-capabilities" type="symbol" multiple="true"/> + <field name="properties" type="fields"/> + </type> + <type name="flow" class="composite" source="list" provides="frame"> + <descriptor name="amqp:flow:list" code="0x00000000:0x00000013"/> + <field name="next-incoming-id" type="transfer-number"/> + <field name="incoming-window" type="uint" mandatory="true"/> + <field name="next-outgoing-id" type="transfer-number" mandatory="true"/> + <field name="outgoing-window" type="uint" mandatory="true"/> + <field name="handle" type="handle"/> + <field name="delivery-count" type="sequence-no"/> + <field name="link-credit" type="uint"/> + <field name="available" type="uint"/> + <field name="drain" type="boolean" default="false"/> + <field name="echo" type="boolean" default="false"/> + <field name="properties" type="fields"/> + </type> + <type name="transfer" class="composite" source="list" provides="frame"> + <descriptor name="amqp:transfer:list" code="0x00000000:0x00000014"/> + <field name="handle" type="handle" mandatory="true"/> + <field name="delivery-id" type="delivery-number"/> + <field name="delivery-tag" type="delivery-tag"/> + <field name="message-format" type="message-format"/> + <field name="settled" type="boolean"/> + <field name="more" type="boolean" default="false"/> + <field name="rcv-settle-mode" type="receiver-settle-mode"/> + <field name="state" type="*" requires="delivery-state"/> + <field name="resume" type="boolean" default="false"/> + <field name="aborted" type="boolean" default="false"/> + <field name="batchable" type="boolean" default="false"/> + </type> + <type name="disposition" class="composite" source="list" provides="frame"> + <descriptor name="amqp:disposition:list" code="0x00000000:0x00000015"/> + <field name="role" type="role" mandatory="true"/> + <field name="first" type="delivery-number" mandatory="true"/> + <field name="last" type="delivery-number"/> + <field name="settled" type="boolean" default="false"/> + <field name="state" type="*" requires="delivery-state"/> + <field name="batchable" type="boolean" default="false"/> + </type> + <type name="detach" class="composite" source="list" provides="frame"> + <descriptor name="amqp:detach:list" code="0x00000000:0x00000016"/> + <field name="handle" type="handle" mandatory="true"/> + <field name="closed" type="boolean" default="false"/> + <field name="error" type="error"/> + </type> + <type name="end" class="composite" source="list" provides="frame"> + <descriptor name="amqp:end:list" code="0x00000000:0x00000017"/> + <field name="error" type="error"/> + </type> + <type name="close" class="composite" source="list" provides="frame"> + <descriptor name="amqp:close:list" code="0x00000000:0x00000018"/> + <field name="error" type="error"/> + </type> + <type name="sasl-code" class="restricted" source="ubyte"> + <choice name="ok" value="0"/> + <choice name="auth" value="1"/> + <choice name="sys" value="2"/> + <choice name="sys-perm" value="3"/> + <choice name="sys-temp" value="4"/> + </type> + <type name="sasl-mechanisms" class="composite" source="list" provides="sasl-frame"> + <descriptor name="amqp:sasl-mechanisms:list" code="0x00000000:0x00000040"/> + <field name="sasl-server-mechanisms" type="symbol" multiple="true" mandatory="true"/> + </type> + <type name="sasl-init" class="composite" source="list" provides="sasl-frame"> + <descriptor name="amqp:sasl-init:list" code="0x00000000:0x00000041"/> + <field name="mechanism" type="symbol" mandatory="true"/> + <field name="initial-response" type="binary"/> + <field name="hostname" type="string"/> + </type> + <type name="sasl-challenge" class="composite" source="list" provides="sasl-frame"> + <descriptor name="amqp:sasl-challenge:list" code="0x00000000:0x00000042"/> + <field name="challenge" type="binary" mandatory="true"/> + </type> + <type name="sasl-response" class="composite" source="list" provides="sasl-frame"> + <descriptor name="amqp:sasl-response:list" code="0x00000000:0x00000043"/> + <field name="response" type="binary" mandatory="true"/> + </type> + <type name="sasl-outcome" class="composite" source="list" provides="sasl-frame"> + <descriptor name="amqp:sasl-outcome:list" code="0x00000000:0x00000044"/> + <field name="code" type="sasl-code" mandatory="true"/> + <field name="additional-data" type="binary"/> + </type> + <type name="terminus-durability" class="restricted" source="uint"> + <choice name="none" value="0"/> + <choice name="configuration" value="1"/> + <choice name="unsettled-state" value="2"/> + </type> + <type name="terminus-expiry-policy" class="restricted" source="symbol"> + <choice name="link-detach" value="link-detach"/> + <choice name="session-end" value="session-end"/> + <choice name="connection-close" value="connection-close"/> + <choice name="never" value="never"/> + </type> + <type name="node-properties" class="restricted" source="fields"/> + <type name="filter-set" class="restricted" source="map"/> + <type name="source" class="composite" source="list" provides="source"> + <descriptor name="amqp:source:list" code="0x00000000:0x00000028"/> + <field name="address" type="*" requires="address"/> + <field name="durable" type="terminus-durability" default="none"/> + <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/> + <field name="timeout" type="seconds" default="0"/> + <field name="dynamic" type="boolean" default="false"/> + <field name="dynamic-node-properties" type="node-properties"/> + <field name="distribution-mode" type="symbol" requires="distribution-mode"/> + <field name="filter" type="filter-set"/> + <field name="default-outcome" type="*" requires="outcome"/> + <field name="outcomes" type="symbol" multiple="true"/> + <field name="capabilities" type="symbol" multiple="true"/> + </type> + <type name="target" class="composite" source="list" provides="target"> + <descriptor name="amqp:target:list" code="0x00000000:0x00000029"/> + <field name="address" type="*" requires="address"/> + <field name="durable" type="terminus-durability" default="none"/> + <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/> + <field name="timeout" type="seconds" default="0"/> + <field name="dynamic" type="boolean" default="false"/> + <field name="dynamic-node-properties" type="node-properties"/> + <field name="capabilities" type="symbol" multiple="true"/> + </type> + <type name="annotations" class="restricted" source="map"/> + <type name="message-id-ulong" class="restricted" source="ulong" provides="message-id"/> + <type name="message-id-uuid" class="restricted" source="uuid" provides="message-id"/> + <type name="message-id-binary" class="restricted" source="binary" provides="message-id"/> + <type name="message-id-string" class="restricted" source="string" provides="message-id"/> + <type name="address-string" class="restricted" source="string" provides="address"/> + <type name="header" class="composite" source="list" provides="section"> + <descriptor name="amqp:header:list" code="0x00000000:0x00000070"/> + <field name="durable" type="boolean" default="false"/> + <field name="priority" type="ubyte" default="4"/> + <field name="ttl" type="milliseconds"/> + <field name="first-acquirer" type="boolean" default="false"/> + <field name="delivery-count" type="uint" default="0"/> + </type> + <type name="delivery-annotations" class="restricted" source="annotations" provides="section"> + <descriptor name="amqp:delivery-annotations:map" code="0x00000000:0x00000071"/> + </type> + <type name="message-annotations" class="restricted" source="annotations" provides="section"> + <descriptor name="amqp:message-annotations:map" code="0x00000000:0x00000072"/> + </type> + <type name="application-properties" class="restricted" source="map" provides="section"> + <descriptor name="amqp:application-properties:map" code="0x00000000:0x00000074"/> + </type> + <type name="data" class="restricted" source="binary" provides="section"> + <descriptor name="amqp:data:binary" code="0x00000000:0x00000075"/> + </type> + <type name="amqp-sequence" class="restricted" source="list" provides="section"> + <descriptor name="amqp:amqp-sequence:list" code="0x00000000:0x00000076"/> + </type> + <type name="amqp-value" class="restricted" source="*" provides="section"> + <descriptor name="amqp:amqp-value:*" code="0x00000000:0x00000077"/> + </type> + <type name="footer" class="restricted" source="annotations" provides="section"> + <descriptor name="amqp:footer:map" code="0x00000000:0x00000078"/> + </type> + <type name="properties" class="composite" source="list" provides="section"> + <descriptor name="amqp:properties:list" code="0x00000000:0x00000073"/> + <field name="message-id" type="*" requires="message-id"/> + <field name="user-id" type="binary"/> + <field name="to" type="*" requires="address"/> + <field name="subject" type="string"/> + <field name="reply-to" type="*" requires="address"/> + <field name="correlation-id" type="*" requires="message-id"/> + <field name="content-type" type="symbol"/> + <field name="content-encoding" type="symbol"/> + <field name="absolute-expiry-time" type="timestamp"/> + <field name="creation-time" type="timestamp"/> + <field name="group-id" type="string"/> + <field name="group-sequence" type="sequence-no"/> + <field name="reply-to-group-id" type="string"/> + </type> + <type name="received" class="composite" source="list" provides="delivery-state"> + <descriptor name="amqp:received:list" code="0x00000000:0x00000023"/> + <field name="section-number" type="uint" mandatory="true"/> + <field name="section-offset" type="ulong" mandatory="true"/> + </type> + <type name="accepted" class="composite" source="list" provides="delivery-state, outcome"> + <descriptor name="amqp:accepted:list" code="0x00000000:0x00000024"/> + </type> + <type name="rejected" class="composite" source="list" provides="delivery-state, outcome"> + <descriptor name="amqp:rejected:list" code="0x00000000:0x00000025"/> + <field name="error" type="error"/> + </type> + <type name="released" class="composite" source="list" provides="delivery-state, outcome"> + <descriptor name="amqp:released:list" code="0x00000000:0x00000026"/> + </type> + <type name="modified" class="composite" source="list" provides="delivery-state, outcome"> + <descriptor name="amqp:modified:list" code="0x00000000:0x00000027"/> + <field name="delivery-failed" type="boolean"/> + <field name="undeliverable-here" type="boolean"/> + <field name="message-annotations" type="fields"/> + </type> + </section> +</amqp>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions.xsd Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<xs:schema xmlns="http://tempuri.org/amqp_definitions" elementFormDefault="qualified" targetNamespace="http://tempuri.org/amqp_definitions" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="amqp"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + <xs:element ref="section" /> + </xs:choice> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="label" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="section"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + <xs:element ref="definition" /> + <xs:element ref="type" /> + </xs:choice> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="title" type="xs:string" /> + <xs:attribute name="label" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="definition"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="value" type="xs:string" use="required" /> + <xs:attribute name="label" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="type"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="encoding" /> + <xs:element ref="descriptor" /> + <xs:element ref="field" /> + <xs:element ref="choice" /> + <xs:element ref="doc" /> + </xs:choice> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="class" use="required"> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="primitive" /> + <xs:enumeration value="composite" /> + <xs:enumeration value="restricted" /> + <xs:enumeration value="union" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="source" type="xs:string" /> + <xs:attribute name="provides" type="xs:string" /> + <xs:attribute name="label" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="encoding"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + </xs:sequence> + <xs:attribute name="name" type="xs:string" /> + <xs:attribute name="label" type="xs:string" /> + <xs:attribute name="code" type="xs:string" use="required" /> + <xs:attribute name="category" use="required"> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="fixed" /> + <xs:enumeration value="variable" /> + <xs:enumeration value="compound" /> + <xs:enumeration value="array" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="width" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="descriptor"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + </xs:sequence> + <xs:attribute name="name" type="xs:string" /> + <xs:attribute name="code" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="field"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" /> + <xs:attribute name="requires" type="xs:string" /> + <xs:attribute name="default" type="xs:string" /> + <xs:attribute name="label" type="xs:string" /> + <xs:attribute name="mandatory" type="xs:string" /> + <xs:attribute name="multiple" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="choice"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="doc" /> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="value" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="doc"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" /> + <xs:attribute name="title" type="xs:string" /> + </xs:complexType> + </xs:element> +</xs:schema> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_c.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,2957 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version: 15.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ +namespace amqplib_generator +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using amqplib_generator; + using System; + + /// <summary> + /// Class to produce the template output + /// </summary> + + #line 1 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public partial class amqp_definitions_c : amqp_definitions_cBase + { +#line hidden + /// <summary> + /// Create the template output + /// </summary> + public virtual string TransformText() + { + this.Write("\r\n"); + + #line 8 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + amqp amqp = Program.LoadAMQPTypes(); + + #line default + #line hidden + this.Write(@" +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#include <stdlib.h> +#include ""azure_c_shared_utility/optimize_size.h"" +#include ""azure_c_shared_utility/gballoc.h"" +#include ""azure_uamqp_c/amqpvalue.h"" +#include ""azure_uamqp_c/amqp_definitions.h"" +#include <stdlib.h> +#include <stdbool.h> + +"); + + #line 21 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + foreach (section section in amqp.Items.Where(item => item is section)) + + #line default + #line hidden + + #line 22 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 23 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + List<type> types = new List<type>(); + + #line default + #line hidden + + #line 24 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + types.AddRange(section.Items.Where(item => item is type).Cast<type>()); + + #line default + #line hidden + + #line 25 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + foreach (type type in types) + + #line default + #line hidden + + #line 26 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 27 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string type_name = type.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + + #line 28 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (type.@class == typeClass.composite) + + #line default + #line hidden + + #line 29 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 30 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + var descriptor = type.Items.Where(item => item is descriptor).First() as descriptor; + + #line default + #line hidden + this.Write("/* "); + + #line 31 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type.name)); + + #line default + #line hidden + this.Write(" */\r\n\r\ntypedef struct "); + + #line 33 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE_TAG\r\n{\r\n AMQP_VALUE composite_value;\r\n} "); + + #line 36 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE;\r\n\r\n"); + + #line 38 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string arg_list = Program.GetMandatoryArgList(type); + + #line default + #line hidden + + #line 39 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + KeyValuePair<field, int>[] mandatory_args = Program.GetMandatoryArgs(type).ToArray(); + + #line default + #line hidden + this.Write("static "); + + #line 40 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 40 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_create_internal(void)\r\n{\r\n "); + + #line 42 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 42 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 42 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)malloc(sizeof("); + + #line 42 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE));\r\n if ("); + + #line 43 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance != NULL)\r\n {\r\n "); + + #line 45 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value = NULL;\r\n }\r\n\r\n return "); + + #line 48 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance;\r\n}\r\n\r\n"); + + #line 51 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 51 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_create("); + + #line 51 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(arg_list)); + + #line default + #line hidden + this.Write(")\r\n{\r\n "); + + #line 53 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 53 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 53 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)malloc(sizeof("); + + #line 53 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE));\r\n if ("); + + #line 54 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance != NULL)\r\n {\r\n "); + + #line 56 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor("); + + #line 56 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(Program.GetDescriptor(type)))); + + #line default + #line hidden + this.Write(");\r\n if ("); + + #line 57 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value == NULL)\r\n {\r\n free("); + + #line 59 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance);\r\n "); + + #line 60 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = NULL;\r\n }\r\n"); + + #line 62 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (mandatory_args.Count() > 0) + + #line default + #line hidden + + #line 63 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" else\r\n {\r\n"); + + #line 66 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + for (int i = 0; i < mandatory_args.Count(); i++) + + #line default + #line hidden + + #line 67 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 68 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + this.Write(" AMQP_VALUE "); + + #line 69 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_arg_name)); + + #line default + #line hidden + this.Write("_amqp_value;\r\n"); + + #line 70 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" int result = 0;\r\n\r\n"); + + #line 73 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + for (int i = 0; i < mandatory_args.Count(); i++) + + #line default + #line hidden + + #line 74 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 75 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string mandatory_arg_type = Program.GetCType(mandatory_args[i].Key.type.ToLower(), mandatory_args[i].Key.multiple == "true").Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 76 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 77 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (mandatory_args[i].Key.multiple != "true") + + #line default + #line hidden + + #line 78 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" "); + + #line 79 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_arg_name)); + + #line default + #line hidden + this.Write("_amqp_value = amqpvalue_create_"); + + #line 79 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_args[i].Key.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("("); + + #line 79 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("_value);\r\n"); + + #line 80 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 81 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 82 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" "); + + #line 83 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_arg_name)); + + #line default + #line hidden + this.Write("_amqp_value = "); + + #line 83 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("_value;\r\n"); + + #line 84 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" if ((result == 0) && (amqpvalue_set_composite_item("); + + #line 85 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value, "); + + #line 85 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_args[i].Value)); + + #line default + #line hidden + this.Write(", "); + + #line 85 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_arg_name)); + + #line default + #line hidden + this.Write("_amqp_value) != 0))\r\n {\r\n result = __FAILURE__;\r\n " + + " }\r\n"); + + #line 89 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 91 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + for (int i = 0; i < mandatory_args.Count(); i++) + + #line default + #line hidden + + #line 92 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 93 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + this.Write(" amqpvalue_destroy("); + + #line 94 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(mandatory_arg_name)); + + #line default + #line hidden + this.Write("_amqp_value);\r\n"); + + #line 95 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n"); + + #line 97 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n\r\n return "); + + #line 100 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance;\r\n}\r\n\r\n"); + + #line 103 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 103 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_clone("); + + #line 103 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE value)\r\n{\r\n "); + + #line 105 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 105 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 105 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)malloc(sizeof("); + + #line 105 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE));\r\n if ("); + + #line 106 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance != NULL)\r\n {\r\n "); + + #line 108 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value = amqpvalue_clone((("); + + #line 108 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)value)->composite_value);\r\n if ("); + + #line 109 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value == NULL)\r\n {\r\n free("); + + #line 111 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance);\r\n "); + + #line 112 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = NULL;\r\n }\r\n }\r\n\r\n return "); + + #line 116 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance;\r\n}\r\n\r\nvoid "); + + #line 119 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy("); + + #line 119 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 119 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(")\r\n{\r\n if ("); + + #line 121 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(" != NULL)\r\n {\r\n "); + + #line 123 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 123 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 123 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)"); + + #line 123 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(";\r\n amqpvalue_destroy("); + + #line 124 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value);\r\n free("); + + #line 125 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance);\r\n }\r\n}\r\n\r\nAMQP_VALUE amqpvalue_create_"); + + #line 129 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("("); + + #line 129 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 129 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(")\r\n{\r\n AMQP_VALUE result;\r\n\r\n if ("); + + #line 133 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(" == NULL)\r\n {\r\n result = NULL;\r\n }\r\n else\r\n {\r\n "); + + #line 139 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 139 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 139 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)"); + + #line 139 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(";\r\n result = amqpvalue_clone("); + + #line 140 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value);\r\n }\r\n\r\n return result;\r\n}\r\n\r\nbool is_"); + + #line 146 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_type_by_descriptor(AMQP_VALUE descriptor)\r\n{\r\n bool result;\r\n\r\n uint64_t d" + + "escriptor_ulong;\r\n if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) ==" + + " 0) &&\r\n (descriptor_ulong == "); + + #line 152 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(descriptor).ToString())); + + #line default + #line hidden + this.Write("))\r\n {\r\n result = true;\r\n }\r\n else\r\n {\r\n result = false" + + ";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n\r\nint amqpvalue_get_"); + + #line 165 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("(AMQP_VALUE value, "); + + #line 165 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE* "); + + #line 165 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle)\r\n{\r\n int result;\r\n "); + + #line 168 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 168 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 168 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)"); + + #line 168 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_create_internal();\r\n *"); + + #line 169 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle = "); + + #line 169 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_instance;\r\n if (*"); + + #line 170 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle == NULL)\r\n {\r\n result = __FAILURE__;\r\n }\r\n else\r\n {\r\n " + + " AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value);\r\n " + + " if (list_value == NULL)\r\n {\r\n "); + + #line 179 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy(*"); + + #line 179 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(@"_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { +"); + + #line 193 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + int k = 0; + + #line default + #line hidden + + #line 194 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + bool first_one = true; + + #line default + #line hidden + + #line 195 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + foreach (field field in type.Items.Where(item => item is field)) + + #line default + #line hidden + + #line 196 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 197 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string field_name = field.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + + #line 198 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string c_type = Program.GetCType(field.type, false).Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 199 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + type field_type = Program.GetTypeByName(field.type); + + #line default + #line hidden + + #line 200 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; + + #line default + #line hidden + + #line 201 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (first_one) + + #line default + #line hidden + + #line 202 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 203 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + first_one = false; + + #line default + #line hidden + this.Write(" AMQP_VALUE item_value;\r\n"); + + #line 205 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" /* "); + + #line 206 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.name)); + + #line default + #line hidden + this.Write(" */\r\n if (list_item_count > "); + + #line 207 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(k)); + + #line default + #line hidden + this.Write(")\r\n {\r\n item_value = amqpvalue_get_list" + + "_item(list_value, "); + + #line 209 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(k)); + + #line default + #line hidden + this.Write(");\r\n if (item_value == NULL)\r\n {\r\n"); + + #line 212 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.mandatory == "true") + + #line default + #line hidden + + #line 213 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" {\r\n "); + + #line 215 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy(*"); + + #line 215 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle);\r\n result = __FAILURE__;\r\n " + + " break;\r\n }\r\n"); + + #line 219 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 220 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 221 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" /* do nothing */\r\n"); + + #line 223 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n else\r\n " + + "{\r\n"); + + #line 227 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.type != "*") + + #line default + #line hidden + + #line 228 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)" + + "\r\n {\r\n"); + + #line 231 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.mandatory == "true") + + #line default + #line hidden + + #line 232 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" amqpvalue_destroy(item_value);\r\n " + + " "); + + #line 234 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy(*"); + + #line 234 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle);\r\n result = __FAILURE__;\r\n " + + " break;\r\n"); + + #line 237 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 238 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 239 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" /* no error, field is not mandatory */\r\n"); + + #line 241 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n else\r\n " + + " {\r\n"); + + #line 245 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.multiple != "true") + + #line default + #line hidden + + #line 246 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" "); + + #line 247 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write(" "); + + #line 247 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(";\r\n if (amqpvalue_get_"); + + #line 248 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(item_value, &"); + + #line 248 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(") != 0)\r\n"); + + #line 249 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 250 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 251 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" "); + + #line 252 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write(" "); + + #line 252 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(" = NULL;\r\n AMQP_VALUE "); + + #line 253 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_array;\r\n if (((amqpvalue_get_type(item_value) != " + + "AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &"); + + #line 254 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_array) != 0)) &&\r\n (amqpvalue_get_"); + + #line 255 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(item_value, &"); + + #line 255 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(") != 0))\r\n"); + + #line 256 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" {\r\n amqpvalue_" + + "destroy(item_value);\r\n "); + + #line 259 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy(*"); + + #line 259 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_handle);\r\n result = __FAILURE__;\r\n " + + " break;\r\n }\r\n"); + + #line 263 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (c_type == "ERROR_HANDLE") + + #line default + #line hidden + + #line 264 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" else\r\n {\r\n " + + " error_destroy("); + + #line 267 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name.ToLower())); + + #line default + #line hidden + this.Write(");\r\n }\r\n"); + + #line 269 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n\r\n"); + + #line 272 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 273 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 274 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 275 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" amqpvalue_destroy(item_value);\r\n " + + " }\r\n }\r\n"); + + #line 279 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.mandatory == "true") + + #line default + #line hidden + + #line 280 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" else\r\n {\r\n result =" + + " __FAILURE__;\r\n }\r\n"); + + #line 285 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 286 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + k++; + + #line default + #line hidden + + #line 287 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write("\r\n "); + + #line 289 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_instance->composite_value = amqpvalue_clone(value);\r\n\r\n resul" + + "t = 0;\r\n } while((void)0,0);\r\n }\r\n }\r\n }\r\n\r\n" + + " return result;\r\n}\r\n\r\n"); + + #line 300 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + int j = 0; + + #line default + #line hidden + + #line 301 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + foreach (field field in type.Items.Where(item => item is field)) + + #line default + #line hidden + + #line 302 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 303 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string field_name = field.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + + #line 304 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string c_type = Program.GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 305 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string single_value_c_type = Program.GetCType(field.type, false).Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 306 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + type field_type = Program.GetTypeByName(field.type); + + #line default + #line hidden + + #line 307 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; + + #line default + #line hidden + this.Write("int "); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_get_"); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("("); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", "); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write("* "); + + #line 308 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value)\r\n{\r\n int result;\r\n\r\n if ("); + + #line 312 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(" == NULL)\r\n {\r\n result = __FAILURE__;\r\n }\r\n else\r\n {\r\n " + + "uint32_t item_count;\r\n "); + + #line 319 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 319 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 319 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)"); + + #line 319 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(";\r\n if (amqpvalue_get_composite_item_count("); + + #line 320 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value, &item_count) != 0)\r\n {\r\n result = _" + + "_FAILURE__;\r\n }\r\n else\r\n {\r\n if (item_count <= "); + + #line 326 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(j)); + + #line default + #line hidden + this.Write(")\r\n {\r\n"); + + #line 328 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.@default != null) + + #line default + #line hidden + + #line 329 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 330 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) + + #line default + #line hidden + + #line 331 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 332 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 332 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_type.@name.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("_"); + + #line 332 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 333 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 334 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 335 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 336 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 336 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default)); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 337 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" result = 0;\r\n"); + + #line 339 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 340 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 341 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" result = __FAILURE__;\r\n"); + + #line 343 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n else\r\n {\r\n AMQP_VALUE item_v" + + "alue = amqpvalue_get_composite_item_in_place("); + + #line 347 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value, "); + + #line 347 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(j)); + + #line default + #line hidden + this.Write(");\r\n if ((item_value == NULL) ||\r\n (amqpvalue_g" + + "et_type(item_value) == AMQP_TYPE_NULL))\r\n {\r\n"); + + #line 351 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.@default != null) + + #line default + #line hidden + + #line 352 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 353 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) + + #line default + #line hidden + + #line 354 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 355 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 355 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_type.@name.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("_"); + + #line 355 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 356 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 357 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 358 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 359 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 359 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default)); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 360 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" result = 0;\r\n"); + + #line 362 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 363 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 364 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" result = __FAILURE__;\r\n"); + + #line 366 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n else\r\n {\r\n"); + + #line 370 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.multiple == "true") + + #line default + #line hidden + + #line 371 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" "); + + #line 372 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(single_value_c_type)); + + #line default + #line hidden + this.Write(" "); + + #line 372 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_single_value;\r\n"); + + #line 373 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 374 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.type.Replace('-', '_').Replace(':', '_') == "*") + + #line default + #line hidden + + #line 375 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 376 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = item_value;\r\n result = 0;\r\n"); + + #line 378 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 379 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 380 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 382 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.multiple != "true") + + #line default + #line hidden + + #line 383 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" int get_single_value_result = amqpvalue_get_"); + + #line 384 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(item_value, "); + + #line 384 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n if (get_single_value_result != 0)\r\n"); + + #line 385 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 386 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 387 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" int get_single_value_result;\r\n if (amqpval" + + "ue_get_type(item_value) != AMQP_TYPE_ARRAY)\r\n {\r\n " + + " get_single_value_result = amqpvalue_get_"); + + #line 390 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(item_value, &"); + + #line 390 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_single_value);\r\n }\r\n else\r\n " + + " {\r\n (void)memset((void*)&"); + + #line 394 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_single_value, 0, sizeof("); + + #line 394 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_single_value));\r\n get_single_value_result = 1;\r\n " + + " }\r\n\r\n if (((amqpvalue_get_type(item_value) != AMQP" + + "_TYPE_ARRAY) || (amqpvalue_get_array(item_value, "); + + #line 398 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value) != 0)) &&\r\n (get_single_value_result != 0))\r\n"); + + #line 400 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" {\r\n"); + + #line 402 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.@default != null) + + #line default + #line hidden + + #line 403 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL)\r\n " + + " {\r\n result = __FAILURE__;\r\n " + + " }\r\n else\r\n {\r\n" + + ""); + + #line 410 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) + + #line default + #line hidden + + #line 411 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 412 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 412 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_type.@name.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("_"); + + #line 412 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 413 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 414 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 415 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" *"); + + #line 416 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = "); + + #line 416 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.@default)); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 417 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" result = 0;\r\n }\r\n"); + + #line 420 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 421 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 422 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" result = __FAILURE__;\r\n"); + + #line 424 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n else\r\n {\r\n"); + + #line 428 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (field.multiple == "true") + + #line default + #line hidden + + #line 429 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY)\r\n " + + " {\r\n *"); + + #line 432 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value = amqpvalue_create_array();\r\n if (*"); + + #line 433 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(@"_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_"); + + #line 439 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("("); + + #line 439 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_single_value);\r\n if (single_amqp_value == NULL)\r\n" + + " {\r\n amqpvalue" + + "_destroy(*"); + + #line 442 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n result = __FAILURE__;\r\n " + + " }\r\n else\r\n " + + " {\r\n if (amqpvalue_add_array_item" + + "(*"); + + #line 447 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value, single_amqp_value) != 0)\r\n {\r\n " + + " amqpvalue_destroy(*"); + + #line 449 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(@"_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item("); + + #line 455 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value, "); + + #line 455 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(j)); + + #line default + #line hidden + this.Write(", *"); + + #line 455 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value) != 0)\r\n {\r\n " + + " amqpvalue_destroy(*"); + + #line 457 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(@"_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*"); + + #line 468 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n }\r\n }\r\n " + + " else\r\n {\r\n result =" + + " 0;\r\n }\r\n"); + + #line 475 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 476 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 477 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" result = 0;\r\n"); + + #line 479 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n"); + + #line 481 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" }\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nin" + + "t "); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_set_"); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("("); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE "); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", "); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write(" "); + + #line 490 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value)\r\n{\r\n int result;\r\n\r\n if ("); + + #line 494 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(" == NULL)\r\n {\r\n result = __FAILURE__;\r\n }\r\n else\r\n {\r\n " + + ""); + + #line 500 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE* "); + + #line 500 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance = ("); + + #line 500 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE*)"); + + #line 500 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(";\r\n"); + + #line 501 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (c_type != "AMQP_VALUE") + + #line default + #line hidden + + #line 502 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" AMQP_VALUE "); + + #line 503 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value = amqpvalue_create_"); + + #line 503 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field.type.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("("); + + #line 503 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n"); + + #line 504 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 505 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 506 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write(" AMQP_VALUE "); + + #line 507 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value;\r\n if ("); + + #line 508 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value == NULL)\r\n {\r\n "); + + #line 510 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value = NULL;\r\n }\r\n else\r\n {\r\n "); + + #line 514 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value = amqpvalue_clone("); + + #line 514 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n }\r\n"); + + #line 516 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write(" if ("); + + #line 517 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value == NULL)\r\n {\r\n result = __FAILURE__;\r\n }\r\n " + + " else\r\n {\r\n if (amqpvalue_set_composite_item("); + + #line 523 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_instance->composite_value, "); + + #line 523 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(j)); + + #line default + #line hidden + this.Write(", "); + + #line 523 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value) != 0)\r\n {\r\n result = __FAILURE__;\r\n " + + " }\r\n else\r\n {\r\n result = 0;\r\n " + + " }\r\n\r\n amqpvalue_destroy("); + + #line 532 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_amqp_value);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n"); + + #line 539 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + j++; + + #line default + #line hidden + + #line 540 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 542 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 543 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else if (type.@class == typeClass.restricted) + + #line default + #line hidden + + #line 544 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 545 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + string c_type = Program.GetCType(type.source, false).Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 546 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + bool hasDescriptor = (type.Items != null) && (type.Items.Where(item => item is descriptor).Count() > 0); + + #line default + #line hidden + this.Write("/* "); + + #line 547 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type.name)); + + #line default + #line hidden + this.Write(" */\r\n\r\n"); + + #line 549 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (c_type != "AMQP_VALUE") + + #line default + #line hidden + + #line 550 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 551 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (!hasDescriptor) + + #line default + #line hidden + + #line 552 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write("AMQP_VALUE amqpvalue_create_"); + + #line 553 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("("); + + #line 553 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(" value)\r\n{\r\n return amqpvalue_create_"); + + #line 555 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type.source.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(value);\r\n}\r\n"); + + #line 557 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 558 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 559 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write("AMQP_VALUE amqpvalue_create_"); + + #line 560 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("("); + + #line 560 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(" value)\r\n{\r\n AMQP_VALUE result;\r\n AMQP_VALUE described_value = amqpvalue_cr" + + "eate_"); + + #line 563 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type.source.ToLower().Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("(value);\r\n if (described_value == NULL)\r\n {\r\n result = NULL;\r\n }\r" + + "\n else\r\n {\r\n AMQP_VALUE descriptor = amqpvalue_create_ulong("); + + #line 570 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(Program.GetDescriptor(type)))); + + #line default + #line hidden + this.Write(@"); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_"); + + #line 588 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_type_by_descriptor(AMQP_VALUE descriptor)\r\n{\r\n bool result;\r\n\r\n uint64_t d" + + "escriptor_ulong;\r\n if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) ==" + + " 0) &&\r\n (descriptor_ulong == "); + + #line 594 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(Program.GetDescriptor(type)).ToString())); + + #line default + #line hidden + this.Write("))\r\n {\r\n result = true;\r\n }\r\n else\r\n {\r\n result = false" + + ";\r\n }\r\n\r\n return result;\r\n}\r\n"); + + #line 605 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 606 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 607 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 608 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + + #line 609 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + if (!hasDescriptor) + + #line default + #line hidden + + #line 610 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write("AMQP_VALUE amqpvalue_create_"); + + #line 611 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("(AMQP_VALUE value)\r\n{\r\n return amqpvalue_clone(value);\r\n}\r\n"); + + #line 615 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 616 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + else + + #line default + #line hidden + + #line 617 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + { + + #line default + #line hidden + this.Write("AMQP_VALUE amqpvalue_create_"); + + #line 618 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("(AMQP_VALUE value)\r\n{\r\n AMQP_VALUE result;\r\n AMQP_VALUE described_value = a" + + "mqpvalue_clone(value);\r\n if (described_value == NULL)\r\n {\r\n result " + + "= NULL;\r\n }\r\n else\r\n {\r\n AMQP_VALUE descriptor = amqpvalue_creat" + + "e_ulong("); + + #line 628 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(Program.GetDescriptor(type)))); + + #line default + #line hidden + this.Write(@"); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_"); + + #line 646 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_type_by_descriptor(AMQP_VALUE descriptor)\r\n{\r\n bool result;\r\n\r\n uint64_t d" + + "escriptor_ulong;\r\n if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) ==" + + " 0) &&\r\n (descriptor_ulong == "); + + #line 652 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(Program.GetDescriptorCode(Program.GetDescriptor(type)).ToString())); + + #line default + #line hidden + this.Write("))\r\n {\r\n result = true;\r\n }\r\n else\r\n {\r\n result = false" + + ";\r\n }\r\n\r\n return result;\r\n}\r\n"); + + #line 663 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 664 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 666 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 667 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + + #line 668 "E:\iot\azure-iot-sdk-c\uamqp\uamqp_generator\amqp_definitions_c.tt" + } + + #line default + #line hidden + return this.GenerationEnvironment.ToString(); + } + } + + #line default + #line hidden + #region Base class + /// <summary> + /// Base class for this transformation + /// </summary> + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public class amqp_definitions_cBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List<int> indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary<string, object> sessionField; + #endregion + #region Properties + /// <summary> + /// The string builder that generation-time code is using to assemble generated output + /// </summary> + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// <summary> + /// The error collection for the generation process + /// </summary> + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// <summary> + /// A list of the lengths of each indent that was added with PushIndent + /// </summary> + private System.Collections.Generic.List<int> indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List<int>(); + } + return this.indentLengthsField; + } + } + /// <summary> + /// Gets the current indent we use when adding lines to the output + /// </summary> + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// <summary> + /// Current transformation session + /// </summary> + public virtual global::System.Collections.Generic.IDictionary<string, object> Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Raise an error + /// </summary> + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// <summary> + /// Raise a warning + /// </summary> + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// <summary> + /// Increase the indent + /// </summary> + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// <summary> + /// Remove the last indent that was added with PushIndent + /// </summary> + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// <summary> + /// Remove any indentation + /// </summary> + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// <summary> + /// Utility class to produce culture-oriented representation of an object as a string. + /// </summary> + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// <summary> + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// </summary> + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// <summary> + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// </summary> + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// <summary> + /// Helper to produce culture-oriented representation of an object as a string + /// </summary> + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_c.tt Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,672 @@ +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="amqplib_generator" #> + +<# amqp amqp = Program.LoadAMQPTypes(); #> + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_uamqp_c/amqp_definitions.h" +#include <stdlib.h> +#include <stdbool.h> + +<# foreach (section section in amqp.Items.Where(item => item is section)) #> +<# { #> +<# List<type> types = new List<type>(); #> +<# types.AddRange(section.Items.Where(item => item is type).Cast<type>()); #> +<# foreach (type type in types) #> +<# { #> +<# string type_name = type.name.ToLower().Replace('-', '_'); #> +<# if (type.@class == typeClass.composite) #> +<# { #> +<# var descriptor = type.Items.Where(item => item is descriptor).First() as descriptor; #> +/* <#= type.name #> */ + +typedef struct <#= type_name.ToUpper() #>_INSTANCE_TAG +{ + AMQP_VALUE composite_value; +} <#= type_name.ToUpper() #>_INSTANCE; + +<# string arg_list = Program.GetMandatoryArgList(type); #> +<# KeyValuePair<field, int>[] mandatory_args = Program.GetMandatoryArgs(type).ToArray(); #> +static <#= type_name.ToUpper() #>_HANDLE <#= type_name #>_create_internal(void) +{ + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)malloc(sizeof(<#= type_name.ToUpper() #>_INSTANCE)); + if (<#= type_name #>_instance != NULL) + { + <#= type_name #>_instance->composite_value = NULL; + } + + return <#= type_name #>_instance; +} + +<#= type_name.ToUpper() #>_HANDLE <#= type_name #>_create(<#= arg_list #>) +{ + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)malloc(sizeof(<#= type_name.ToUpper() #>_INSTANCE)); + if (<#= type_name #>_instance != NULL) + { + <#= type_name #>_instance->composite_value = amqpvalue_create_composite_with_ulong_descriptor(<#= Program.GetDescriptorCode(Program.GetDescriptor(type)) #>); + if (<#= type_name #>_instance->composite_value == NULL) + { + free(<#= type_name #>_instance); + <#= type_name #>_instance = NULL; + } +<# if (mandatory_args.Count() > 0) #> +<# { #> + else + { +<# for (int i = 0; i < mandatory_args.Count(); i++) #> +<# { #> +<# string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); #> + AMQP_VALUE <#= mandatory_arg_name #>_amqp_value; +<# } #> + int result = 0; + +<# for (int i = 0; i < mandatory_args.Count(); i++) #> +<# { #> +<# string mandatory_arg_type = Program.GetCType(mandatory_args[i].Key.type.ToLower(), mandatory_args[i].Key.multiple == "true").Replace('-', '_').Replace(':', '_'); #> +<# string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); #> +<# if (mandatory_args[i].Key.multiple != "true") #> +<# { #> + <#= mandatory_arg_name #>_amqp_value = amqpvalue_create_<#= mandatory_args[i].Key.type.ToLower().Replace('-', '_').Replace(':', '_') #>(<#= mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_') #>_value); +<# } #> +<# else #> +<# { #> + <#= mandatory_arg_name #>_amqp_value = <#= mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_') #>_value; +<# } #> + if ((result == 0) && (amqpvalue_set_composite_item(<#= type_name #>_instance->composite_value, <#= mandatory_args[i].Value #>, <#= mandatory_arg_name #>_amqp_value) != 0)) + { + result = __FAILURE__; + } +<# } #> + +<# for (int i = 0; i < mandatory_args.Count(); i++) #> +<# { #> +<# string mandatory_arg_name = mandatory_args[i].Key.name.ToLower().Replace('-', '_').Replace(':', '_'); #> + amqpvalue_destroy(<#= mandatory_arg_name #>_amqp_value); +<# } #> + } +<# } #> + } + + return <#= type_name #>_instance; +} + +<#= type_name.ToUpper() #>_HANDLE <#= type_name #>_clone(<#= type_name.ToUpper() #>_HANDLE value) +{ + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)malloc(sizeof(<#= type_name.ToUpper() #>_INSTANCE)); + if (<#= type_name #>_instance != NULL) + { + <#= type_name #>_instance->composite_value = amqpvalue_clone(((<#= type_name.ToUpper() #>_INSTANCE*)value)->composite_value); + if (<#= type_name #>_instance->composite_value == NULL) + { + free(<#= type_name #>_instance); + <#= type_name #>_instance = NULL; + } + } + + return <#= type_name #>_instance; +} + +void <#= type_name #>_destroy(<#= type_name.ToUpper() #>_HANDLE <#= type_name #>) +{ + if (<#= type_name #> != NULL) + { + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)<#= type_name #>; + amqpvalue_destroy(<#= type_name #>_instance->composite_value); + free(<#= type_name #>_instance); + } +} + +AMQP_VALUE amqpvalue_create_<#= type_name #>(<#= type_name.ToUpper() #>_HANDLE <#= type_name #>) +{ + AMQP_VALUE result; + + if (<#= type_name #> == NULL) + { + result = NULL; + } + else + { + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)<#= type_name #>; + result = amqpvalue_clone(<#= type_name #>_instance->composite_value); + } + + return result; +} + +bool is_<#= type_name #>_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == <#= Program.GetDescriptorCode(descriptor).ToString() #>)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + + +int amqpvalue_get_<#= type_name #>(AMQP_VALUE value, <#= type_name.ToUpper() #>_HANDLE* <#= type_name.ToLower() #>_handle) +{ + int result; + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name.ToLower() #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)<#= type_name #>_create_internal(); + *<#= type_name.ToLower() #>_handle = <#= type_name.ToLower() #>_instance; + if (*<#= type_name.ToLower() #>_handle == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE list_value = amqpvalue_get_inplace_described_value(value); + if (list_value == NULL) + { + <#= type_name #>_destroy(*<#= type_name.ToLower() #>_handle); + result = __FAILURE__; + } + else + { + uint32_t list_item_count; + if (amqpvalue_get_list_item_count(list_value, &list_item_count) != 0) + { + result = __FAILURE__; + } + else + { + do + { +<# int k = 0; #> +<# bool first_one = true; #> +<# foreach (field field in type.Items.Where(item => item is field)) #> +<# { #> +<# string field_name = field.name.ToLower().Replace('-', '_'); #> +<# string c_type = Program.GetCType(field.type, false).Replace('-', '_').Replace(':', '_'); #> +<# type field_type = Program.GetTypeByName(field.type); #> +<# if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; #> +<# if (first_one) #> +<# { #> +<# first_one = false; #> + AMQP_VALUE item_value; +<# } #> + /* <#= field.name #> */ + if (list_item_count > <#= k #>) + { + item_value = amqpvalue_get_list_item(list_value, <#= k #>); + if (item_value == NULL) + { +<# if (field.mandatory == "true") #> +<# { #> + { + <#= type_name #>_destroy(*<#= type_name.ToLower() #>_handle); + result = __FAILURE__; + break; + } +<# } #> +<# else #> +<# { #> + /* do nothing */ +<# } #> + } + else + { +<# if (field.type != "*") #> +<# { #> + if (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL) + { +<# if (field.mandatory == "true") #> +<# { #> + amqpvalue_destroy(item_value); + <#= type_name #>_destroy(*<#= type_name.ToLower() #>_handle); + result = __FAILURE__; + break; +<# } #> +<# else #> +<# { #> + /* no error, field is not mandatory */ +<# } #> + } + else + { +<# if (field.multiple != "true") #> +<# { #> + <#= c_type #> <#= field_name #>; + if (amqpvalue_get_<#= field.type.ToLower().Replace('-', '_').Replace(':', '_') #>(item_value, &<#= field_name #>) != 0) +<# } #> +<# else #> +<# { #> + <#= c_type #> <#= field_name #> = NULL; + AMQP_VALUE <#= field_name #>_array; + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, &<#= field_name #>_array) != 0)) && + (amqpvalue_get_<#= field.type.ToLower().Replace('-', '_').Replace(':', '_') #>(item_value, &<#= field_name #>) != 0)) +<# } #> + { + amqpvalue_destroy(item_value); + <#= type_name #>_destroy(*<#= type_name.ToLower() #>_handle); + result = __FAILURE__; + break; + } +<# if (c_type == "ERROR_HANDLE") #> +<# { #> + else + { + error_destroy(<#= field_name.ToLower() #>); + } +<# } #> + } + +<# } #> +<# else #> +<# { #> +<# } #> + amqpvalue_destroy(item_value); + } + } +<# if (field.mandatory == "true") #> +<# { #> + else + { + result = __FAILURE__; + } +<# } #> +<# k++; #> +<# } #> + + <#= type_name.ToLower() #>_instance->composite_value = amqpvalue_clone(value); + + result = 0; + } while((void)0,0); + } + } + } + + return result; +} + +<# int j = 0; #> +<# foreach (field field in type.Items.Where(item => item is field)) #> +<# { #> +<# string field_name = field.name.ToLower().Replace('-', '_'); #> +<# string c_type = Program.GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_'); #> +<# string single_value_c_type = Program.GetCType(field.type, false).Replace('-', '_').Replace(':', '_'); #> +<# type field_type = Program.GetTypeByName(field.type); #> +<# if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; #> +int <#= type_name #>_get_<#= field_name #>(<#= type_name.ToUpper() #>_HANDLE <#= type_name #>, <#= c_type #>* <#= field_name #>_value) +{ + int result; + + if (<#= type_name #> == NULL) + { + result = __FAILURE__; + } + else + { + uint32_t item_count; + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)<#= type_name #>; + if (amqpvalue_get_composite_item_count(<#= type_name #>_instance->composite_value, &item_count) != 0) + { + result = __FAILURE__; + } + else + { + if (item_count <= <#= j #>) + { +<# if (field.@default != null) #> +<# { #> +<# if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) #> +<# { #> + *<#= field_name #>_value = <#= field_type.@name.Replace('-', '_').Replace(':', '_') #>_<#= field.@default.Replace('-', '_').Replace(':', '_') #>; +<# } #> +<# else #> +<# { #> + *<#= field_name #>_value = <#= field.@default #>; +<# } #> + result = 0; +<# } #> +<# else #> +<# { #> + result = __FAILURE__; +<# } #> + } + else + { + AMQP_VALUE item_value = amqpvalue_get_composite_item_in_place(<#= type_name #>_instance->composite_value, <#= j #>); + if ((item_value == NULL) || + (amqpvalue_get_type(item_value) == AMQP_TYPE_NULL)) + { +<# if (field.@default != null) #> +<# { #> +<# if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) #> +<# { #> + *<#= field_name #>_value = <#= field_type.@name.Replace('-', '_').Replace(':', '_') #>_<#= field.@default.Replace('-', '_').Replace(':', '_') #>; +<# } #> +<# else #> +<# { #> + *<#= field_name #>_value = <#= field.@default #>; +<# } #> + result = 0; +<# } #> +<# else #> +<# { #> + result = __FAILURE__; +<# } #> + } + else + { +<# if (field.multiple == "true") #> +<# { #> + <#= single_value_c_type #> <#= field_name #>_single_value; +<# } #> +<# if (field.type.Replace('-', '_').Replace(':', '_') == "*") #> +<# { #> + *<#= field_name #>_value = item_value; + result = 0; +<# } #> +<# else #> +<# { #> +<# if (field.multiple != "true") #> +<# { #> + int get_single_value_result = amqpvalue_get_<#= field.type.Replace('-', '_').Replace(':', '_') #>(item_value, <#= field_name #>_value); + if (get_single_value_result != 0) +<# } #> +<# else #> +<# { #> + int get_single_value_result; + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + get_single_value_result = amqpvalue_get_<#= field.type.ToLower().Replace('-', '_').Replace(':', '_') #>(item_value, &<#= field_name #>_single_value); + } + else + { + (void)memset((void*)&<#= field_name #>_single_value, 0, sizeof(<#= field_name #>_single_value)); + get_single_value_result = 1; + } + + if (((amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) || (amqpvalue_get_array(item_value, <#= field_name #>_value) != 0)) && + (get_single_value_result != 0)) +<# } #> + { +<# if (field.@default != null) #> +<# { #> + if (amqpvalue_get_type(item_value) != AMQP_TYPE_NULL) + { + result = __FAILURE__; + } + else + { +<# if ((field_type != null) && (field_type.@class == typeClass.restricted) && (field_type.Items != null)) #> +<# { #> + *<#= field_name #>_value = <#= field_type.@name.Replace('-', '_').Replace(':', '_') #>_<#= field.@default.Replace('-', '_').Replace(':', '_') #>; +<# } #> +<# else #> +<# { #> + *<#= field_name #>_value = <#= field.@default #>; +<# } #> + result = 0; + } +<# } #> +<# else #> +<# { #> + result = __FAILURE__; +<# } #> + } + else + { +<# if (field.multiple == "true") #> +<# { #> + if (amqpvalue_get_type(item_value) != AMQP_TYPE_ARRAY) + { + *<#= field_name #>_value = amqpvalue_create_array(); + if (*<#= field_name #>_value == NULL) + { + result = __FAILURE__; + } + else + { + AMQP_VALUE single_amqp_value = amqpvalue_create_<#= field.type.ToLower().Replace('-', '_').Replace(':', '_') #>(<#= field_name #>_single_value); + if (single_amqp_value == NULL) + { + amqpvalue_destroy(*<#= field_name #>_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_add_array_item(*<#= field_name #>_value, single_amqp_value) != 0) + { + amqpvalue_destroy(*<#= field_name #>_value); + amqpvalue_destroy(single_amqp_value); + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(<#= type_name #>_instance->composite_value, <#= j #>, *<#= field_name #>_value) != 0) + { + amqpvalue_destroy(*<#= field_name #>_value); + result = __FAILURE__; + } + else + { + result = 0; + } + } + + amqpvalue_destroy(single_amqp_value); + } + amqpvalue_destroy(*<#= field_name #>_value); + } + } + else + { + result = 0; + } +<# } #> +<# else #> +<# { #> + result = 0; +<# } #> + } +<# } #> + } + } + } + } + + return result; +} + +int <#= type_name #>_set_<#= field_name #>(<#= type_name.ToUpper() #>_HANDLE <#= type_name #>, <#= c_type #> <#= field_name #>_value) +{ + int result; + + if (<#= type_name #> == NULL) + { + result = __FAILURE__; + } + else + { + <#= type_name.ToUpper() #>_INSTANCE* <#= type_name #>_instance = (<#= type_name.ToUpper() #>_INSTANCE*)<#= type_name #>; +<# if (c_type != "AMQP_VALUE") #> +<# { #> + AMQP_VALUE <#= field_name #>_amqp_value = amqpvalue_create_<#= field.type.ToLower().Replace('-', '_').Replace(':', '_') #>(<#= field_name #>_value); +<# } #> +<# else #> +<# { #> + AMQP_VALUE <#= field_name #>_amqp_value; + if (<#= field_name #>_value == NULL) + { + <#= field_name #>_amqp_value = NULL; + } + else + { + <#= field_name #>_amqp_value = amqpvalue_clone(<#= field_name #>_value); + } +<# } #> + if (<#= field_name #>_amqp_value == NULL) + { + result = __FAILURE__; + } + else + { + if (amqpvalue_set_composite_item(<#= type_name #>_instance->composite_value, <#= j #>, <#= field_name #>_amqp_value) != 0) + { + result = __FAILURE__; + } + else + { + result = 0; + } + + amqpvalue_destroy(<#= field_name #>_amqp_value); + } + } + + return result; +} + +<# j++; #> +<# } #> + +<# } #> +<# else if (type.@class == typeClass.restricted) #> +<# { #> +<# string c_type = Program.GetCType(type.source, false).Replace('-', '_').Replace(':', '_'); #> +<# bool hasDescriptor = (type.Items != null) && (type.Items.Where(item => item is descriptor).Count() > 0); #> +/* <#= type.name #> */ + +<# if (c_type != "AMQP_VALUE") #> +<# { #> +<# if (!hasDescriptor) #> +<# { #> +AMQP_VALUE amqpvalue_create_<#= type_name.ToLower() #>(<#= type_name.ToLower() #> value) +{ + return amqpvalue_create_<#= type.source.ToLower().Replace('-', '_').Replace(':', '_') #>(value); +} +<# } #> +<# else #> +<# { #> +AMQP_VALUE amqpvalue_create_<#= type_name.ToLower() #>(<#= type_name.ToLower() #> value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_create_<#= type.source.ToLower().Replace('-', '_').Replace(':', '_') #>(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(<#= Program.GetDescriptorCode(Program.GetDescriptor(type)) #>); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_<#= type_name.ToLower() #>_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == <#= Program.GetDescriptorCode(Program.GetDescriptor(type)).ToString() #>)) + { + result = true; + } + else + { + result = false; + } + + return result; +} +<# } #> +<# } #> +<# else #> +<# { #> +<# if (!hasDescriptor) #> +<# { #> +AMQP_VALUE amqpvalue_create_<#= type_name.ToLower() #>(AMQP_VALUE value) +{ + return amqpvalue_clone(value); +} +<# } #> +<# else #> +<# { #> +AMQP_VALUE amqpvalue_create_<#= type_name.ToLower() #>(AMQP_VALUE value) +{ + AMQP_VALUE result; + AMQP_VALUE described_value = amqpvalue_clone(value); + if (described_value == NULL) + { + result = NULL; + } + else + { + AMQP_VALUE descriptor = amqpvalue_create_ulong(<#= Program.GetDescriptorCode(Program.GetDescriptor(type)) #>); + if (descriptor == NULL) + { + result = NULL; + } + else + { + result = amqpvalue_create_described(amqpvalue_clone(descriptor), amqpvalue_clone(described_value)); + + amqpvalue_destroy(descriptor); + } + + amqpvalue_destroy(described_value); + } + + return result; +} + +bool is_<#= type_name.ToLower() #>_type_by_descriptor(AMQP_VALUE descriptor) +{ + bool result; + + uint64_t descriptor_ulong; + if ((amqpvalue_get_ulong(descriptor, &descriptor_ulong) == 0) && + (descriptor_ulong == <#= Program.GetDescriptorCode(Program.GetDescriptor(type)).ToString() #>)) + { + result = true; + } + else + { + result = false; + } + + return result; +} +<# } #> +<# } #> + +<# } #> +<# } #> +<# } #>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_h.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,402 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version: 15.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ +namespace amqplib_generator +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using amqplib_generator; + using System; + + /// <summary> + /// Class to produce the template output + /// </summary> + + #line 1 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public partial class amqp_definitions_h : amqp_definitions_hBase + { +#line hidden + /// <summary> + /// Create the template output + /// </summary> + public virtual string TransformText() + { + this.Write("\r\n"); + + #line 8 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + amqp amqp = Program.LoadAMQPTypes(); + + #line default + #line hidden + this.Write(@" +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_H +#define AMQP_DEFINITIONS_H + +#ifdef __cplusplus +#include <cstdint> +extern ""C"" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include ""azure_uamqp_c/amqpvalue.h"" +#include ""azure_c_shared_utility/umock_c_prod.h"" + +"); + + #line 30 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + foreach (section section in amqp.Items.Where(item => item is section)) + + #line default + #line hidden + + #line 31 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + { + + #line default + #line hidden + + #line 32 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + List<type> types = new List<type>(); + + #line default + #line hidden + + #line 33 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + types.AddRange(section.Items.Where(item => item is type).Cast<type>()); + + #line default + #line hidden + + #line 34 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + foreach (type type in types) + + #line default + #line hidden + + #line 35 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + { + + #line default + #line hidden + + #line 36 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + string type_name = type.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + this.Write("#include \""); + + #line 37 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture($"azure_uamqp_c/amqp_definitions_{type_name}.h")); + + #line default + #line hidden + this.Write("\"\r\n"); + + #line 38 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + } + + #line default + #line hidden + + #line 39 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_h.tt" + } + + #line default + #line hidden + this.Write("\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif /* AMQP_DEFINITIONS_H */\r\n"); + return this.GenerationEnvironment.ToString(); + } + } + + #line default + #line hidden + #region Base class + /// <summary> + /// Base class for this transformation + /// </summary> + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public class amqp_definitions_hBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List<int> indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary<string, object> sessionField; + #endregion + #region Properties + /// <summary> + /// The string builder that generation-time code is using to assemble generated output + /// </summary> + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// <summary> + /// The error collection for the generation process + /// </summary> + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// <summary> + /// A list of the lengths of each indent that was added with PushIndent + /// </summary> + private System.Collections.Generic.List<int> indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List<int>(); + } + return this.indentLengthsField; + } + } + /// <summary> + /// Gets the current indent we use when adding lines to the output + /// </summary> + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// <summary> + /// Current transformation session + /// </summary> + public virtual global::System.Collections.Generic.IDictionary<string, object> Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Raise an error + /// </summary> + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// <summary> + /// Raise a warning + /// </summary> + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// <summary> + /// Increase the indent + /// </summary> + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// <summary> + /// Remove the last indent that was added with PushIndent + /// </summary> + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// <summary> + /// Remove any indentation + /// </summary> + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// <summary> + /// Utility class to produce culture-oriented representation of an object as a string. + /// </summary> + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// <summary> + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// </summary> + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// <summary> + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// </summary> + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// <summary> + /// Helper to produce culture-oriented representation of an object as a string + /// </summary> + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_h.tt Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,45 @@ +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="amqplib_generator" #> + +<# amqp amqp = Program.LoadAMQPTypes(); #> + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +#ifndef AMQP_DEFINITIONS_H +#define AMQP_DEFINITIONS_H + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +<# foreach (section section in amqp.Items.Where(item => item is section)) #> +<# { #> +<# List<type> types = new List<type>(); #> +<# types.AddRange(section.Items.Where(item => item is type).Cast<type>()); #> +<# foreach (type type in types) #> +<# { #> +<# string type_name = type.name.ToLower().Replace('-', '_'); #> +#include "<#=$"azure_uamqp_c/amqp_definitions_{type_name}.h" #>" +<# } #> +<# } #> + +#ifdef __cplusplus +} +#endif + +#endif /* AMQP_DEFINITIONS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_type_h.cs Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,926 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version: 15.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ +namespace amqplib_generator +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using amqplib_generator; + using System; + + /// <summary> + /// Class to produce the template output + /// </summary> + + #line 1 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public partial class amqp_definitions_type_h : amqp_definitions_type_hBase + { +#line hidden + /// <summary> + /// Create the template output + /// </summary> + public virtual string TransformText() + { + this.Write("\r\n"); + + #line 8 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + amqp amqp = Program.LoadAMQPTypes(); + + #line default + #line hidden + this.Write(@" +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +"); + + #line 16 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + type type = Program.CurrentTypeObject; + + #line default + #line hidden + + #line 17 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string type_name = type.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + + #line 18 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string amqpDefinitionName = $"AMQP_DEFINITIONS_{type_name.ToUpper()}_H"; + + #line default + #line hidden + this.Write("#ifndef "); + + #line 19 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(amqpDefinitionName)); + + #line default + #line hidden + this.Write("\r\n#define "); + + #line 20 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(amqpDefinitionName)); + + #line default + #line hidden + this.Write("\r\n\r\n\r\n#ifdef __cplusplus\r\n#include <cstdint>\r\nextern \"C\" {\r\n#else\r\n#include <stdi" + + "nt.h>\r\n#include <stdbool.h>\r\n#endif\r\n\r\n#include \"azure_uamqp_c/amqpvalue.h\"\r\n#in" + + "clude \"azure_c_shared_utility/umock_c_prod.h\"\r\n\r\n"); + + #line 34 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if (type.@class == typeClass.composite) + + #line default + #line hidden + + #line 35 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" typedef struct "); + + #line 36 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_INSTANCE_TAG* "); + + #line 36 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE;\r\n\r\n"); + + #line 38 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string arg_list = Program.GetMandatoryArgListMock(type); + + #line default + #line hidden + this.Write(" MOCKABLE_FUNCTION(, "); + + #line 39 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 39 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_create "); + + #line 39 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(arg_list)); + + #line default + #line hidden + this.Write(");\r\n MOCKABLE_FUNCTION(, "); + + #line 40 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 40 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_clone, "); + + #line 40 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, value);\r\n MOCKABLE_FUNCTION(, void, "); + + #line 41 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_destroy, "); + + #line 41 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 41 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(");\r\n MOCKABLE_FUNCTION(, bool, is_"); + + #line 42 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_type_by_descriptor, AMQP_VALUE, descriptor);\r\n MOCKABLE_FUNCTION(, int, amqpv" + + "alue_get_"); + + #line 43 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", AMQP_VALUE, value, "); + + #line 43 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE*, "); + + #line 43 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_handle);\r\n MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_"); + + #line 44 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", "); + + #line 44 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 44 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(");\r\n\r\n"); + + #line 46 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + foreach (field field in type.Items.Where(item => item is field)) + + #line default + #line hidden + + #line 47 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + + #line 48 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string field_name = field.name.ToLower().Replace('-', '_'); + + #line default + #line hidden + + #line 49 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string c_type = Program.GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + + #line 50 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + type field_type = Program.GetTypeByName(field.type); + + #line default + #line hidden + + #line 51 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; + + #line default + #line hidden + this.Write(" MOCKABLE_FUNCTION(, int, "); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_get_"); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(", "); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", "); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write("*, "); + + #line 52 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n MOCKABLE_FUNCTION(, int, "); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_set_"); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write(", "); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToUpper())); + + #line default + #line hidden + this.Write("_HANDLE, "); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write(", "); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write(", "); + + #line 53 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(field_name)); + + #line default + #line hidden + this.Write("_value);\r\n"); + + #line 54 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 56 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + + #line 57 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + else + + #line default + #line hidden + + #line 58 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if (type.@class == typeClass.restricted) + + #line default + #line hidden + + #line 59 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + + #line 60 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + string c_type = Program.GetCType(type.source, false).Replace('-', '_').Replace(':', '_'); + + #line default + #line hidden + this.Write("\r\n typedef "); + + #line 62 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(c_type)); + + #line default + #line hidden + this.Write(" "); + + #line 62 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(";\r\n\r\n"); + + #line 64 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if (c_type != "AMQP_VALUE") + + #line default + #line hidden + + #line 65 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_"); + + #line 66 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(", "); + + #line 66 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(", value);\r\n"); + + #line 67 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + + #line 68 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + else + + #line default + #line hidden + + #line 69 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_"); + + #line 70 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(", AMQP_VALUE, value);\r\n #define "); + + #line 71 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_clone amqpvalue_clone\r\n #define "); + + #line 72 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write("_destroy amqpvalue_destroy\r\n"); + + #line 73 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 75 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if ((type.Items != null) && (type.Items.Where(item => item is descriptor).Count() > 0)) + + #line default + #line hidden + + #line 76 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" MOCKABLE_FUNCTION(, bool, is_"); + + #line 77 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_type_by_descriptor, AMQP_VALUE, descriptor);\r\n"); + + #line 78 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + this.Write("\r\n #define amqpvalue_get_"); + + #line 80 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name.ToLower())); + + #line default + #line hidden + this.Write(" amqpvalue_get_"); + + #line 80 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type.source.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("\r\n\r\n"); + + #line 82 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if (type.Items != null) + + #line default + #line hidden + + #line 83 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + + #line 84 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + foreach (choice choice in type.Items.Where(item => item is choice)) + + #line default + #line hidden + + #line 85 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + + #line 86 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + if (type.@source == "symbol") + + #line default + #line hidden + + #line 87 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" #define "); + + #line 88 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_"); + + #line 88 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(choice.name.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write(" \""); + + #line 88 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(choice.value)); + + #line default + #line hidden + this.Write("\"\r\n"); + + #line 89 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + + #line 90 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + else + + #line default + #line hidden + + #line 91 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + { + + #line default + #line hidden + this.Write(" #define "); + + #line 92 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(type_name)); + + #line default + #line hidden + this.Write("_"); + + #line 92 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(choice.name.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write(" "); + + #line 92 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(choice.value.Replace('-', '_').Replace(':', '_'))); + + #line default + #line hidden + this.Write("\r\n"); + + #line 93 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + + #line 94 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + + #line 95 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + this.Write("\r\n"); + + #line 97 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + } + + #line default + #line hidden + this.Write("\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif /* "); + + #line 103 "G:\repos\azure-uamqp-c\uamqp_generator\amqp_definitions_type_h.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(amqpDefinitionName)); + + #line default + #line hidden + this.Write(" */\r\n"); + return this.GenerationEnvironment.ToString(); + } + } + + #line default + #line hidden + #region Base class + /// <summary> + /// Base class for this transformation + /// </summary> + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + public class amqp_definitions_type_hBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List<int> indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary<string, object> sessionField; + #endregion + #region Properties + /// <summary> + /// The string builder that generation-time code is using to assemble generated output + /// </summary> + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// <summary> + /// The error collection for the generation process + /// </summary> + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// <summary> + /// A list of the lengths of each indent that was added with PushIndent + /// </summary> + private System.Collections.Generic.List<int> indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List<int>(); + } + return this.indentLengthsField; + } + } + /// <summary> + /// Gets the current indent we use when adding lines to the output + /// </summary> + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// <summary> + /// Current transformation session + /// </summary> + public virtual global::System.Collections.Generic.IDictionary<string, object> Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// <summary> + /// Write text directly into the generated output + /// </summary> + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Write formatted text directly into the generated output + /// </summary> + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// <summary> + /// Raise an error + /// </summary> + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// <summary> + /// Raise a warning + /// </summary> + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// <summary> + /// Increase the indent + /// </summary> + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// <summary> + /// Remove the last indent that was added with PushIndent + /// </summary> + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// <summary> + /// Remove any indentation + /// </summary> + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// <summary> + /// Utility class to produce culture-oriented representation of an object as a string. + /// </summary> + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// <summary> + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// </summary> + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// <summary> + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// </summary> + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// <summary> + /// Helper to produce culture-oriented representation of an object as a string + /// </summary> + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/amqp_definitions_type_h.tt Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,103 @@ +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="amqplib_generator" #> + +<# amqp amqp = Program.LoadAMQPTypes(); #> + +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file is generated. DO NOT EDIT it manually. +// The generator that produces it is located at /uamqp_generator/uamqp_generator.sln + +<# type type = Program.CurrentTypeObject; #> +<# string type_name = type.name.ToLower().Replace('-', '_'); #> +<# string amqpDefinitionName = $"AMQP_DEFINITIONS_{type_name.ToUpper()}_H"; #> +#ifndef <#=amqpDefinitionName#> +#define <#=amqpDefinitionName#> + + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#endif + +#include "azure_uamqp_c/amqpvalue.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +<# if (type.@class == typeClass.composite) #> +<# { #> + typedef struct <#= type_name.ToUpper() #>_INSTANCE_TAG* <#= type_name.ToUpper() #>_HANDLE; + +<# string arg_list = Program.GetMandatoryArgListMock(type); #> + MOCKABLE_FUNCTION(, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>_create <#= arg_list #>); + MOCKABLE_FUNCTION(, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>_clone, <#= type_name.ToUpper() #>_HANDLE, value); + MOCKABLE_FUNCTION(, void, <#= type_name #>_destroy, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>); + MOCKABLE_FUNCTION(, bool, is_<#= type_name #>_type_by_descriptor, AMQP_VALUE, descriptor); + MOCKABLE_FUNCTION(, int, amqpvalue_get_<#= type_name #>, AMQP_VALUE, value, <#= type_name.ToUpper() #>_HANDLE*, <#= type_name.ToUpper() #>_handle); + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_<#= type_name #>, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>); + +<# foreach (field field in type.Items.Where(item => item is field)) #> +<# { #> +<# string field_name = field.name.ToLower().Replace('-', '_'); #> +<# string c_type = Program.GetCType(field.type, field.multiple == "true").Replace('-', '_').Replace(':', '_'); #> +<# type field_type = Program.GetTypeByName(field.type); #> +<# if ((field_type != null) && (field_type.@class == typeClass.composite)) c_type = field_type.name.ToUpper().Replace('-', '_').Replace(':', '_') + "_HANDLE"; #> + MOCKABLE_FUNCTION(, int, <#= type_name #>_get_<#= field_name #>, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>, <#= c_type #>*, <#= field_name #>_value); + MOCKABLE_FUNCTION(, int, <#= type_name #>_set_<#= field_name #>, <#= type_name.ToUpper() #>_HANDLE, <#= type_name #>, <#= c_type #>, <#= field_name #>_value); +<# } #> + +<# } #> +<# else #> +<# if (type.@class == typeClass.restricted) #> +<# { #> +<# string c_type = Program.GetCType(type.source, false).Replace('-', '_').Replace(':', '_'); #> + + typedef <#= c_type #> <#= type_name.ToLower() #>; + +<# if (c_type != "AMQP_VALUE") #> +<# { #> + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_<#= type_name.ToLower() #>, <#= type_name.ToLower() #>, value); +<# } #> +<# else #> +<# { #> + MOCKABLE_FUNCTION(, AMQP_VALUE, amqpvalue_create_<#= type_name.ToLower() #>, AMQP_VALUE, value); + #define <#= type_name.ToLower() #>_clone amqpvalue_clone + #define <#= type_name.ToLower() #>_destroy amqpvalue_destroy +<# } #> + +<# if ((type.Items != null) && (type.Items.Where(item => item is descriptor).Count() > 0)) #> +<# { #> + MOCKABLE_FUNCTION(, bool, is_<#= type_name #>_type_by_descriptor, AMQP_VALUE, descriptor); +<# } #> + + #define amqpvalue_get_<#= type_name.ToLower() #> amqpvalue_get_<#= type.source.Replace('-', '_').Replace(':', '_') #> + +<# if (type.Items != null) #> +<# { #> +<# foreach (choice choice in type.Items.Where(item => item is choice)) #> +<# { #> +<# if (type.@source == "symbol") #> +<# { #> + #define <#= type_name #>_<#= choice.name.Replace('-', '_').Replace(':', '_') #> "<#= choice.value #>" +<# } #> +<# else #> +<# { #> + #define <#= type_name #>_<#= choice.name.Replace('-', '_').Replace(':', '_') #> <#= choice.value.Replace('-', '_').Replace(':', '_') #> +<# } #> +<# } #> +<# } #> + +<# } #> + +#ifdef __cplusplus +} +#endif + +#endif /* <#=amqpDefinitionName#> */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/uamqp_generator.csproj Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{53D12D47-7F69-47C5-A45B-5DF5424955D7}</ProjectGuid> + <OutputType>Exe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>amqplib_generator</RootNamespace> + <AssemblyName>amqplib_generator</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>x64</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="amqp_definitions.cs" /> + <Compile Include="amqp_definitions_c.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>amqp_definitions_c.tt</DependentUpon> + </Compile> + <Compile Include="amqp_definitions_h.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>amqp_definitions_h.tt</DependentUpon> + </Compile> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="amqp_definitions_type_h.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>amqp_definitions_type_h.tt</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="amqp_definitions.xsd"> + <SubType>Designer</SubType> + </None> + <None Include="amqp_definitions_c.tt"> + <Generator>TextTemplatingFilePreprocessor</Generator> + <LastGenOutput>amqp_definitions_c.cs</LastGenOutput> + </None> + <None Include="App.config" /> + <None Include="amqp_definitions_type_h.tt"> + <Generator>TextTemplatingFilePreprocessor</Generator> + <LastGenOutput>amqp_definitions_type_h.cs</LastGenOutput> + </None> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <ItemGroup> + <Content Include="amqp_definitions.xml" /> + <Content Include="amqp_definitions_h.tt"> + <Generator>TextTemplatingFilePreprocessor</Generator> + <LastGenOutput>amqp_definitions_h.cs</LastGenOutput> + </Content> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uamqp/uamqp_generator/uamqp_generator.sln Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uamqp_generator", "uamqp_generator.csproj", "{53D12D47-7F69-47C5-A45B-5DF5424955D7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Debug|x64.ActiveCfg = Debug|x64 + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Debug|x64.Build.0 = Debug|x64 + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Release|Any CPU.Build.0 = Release|Any CPU + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Release|x64.ActiveCfg = Release|x64 + {53D12D47-7F69-47C5-A45B-5DF5424955D7}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/inc/azure_umqtt_c/mqtt_client.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MQTT_CLIENT_H +#define MQTT_CLIENT_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_umqtt_c/mqttconst.h" +#include "azure_umqtt_c/mqtt_message.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstdint> +extern "C" { +#else +#include <stdint.h> +#endif // __cplusplus + + +typedef struct MQTT_CLIENT_TAG* MQTT_CLIENT_HANDLE; + +#define MQTT_CLIENT_EVENT_VALUES \ + MQTT_CLIENT_ON_CONNACK, \ + MQTT_CLIENT_ON_PUBLISH_ACK, \ + MQTT_CLIENT_ON_PUBLISH_RECV, \ + MQTT_CLIENT_ON_PUBLISH_REL, \ + MQTT_CLIENT_ON_PUBLISH_COMP, \ + MQTT_CLIENT_ON_SUBSCRIBE_ACK, \ + MQTT_CLIENT_ON_UNSUBSCRIBE_ACK, \ + MQTT_CLIENT_ON_PING_RESPONSE, \ + MQTT_CLIENT_ON_DISCONNECT + +DEFINE_ENUM(MQTT_CLIENT_EVENT_RESULT, MQTT_CLIENT_EVENT_VALUES); + +#define MQTT_CLIENT_EVENT_ERROR_VALUES \ + MQTT_CLIENT_CONNECTION_ERROR, \ + MQTT_CLIENT_PARSE_ERROR, \ + MQTT_CLIENT_MEMORY_ERROR, \ + MQTT_CLIENT_COMMUNICATION_ERROR, \ + MQTT_CLIENT_NO_PING_RESPONSE, \ + MQTT_CLIENT_UNKNOWN_ERROR + +DEFINE_ENUM(MQTT_CLIENT_EVENT_ERROR, MQTT_CLIENT_EVENT_ERROR_VALUES); + +typedef void(*ON_MQTT_OPERATION_CALLBACK)(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_RESULT actionResult, const void* msgInfo, void* callbackCtx); +typedef void(*ON_MQTT_ERROR_CALLBACK)(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_ERROR error, void* callbackCtx); +typedef void(*ON_MQTT_MESSAGE_RECV_CALLBACK)(MQTT_MESSAGE_HANDLE msgHandle, void* callbackCtx); +typedef void(*ON_MQTT_DISCONNECTED_CALLBACK)(void* callbackCtx); + +MOCKABLE_FUNCTION(, MQTT_CLIENT_HANDLE, mqtt_client_init, ON_MQTT_MESSAGE_RECV_CALLBACK, msgRecv, ON_MQTT_OPERATION_CALLBACK, opCallback, void*, opCallbackCtx, ON_MQTT_ERROR_CALLBACK, onErrorCallBack, void*, errorCBCtx); +MOCKABLE_FUNCTION(, void, mqtt_client_deinit, MQTT_CLIENT_HANDLE, handle); + +MOCKABLE_FUNCTION(, int, mqtt_client_connect, MQTT_CLIENT_HANDLE, handle, XIO_HANDLE, xioHandle, MQTT_CLIENT_OPTIONS*, mqttOptions); +MOCKABLE_FUNCTION(, int, mqtt_client_disconnect, MQTT_CLIENT_HANDLE, handle, ON_MQTT_DISCONNECTED_CALLBACK, callback, void*, ctx); + +MOCKABLE_FUNCTION(, int, mqtt_client_subscribe, MQTT_CLIENT_HANDLE, handle, uint16_t, packetId, SUBSCRIBE_PAYLOAD*, subscribeList, size_t, count); +MOCKABLE_FUNCTION(, int, mqtt_client_unsubscribe, MQTT_CLIENT_HANDLE, handle, uint16_t, packetId, const char**, unsubscribeList, size_t, count); + +MOCKABLE_FUNCTION(, int, mqtt_client_publish, MQTT_CLIENT_HANDLE, handle, MQTT_MESSAGE_HANDLE, msgHandle); + +MOCKABLE_FUNCTION(, void, mqtt_client_dowork, MQTT_CLIENT_HANDLE, handle); + +MOCKABLE_FUNCTION(, void, mqtt_client_set_trace, MQTT_CLIENT_HANDLE, handle, bool, traceOn, bool, rawBytesOn); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // MQTTCLIENT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/inc/azure_umqtt_c/mqtt_codec.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MQTT_CODEC_H +#define MQTT_CODEC_H + +#include "azure_c_shared_utility/xio.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/umock_c_prod.h" +#include "azure_umqtt_c/mqttconst.h" +#include "azure_c_shared_utility/strings.h" + +#ifdef __cplusplus +#include <cstdint> +#include <cstddef> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#endif // __cplusplus + +typedef struct MQTTCODEC_INSTANCE_TAG* MQTTCODEC_HANDLE; + +typedef void(*ON_PACKET_COMPLETE_CALLBACK)(void* context, CONTROL_PACKET_TYPE packet, int flags, BUFFER_HANDLE headerData); + +MOCKABLE_FUNCTION(, MQTTCODEC_HANDLE, mqtt_codec_create, ON_PACKET_COMPLETE_CALLBACK, packetComplete, void*, callbackCtx); +MOCKABLE_FUNCTION(, void, mqtt_codec_destroy, MQTTCODEC_HANDLE, handle); + +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_connect, const MQTT_CLIENT_OPTIONS*, mqttOptions, STRING_HANDLE, trace_log); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_disconnect); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_publish, QOS_VALUE, qosValue, bool, duplicateMsg, bool, serverRetain, uint16_t, packetId, const char*, topicName, const uint8_t*, msgBuffer, size_t, buffLen, STRING_HANDLE, trace_log); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_publishAck, uint16_t, packetId); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_publishReceived, uint16_t, packetId); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_publishRelease, uint16_t, packetId); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_publishComplete, uint16_t, packetId); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_ping); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_subscribe, uint16_t, packetId, SUBSCRIBE_PAYLOAD*, subscribeList, size_t, count, STRING_HANDLE, trace_log); +MOCKABLE_FUNCTION(, BUFFER_HANDLE, mqtt_codec_unsubscribe, uint16_t, packetId, const char**, unsubscribeList, size_t, count, STRING_HANDLE, trace_log); + +MOCKABLE_FUNCTION(, int, mqtt_codec_bytesReceived, MQTTCODEC_HANDLE, handle, const unsigned char*, buffer, size_t, size); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // MQTT_CODEC_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/inc/azure_umqtt_c/mqtt_message.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MQTT_MESSAGE_H +#define MQTT_MESSAGE_H + +#include "azure_umqtt_c/mqttconst.h" +#include "azure_c_shared_utility/umock_c_prod.h" + +#ifdef __cplusplus +#include <cstdint> +#include <cstddef> +extern "C" { +#else +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#endif // __cplusplus + +typedef struct MQTT_MESSAGE_TAG* MQTT_MESSAGE_HANDLE; + +MOCKABLE_FUNCTION(, MQTT_MESSAGE_HANDLE, mqttmessage_create_in_place, uint16_t, packetId, const char*, topicName, QOS_VALUE, qosValue, const uint8_t*, appMsg, size_t, appMsgLength); +MOCKABLE_FUNCTION(, MQTT_MESSAGE_HANDLE, mqttmessage_create, uint16_t, packetId, const char*, topicName, QOS_VALUE, qosValue, const uint8_t*, appMsg, size_t, appMsgLength); +MOCKABLE_FUNCTION(,void, mqttmessage_destroy, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(,MQTT_MESSAGE_HANDLE, mqttmessage_clone, MQTT_MESSAGE_HANDLE, handle); + +MOCKABLE_FUNCTION(, uint16_t, mqttmessage_getPacketId, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(, const char*, mqttmessage_getTopicName, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(, QOS_VALUE, mqttmessage_getQosType, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(, bool, mqttmessage_getIsDuplicateMsg, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(, bool, mqttmessage_getIsRetained, MQTT_MESSAGE_HANDLE, handle); +MOCKABLE_FUNCTION(, int, mqttmessage_setIsDuplicateMsg, MQTT_MESSAGE_HANDLE, handle, bool, duplicateMsg); +MOCKABLE_FUNCTION(, int, mqttmessage_setIsRetained, MQTT_MESSAGE_HANDLE, handle, bool, retainMsg); +MOCKABLE_FUNCTION(, const APP_PAYLOAD*, mqttmessage_getApplicationMsg, MQTT_MESSAGE_HANDLE, handle); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // MQTT_MESSAGE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/inc/azure_umqtt_c/mqttconst.h Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MQTTCONST_H +#define MQTTCONST_H + +#include "azure_c_shared_utility/crt_abstractions.h" + +#ifdef __cplusplus +#include <cstddef> +#include <cstdint> +extern "C" { +#else +#include <stddef.h> +#include <stdint.h> +#endif /* __cplusplus */ + +#define CONTROL_PACKET_TYPE_VALUES \ + CONNECT_TYPE = 0x10, \ + CONNACK_TYPE = 0x20, \ + PUBLISH_TYPE = 0x30, \ + PUBACK_TYPE = 0x40, \ + PUBREC_TYPE = 0x50, \ + PUBREL_TYPE = 0x60, \ + PUBCOMP_TYPE = 0x70, \ + SUBSCRIBE_TYPE = 0x80, \ + SUBACK_TYPE = 0x90, \ + UNSUBSCRIBE_TYPE = 0xA0, \ + UNSUBACK_TYPE = 0xB0, \ + PINGREQ_TYPE = 0xC0, \ + PINGRESP_TYPE = 0xD0, \ + DISCONNECT_TYPE = 0xE0, \ + PACKET_TYPE_ERROR, \ + UNKNOWN_TYPE + +DEFINE_ENUM(CONTROL_PACKET_TYPE, CONTROL_PACKET_TYPE_VALUES) + +#define QOS_VALUE_VALUES \ + DELIVER_AT_MOST_ONCE = 0x00, \ + DELIVER_AT_LEAST_ONCE = 0x01, \ + DELIVER_EXACTLY_ONCE = 0x02, \ + DELIVER_FAILURE = 0x80 + +DEFINE_ENUM(QOS_VALUE, QOS_VALUE_VALUES) + +typedef struct APP_PAYLOAD_TAG +{ + uint8_t* message; + size_t length; +} APP_PAYLOAD; + +typedef struct MQTT_CLIENT_OPTIONS_TAG +{ + char* clientId; + char* willTopic; + char* willMessage; + char* username; + char* password; + uint16_t keepAliveInterval; + bool messageRetain; + bool useCleanSession; + QOS_VALUE qualityOfServiceValue; + bool log_trace; +} MQTT_CLIENT_OPTIONS; + +typedef enum CONNECT_RETURN_CODE_TAG +{ + CONNECTION_ACCEPTED = 0x00, + CONN_REFUSED_UNACCEPTABLE_VERSION = 0x01, + CONN_REFUSED_ID_REJECTED = 0x02, + CONN_REFUSED_SERVER_UNAVAIL = 0x03, + CONN_REFUSED_BAD_USERNAME_PASSWORD = 0x04, + CONN_REFUSED_NOT_AUTHORIZED = 0x05, + CONN_REFUSED_UNKNOWN +} CONNECT_RETURN_CODE; + +typedef struct CONNECT_ACK_TAG +{ + bool isSessionPresent; + CONNECT_RETURN_CODE returnCode; +} CONNECT_ACK; + +typedef struct SUBSCRIBE_PAYLOAD_TAG +{ + const char* subscribeTopic; + QOS_VALUE qosReturn; +} SUBSCRIBE_PAYLOAD; + +typedef struct SUBSCRIBE_ACK_TAG +{ + uint16_t packetId; + QOS_VALUE* qosReturn; + size_t qosCount; +} SUBSCRIBE_ACK; + +typedef struct UNSUBSCRIBE_ACK_TAG +{ + uint16_t packetId; +} UNSUBSCRIBE_ACK; + +typedef struct PUBLISH_ACK_TAG +{ + uint16_t packetId; +} PUBLISH_ACK; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // MQTTCONST_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/src/mqtt_client.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1289 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/platform.h" +#include "azure_c_shared_utility/tickcounter.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/threadapi.h" + +#include "azure_umqtt_c/mqtt_client.h" +#include "azure_umqtt_c/mqtt_codec.h" +#include <inttypes.h> + +#define VARIABLE_HEADER_OFFSET 2 +#define RETAIN_FLAG_MASK 0x1 +#define QOS_LEAST_ONCE_FLAG_MASK 0x2 +#define QOS_EXACTLY_ONCE_FLAG_MASK 0x4 +#define DUPLICATE_FLAG_MASK 0x8 +#define CONNECT_PACKET_MASK 0xf0 +#define TIME_MAX_BUFFER 16 +#define DEFAULT_MAX_PING_RESPONSE_TIME 80 // % of time to send pings +#define MAX_CLOSE_RETRIES 2 + +static const char* const TRUE_CONST = "true"; +static const char* const FALSE_CONST = "false"; + +DEFINE_ENUM_STRINGS(QOS_VALUE, QOS_VALUE_VALUES); + +typedef struct MQTT_CLIENT_TAG +{ + XIO_HANDLE xioHandle; + MQTTCODEC_HANDLE codec_handle; + CONTROL_PACKET_TYPE packetState; + TICK_COUNTER_HANDLE packetTickCntr; + tickcounter_ms_t packetSendTimeMs; + ON_MQTT_OPERATION_CALLBACK fnOperationCallback; + ON_MQTT_MESSAGE_RECV_CALLBACK fnMessageRecv; + void* ctx; + ON_MQTT_ERROR_CALLBACK fnOnErrorCallBack; + void* errorCBCtx; + ON_MQTT_DISCONNECTED_CALLBACK disconnect_cb; + void* disconnect_ctx; + QOS_VALUE qosValue; + uint16_t keepAliveInterval; + MQTT_CLIENT_OPTIONS mqttOptions; + bool clientConnected; + bool socketConnected; + bool logTrace; + bool rawBytesTrace; + tickcounter_ms_t timeSincePing; + uint16_t maxPingRespTime; +} MQTT_CLIENT; + +static void on_connection_closed(void* context) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if (mqtt_client != NULL) + { + mqtt_client->socketConnected = false; + mqtt_client->clientConnected = false; + if (mqtt_client->disconnect_cb) + { + mqtt_client->disconnect_cb(mqtt_client->disconnect_ctx); + } + } +} + +static void close_connection(MQTT_CLIENT* mqtt_client) +{ + if (mqtt_client->socketConnected) + { + (void)xio_close(mqtt_client->xioHandle, on_connection_closed, mqtt_client); + if (mqtt_client->disconnect_cb == NULL) + { + size_t counter = 0; + do + { + xio_dowork(mqtt_client->xioHandle); + counter++; + ThreadAPI_Sleep(2); + } while (mqtt_client->clientConnected && counter < MAX_CLOSE_RETRIES); + } + } + else + { + if (mqtt_client->disconnect_cb) + { + mqtt_client->disconnect_cb(mqtt_client->disconnect_ctx); + } + } + mqtt_client->xioHandle = NULL; +} + +static void set_error_callback(MQTT_CLIENT* mqtt_client, MQTT_CLIENT_EVENT_ERROR error_type) +{ + if (mqtt_client->fnOnErrorCallBack) + { + mqtt_client->fnOnErrorCallBack(mqtt_client, error_type, mqtt_client->errorCBCtx); + } + close_connection(mqtt_client); +} + +static STRING_HANDLE construct_trace_log_handle(MQTT_CLIENT* mqtt_client) +{ + STRING_HANDLE trace_log; + if (mqtt_client->logTrace) + { + trace_log = STRING_new(); + } + else + { + trace_log = NULL; + } + return trace_log; +} + +static uint16_t byteutil_read_uint16(uint8_t** buffer, size_t len) +{ + uint16_t result = 0; + if (buffer != NULL && *buffer != NULL && len >= 2) + { + result = 256 * (**buffer) + (*(*buffer + 1)); + *buffer += 2; // Move the ptr + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "byteutil_read_uint16 == NULL or less than 2"); + } + return result; +} + +static char* byteutil_readUTF(uint8_t** buffer, size_t* byteLen) +{ + char* result = NULL; + if (buffer != NULL) + { + // Get the length of the string + uint16_t len = byteutil_read_uint16(buffer, *byteLen); + if (len > 0) + { + result = (char*)malloc(len + 1); + if (result != NULL) + { + (void)memcpy(result, *buffer, len); + result[len] = '\0'; + *buffer += len; + if (byteLen != NULL) + { + *byteLen = len; + } + } + } + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "readByte buffer == NULL."); + } + return result; +} + +static uint8_t byteutil_readByte(uint8_t** buffer) +{ + uint8_t result = 0; + if (buffer != NULL) + { + result = **buffer; + (*buffer)++; + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "readByte buffer == NULL."); + } + return result; +} + +static void sendComplete(void* context, IO_SEND_RESULT send_result) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if (mqtt_client != NULL) + { + if (send_result == IO_SEND_OK) + { + if (mqtt_client->packetState == DISCONNECT_TYPE) + { + /*Codes_SRS_MQTT_CLIENT_07_032: [If the actionResult parameter is of type MQTT_CLIENT_ON_DISCONNECT the the msgInfo value shall be NULL.]*/ + if (mqtt_client->fnOperationCallback != NULL) + { + mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_DISCONNECT, NULL, mqtt_client->ctx); + } + // close the xio + close_connection(mqtt_client); + } + } + else if (send_result == IO_SEND_ERROR) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "MQTT Send Complete Failure send_result: %d", (int)send_result); + set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR); + } + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "MQTT Send Complete Failure with NULL mqtt_client"); + } +} + +#ifndef NO_LOGGING +static const char* retrievePacketType(CONTROL_PACKET_TYPE packet) +{ + switch (packet&CONNECT_PACKET_MASK) + { + case CONNECT_TYPE: return "CONNECT"; + case CONNACK_TYPE: return "CONNACK"; + case PUBLISH_TYPE: return "PUBLISH"; + case PUBACK_TYPE: return "PUBACK"; + case PUBREC_TYPE: return "PUBREC"; + case PUBREL_TYPE: return "PUBREL"; + case SUBSCRIBE_TYPE: return "SUBSCRIBE"; + case SUBACK_TYPE: return "SUBACK"; + case UNSUBSCRIBE_TYPE: return "UNSUBSCRIBE"; + case UNSUBACK_TYPE: return "UNSUBACK"; + case PINGREQ_TYPE: return "PINGREQ"; + case PINGRESP_TYPE: return "PINGRESP"; + case DISCONNECT_TYPE: return "DISCONNECT"; + default: + case PACKET_TYPE_ERROR: + case UNKNOWN_TYPE: + return "UNKNOWN"; + } +} +#endif // NO_LOGGING + +static void getLogTime(char* timeResult, size_t len) +{ + if (timeResult != NULL) + { + time_t agent_time = get_time(NULL); + if (agent_time == (time_t)-1) + { + timeResult[0] = '\0'; + } + else + { + struct tm* tmInfo = localtime(&agent_time); + if (tmInfo == NULL) + { + timeResult[0] = '\0'; + } + else + { + if (strftime(timeResult, len, "%H:%M:%S", tmInfo) == 0) + { + timeResult[0] = '\0'; + } + } + } + } +} + +static void log_outgoing_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) +{ + if (mqtt_client != NULL && mqtt_client->logTrace && trace_log != NULL) + { + char tmBuffer[TIME_MAX_BUFFER]; + getLogTime(tmBuffer, TIME_MAX_BUFFER); + LOG(AZ_LOG_TRACE, LOG_LINE, "-> %s %s", tmBuffer, STRING_c_str(trace_log)); + } +} + +static void logOutgoingRawTrace(MQTT_CLIENT* mqtt_client, const uint8_t* data, size_t length) +{ + if (mqtt_client != NULL && data != NULL && length > 0 && mqtt_client->rawBytesTrace) + { + char tmBuffer[TIME_MAX_BUFFER]; + getLogTime(tmBuffer, TIME_MAX_BUFFER); + + LOG(AZ_LOG_TRACE, 0, "-> %s %s: ", tmBuffer, retrievePacketType((unsigned char)data[0])); + size_t index = 0; + for (index = 0; index < length; index++) + { + LOG(AZ_LOG_TRACE, 0, "0x%02x ", data[index]); + } + LOG(AZ_LOG_TRACE, LOG_LINE, ""); + } +} + +static void log_incoming_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) +{ + if (mqtt_client != NULL && mqtt_client->logTrace && trace_log != NULL) + { + char tmBuffer[TIME_MAX_BUFFER]; + getLogTime(tmBuffer, TIME_MAX_BUFFER); + LOG(AZ_LOG_TRACE, LOG_LINE, "<- %s %s", tmBuffer, STRING_c_str(trace_log) ); + } +} + +static void logIncomingRawTrace(MQTT_CLIENT* mqtt_client, CONTROL_PACKET_TYPE packet, uint8_t flags, const uint8_t* data, size_t length) +{ +#ifdef NO_LOGGING + UNUSED(flags); +#endif + if (mqtt_client != NULL && mqtt_client->rawBytesTrace) + { + if (data != NULL && length > 0) + { + char tmBuffer[TIME_MAX_BUFFER]; + getLogTime(tmBuffer, TIME_MAX_BUFFER); + + LOG(AZ_LOG_TRACE, 0, "<- %s %s: 0x%02x 0x%02x ", tmBuffer, retrievePacketType((CONTROL_PACKET_TYPE)packet), (unsigned char)(packet | flags), length); + size_t index = 0; + for (index = 0; index < length; index++) + { + LOG(AZ_LOG_TRACE, 0, "0x%02x ", data[index]); + } + LOG(AZ_LOG_TRACE, LOG_LINE, ""); + } + else if (packet == PINGRESP_TYPE) + { + char tmBuffer[TIME_MAX_BUFFER]; + getLogTime(tmBuffer, TIME_MAX_BUFFER); + LOG(AZ_LOG_TRACE, LOG_LINE, "<- %s %s: 0x%02x 0x%02x ", tmBuffer, retrievePacketType((CONTROL_PACKET_TYPE)packet), (unsigned char)(packet | flags), length); + } + } +} + +static int sendPacketItem(MQTT_CLIENT* mqtt_client, const unsigned char* data, size_t length) +{ + int result; + + if (tickcounter_get_current_ms(mqtt_client->packetTickCntr, &mqtt_client->packetSendTimeMs) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Failure getting current ms tickcounter"); + result = __FAILURE__; + } + else + { + result = xio_send(mqtt_client->xioHandle, (const void*)data, length, sendComplete, mqtt_client); + if (result != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "%d: Failure sending control packet data", result); + result = __FAILURE__; + } + else + { + logOutgoingRawTrace(mqtt_client, (const uint8_t*)data, length); + } + } + return result; +} + +static void onOpenComplete(void* context, IO_OPEN_RESULT open_result) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if (mqtt_client != NULL) + { + if (open_result == IO_OPEN_OK && !mqtt_client->socketConnected) + { + mqtt_client->packetState = CONNECT_TYPE; + mqtt_client->socketConnected = true; + + STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); + + // Send the Connect packet + BUFFER_HANDLE connPacket = mqtt_codec_connect(&mqtt_client->mqttOptions, trace_log); + if (connPacket == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_codec_connect failed"); + } + else + { + size_t size = BUFFER_length(connPacket); + /*Codes_SRS_MQTT_CLIENT_07_009: [On success mqtt_client_connect shall send the MQTT CONNECT to the endpoint.]*/ + if (sendPacketItem(mqtt_client, BUFFER_u_char(connPacket), size) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_codec_connect failed"); + } + else + { + log_outgoing_trace(mqtt_client, trace_log); + } + BUFFER_delete(connPacket); + } + if (trace_log != NULL) + { + STRING_delete(trace_log); + } + } + else + { + if (mqtt_client->socketConnected == false && mqtt_client->fnOnErrorCallBack) + { + mqtt_client->fnOnErrorCallBack(mqtt_client, MQTT_CLIENT_CONNECTION_ERROR, mqtt_client->errorCBCtx); + } + close_connection(mqtt_client); + } + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client is NULL"); + } +} + +static void onBytesReceived(void* context, const unsigned char* buffer, size_t size) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if (mqtt_client != NULL) + { + if (mqtt_codec_bytesReceived(mqtt_client->codec_handle, buffer, size) != 0) + { + set_error_callback(mqtt_client, MQTT_CLIENT_PARSE_ERROR); + } + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client is NULL"); + } +} + +static void onIoError(void* context) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if (mqtt_client != NULL && mqtt_client->fnOperationCallback) + { + /*Codes_SRS_MQTT_CLIENT_07_032: [If the actionResult parameter is of type MQTT_CLIENT_ON_DISCONNECT the the msgInfo value shall be NULL.]*/ + /* Codes_SRS_MQTT_CLIENT_07_036: [ If an error is encountered by the ioHandle the mqtt_client shall call xio_close. ] */ + set_error_callback(mqtt_client, MQTT_CLIENT_CONNECTION_ERROR); + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error invalid parameter: mqtt_client: %p", mqtt_client); + } +} + +static void clear_mqtt_options(MQTT_CLIENT* mqtt_client) +{ + if (mqtt_client->mqttOptions.clientId != NULL) + { + free(mqtt_client->mqttOptions.clientId); + mqtt_client->mqttOptions.clientId = NULL; + } + + if (mqtt_client->mqttOptions.willTopic != NULL) + { + free(mqtt_client->mqttOptions.willTopic); + mqtt_client->mqttOptions.willTopic = NULL; + } + + if (mqtt_client->mqttOptions.willMessage != NULL) + { + free(mqtt_client->mqttOptions.willMessage); + mqtt_client->mqttOptions.willMessage = NULL; + } + + if (mqtt_client->mqttOptions.username != NULL) + { + free(mqtt_client->mqttOptions.username); + mqtt_client->mqttOptions.username = NULL; + } + + if (mqtt_client->mqttOptions.password != NULL) + { + free(mqtt_client->mqttOptions.password); + mqtt_client->mqttOptions.password = NULL; + } +} + +static int cloneMqttOptions(MQTT_CLIENT* mqtt_client, const MQTT_CLIENT_OPTIONS* mqttOptions) +{ + int result = 0; + + if (mqttOptions->clientId != NULL) + { + char* clientId; + + if (mallocAndStrcpy_s(&clientId, mqttOptions->clientId) != 0) + { + result = __FAILURE__; + LOG(AZ_LOG_ERROR, LOG_LINE, "mallocAndStrcpy_s clientId"); + } + else + { + if (mqtt_client->mqttOptions.clientId != NULL) + { + free(mqtt_client->mqttOptions.clientId); + } + + mqtt_client->mqttOptions.clientId = clientId; + } + } + if (result == 0 && mqttOptions->willTopic != NULL) + { + char* willTopic; + + if (mallocAndStrcpy_s(&willTopic, mqttOptions->willTopic) != 0) + { + result = __FAILURE__; + LOG(AZ_LOG_ERROR, LOG_LINE, "mallocAndStrcpy_s willTopic"); + } + else + { + if (mqtt_client->mqttOptions.willTopic != NULL) + { + free(mqtt_client->mqttOptions.willTopic); + } + + mqtt_client->mqttOptions.willTopic = willTopic; + } + } + if (result == 0 && mqttOptions->willMessage != NULL) + { + char* willMessage; + + if (mallocAndStrcpy_s(&willMessage, mqttOptions->willMessage) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "mallocAndStrcpy_s willMessage"); + result = __FAILURE__; + } + else + { + if (mqtt_client->mqttOptions.willMessage != NULL) + { + free(mqtt_client->mqttOptions.willMessage); + } + + mqtt_client->mqttOptions.willMessage = willMessage; + } + } + if (result == 0 && mqttOptions->username != NULL) + { + char* username; + + if (mallocAndStrcpy_s(&username, mqttOptions->username) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "mallocAndStrcpy_s username"); + result = __FAILURE__; + } + else + { + if (mqtt_client->mqttOptions.username != NULL) + { + free(mqtt_client->mqttOptions.username); + } + + mqtt_client->mqttOptions.username = username; + } + } + if (result == 0 && mqttOptions->password != NULL) + { + char* password; + + if (mallocAndStrcpy_s(&password, mqttOptions->password) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "mallocAndStrcpy_s password"); + result = __FAILURE__; + } + else + { + if (mqtt_client->mqttOptions.password != NULL) + { + free(mqtt_client->mqttOptions.password); + } + + mqtt_client->mqttOptions.password = password; + } + } + if (result == 0) + { + mqtt_client->mqttOptions.keepAliveInterval = mqttOptions->keepAliveInterval; + mqtt_client->mqttOptions.messageRetain = mqttOptions->messageRetain; + mqtt_client->mqttOptions.useCleanSession = mqttOptions->useCleanSession; + mqtt_client->mqttOptions.qualityOfServiceValue = mqttOptions->qualityOfServiceValue; + } + else + { + clear_mqtt_options(mqtt_client); + } + return result; +} + +static void recvCompleteCallback(void* context, CONTROL_PACKET_TYPE packet, int flags, BUFFER_HANDLE headerData) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; + if ((mqtt_client != NULL && headerData != NULL) || packet == PINGRESP_TYPE) + { + size_t len = BUFFER_length(headerData); + uint8_t* iterator = BUFFER_u_char(headerData); + + logIncomingRawTrace(mqtt_client, packet, (uint8_t)flags, iterator, len); + + if ((iterator != NULL && len > 0) || packet == PINGRESP_TYPE) + { + switch (packet) + { + case CONNACK_TYPE: + { + if (mqtt_client->fnOperationCallback != NULL) + { + STRING_HANDLE trace_log = NULL; + + /*Codes_SRS_MQTT_CLIENT_07_028: [If the actionResult parameter is of type CONNECT_ACK then the msgInfo value shall be a CONNECT_ACK structure.]*/ + CONNECT_ACK connack = { 0 }; + connack.isSessionPresent = (byteutil_readByte(&iterator) == 0x1) ? true : false; + uint8_t rc = byteutil_readByte(&iterator); + connack.returnCode = + (rc < ((uint8_t)CONN_REFUSED_UNKNOWN)) ? + (CONNECT_RETURN_CODE)rc : CONN_REFUSED_UNKNOWN; + + if (mqtt_client->logTrace) + { + trace_log = STRING_construct_sprintf("CONNACK | SESSION_PRESENT: %s | RETURN_CODE: 0x%x", connack.isSessionPresent ? TRUE_CONST : FALSE_CONST, connack.returnCode); + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_CONNACK, (void*)&connack, mqtt_client->ctx); + + if (connack.returnCode == CONNECTION_ACCEPTED) + { + mqtt_client->clientConnected = true; + } + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "fnOperationCallback NULL"); + } + break; + } + case PUBLISH_TYPE: + { + if (mqtt_client->fnMessageRecv != NULL) + { + STRING_HANDLE trace_log = NULL; + + bool isDuplicateMsg = (flags & DUPLICATE_FLAG_MASK) ? true : false; + bool isRetainMsg = (flags & RETAIN_FLAG_MASK) ? true : false; + QOS_VALUE qosValue = (flags == 0) ? DELIVER_AT_MOST_ONCE : (flags & QOS_LEAST_ONCE_FLAG_MASK) ? DELIVER_AT_LEAST_ONCE : DELIVER_EXACTLY_ONCE; + + if (mqtt_client->logTrace) + { + trace_log = STRING_construct_sprintf("PUBLISH | IS_DUP: %s | RETAIN: %d | QOS: %s", isDuplicateMsg ? TRUE_CONST : FALSE_CONST, + isRetainMsg ? 1 : 0, ENUM_TO_STRING(QOS_VALUE, qosValue) ); + } + + uint8_t* initialPos = iterator; + size_t length = len - (iterator - initialPos); + char* topicName = byteutil_readUTF(&iterator, &length); + if (topicName == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Publish MSG: failure reading topic name"); + set_error_callback(mqtt_client, MQTT_CLIENT_PARSE_ERROR); + if (trace_log != NULL) + { + STRING_delete(trace_log); + } + } + else + { + if (mqtt_client->logTrace) + { + STRING_sprintf(trace_log, " | TOPIC_NAME: %s", topicName); + } + uint16_t packetId = 0; + length = len - (iterator - initialPos); + if (qosValue != DELIVER_AT_MOST_ONCE) + { + packetId = byteutil_read_uint16(&iterator, length); + if (mqtt_client->logTrace) + { + STRING_sprintf(trace_log, " | PACKET_ID: %"PRIu16, packetId); + } + } + length = len - (iterator - initialPos); + + MQTT_MESSAGE_HANDLE msgHandle = mqttmessage_create(packetId, topicName, qosValue, iterator, length); + if (msgHandle == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "failure in mqttmessage_create"); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + if (trace_log != NULL) { + STRING_delete(trace_log); + } + } + else + { + if (mqttmessage_setIsDuplicateMsg(msgHandle, isDuplicateMsg) != 0 || + mqttmessage_setIsRetained(msgHandle, isRetainMsg) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "failure setting mqtt message property"); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + if (trace_log != NULL) { + STRING_delete(trace_log); + } + } + else + { + if (mqtt_client->logTrace) + { + STRING_sprintf(trace_log, " | PAYLOAD_LEN: %zu", length); + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + + mqtt_client->fnMessageRecv(msgHandle, mqtt_client->ctx); + + BUFFER_HANDLE pubRel = NULL; + if (qosValue == DELIVER_EXACTLY_ONCE) + { + pubRel = mqtt_codec_publishReceived(packetId); + if (pubRel == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Failed to allocate publish receive message."); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + } + } + else if (qosValue == DELIVER_AT_LEAST_ONCE) + { + pubRel = mqtt_codec_publishAck(packetId); + if (pubRel == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Failed to allocate publish ack message."); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + } + } + if (pubRel != NULL) + { + size_t size = BUFFER_length(pubRel); + (void)sendPacketItem(mqtt_client, BUFFER_u_char(pubRel), size); + BUFFER_delete(pubRel); + } + } + mqttmessage_destroy(msgHandle); + } + free(topicName); + } + } + break; + } + case PUBACK_TYPE: + case PUBREC_TYPE: + case PUBREL_TYPE: + case PUBCOMP_TYPE: + { + if (mqtt_client->fnOperationCallback) + { + STRING_HANDLE trace_log = NULL; + + /*Codes_SRS_MQTT_CLIENT_07_029: [If the actionResult parameter are of types PUBACK_TYPE, PUBREC_TYPE, PUBREL_TYPE or PUBCOMP_TYPE then the msgInfo value shall be a PUBLISH_ACK structure.]*/ + MQTT_CLIENT_EVENT_RESULT action = (packet == PUBACK_TYPE) ? MQTT_CLIENT_ON_PUBLISH_ACK : + (packet == PUBREC_TYPE) ? MQTT_CLIENT_ON_PUBLISH_RECV : + (packet == PUBREL_TYPE) ? MQTT_CLIENT_ON_PUBLISH_REL : MQTT_CLIENT_ON_PUBLISH_COMP; + + PUBLISH_ACK publish_ack = { 0 }; + publish_ack.packetId = byteutil_read_uint16(&iterator, len); + + if (mqtt_client->logTrace) + { + trace_log = STRING_construct_sprintf("%s | PACKET_ID: %"PRIu16, packet == PUBACK_TYPE ? "PUBACK" : (packet == PUBREC_TYPE) ? "PUBREC" : (packet == PUBREL_TYPE) ? "PUBREL" : "PUBCOMP", + publish_ack.packetId); + + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + + BUFFER_HANDLE pubRel = NULL; + mqtt_client->fnOperationCallback(mqtt_client, action, (void*)&publish_ack, mqtt_client->ctx); + if (packet == PUBREC_TYPE) + { + pubRel = mqtt_codec_publishRelease(publish_ack.packetId); + if (pubRel == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Failed to allocate publish release message."); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + } + } + else if (packet == PUBREL_TYPE) + { + pubRel = mqtt_codec_publishComplete(publish_ack.packetId); + if (pubRel == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Failed to allocate publish complete message."); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + } + } + if (pubRel != NULL) + { + size_t size = BUFFER_length(pubRel); + (void)sendPacketItem(mqtt_client, BUFFER_u_char(pubRel), size); + BUFFER_delete(pubRel); + } + } + break; + } + case SUBACK_TYPE: + { + if (mqtt_client->fnOperationCallback) + { + STRING_HANDLE trace_log = NULL; + + /*Codes_SRS_MQTT_CLIENT_07_030: [If the actionResult parameter is of type SUBACK_TYPE then the msgInfo value shall be a SUBSCRIBE_ACK structure.]*/ + SUBSCRIBE_ACK suback = { 0 }; + + size_t remainLen = len; + suback.packetId = byteutil_read_uint16(&iterator, len); + remainLen -= 2; + + if (mqtt_client->logTrace) + { + trace_log = STRING_construct_sprintf("SUBACK | PACKET_ID: %"PRIu16, suback.packetId); + } + + // Allocate the remaining len + suback.qosReturn = (QOS_VALUE*)malloc(sizeof(QOS_VALUE)*remainLen); + if (suback.qosReturn != NULL) + { + while (remainLen > 0) + { + uint8_t qosRet = byteutil_readByte(&iterator); + suback.qosReturn[suback.qosCount++] = + (qosRet <= ((uint8_t)DELIVER_EXACTLY_ONCE)) ? + (QOS_VALUE)qosRet : DELIVER_FAILURE; + remainLen--; + if (mqtt_client->logTrace) + { + STRING_sprintf(trace_log, " | RETURN_CODE: %"PRIu16, suback.qosReturn[suback.qosCount-1]); + } + } + + if (mqtt_client->logTrace) + { + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_SUBSCRIBE_ACK, (void*)&suback, mqtt_client->ctx); + free(suback.qosReturn); + } + else + { + LOG(AZ_LOG_ERROR, LOG_LINE, "allocation of quality of service value failed."); + set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); + } + } + break; + } + case UNSUBACK_TYPE: + { + if (mqtt_client->fnOperationCallback) + { + STRING_HANDLE trace_log = NULL; + + /*Codes_SRS_MQTT_CLIENT_07_031: [If the actionResult parameter is of type UNSUBACK_TYPE then the msgInfo value shall be a UNSUBSCRIBE_ACK structure.]*/ + UNSUBSCRIBE_ACK unsuback = { 0 }; + unsuback.packetId = byteutil_read_uint16(&iterator, len); + + if (mqtt_client->logTrace) + { + trace_log = STRING_construct_sprintf("UNSUBACK | PACKET_ID: %"PRIu16, unsuback.packetId); + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_UNSUBSCRIBE_ACK, (void*)&unsuback, mqtt_client->ctx); + } + break; + } + case PINGRESP_TYPE: + mqtt_client->timeSincePing = 0; + if (mqtt_client->logTrace) + { + STRING_HANDLE trace_log = STRING_construct_sprintf("PINGRESP"); + log_incoming_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + // Forward ping response to operation callback + if (mqtt_client->fnOperationCallback) + { + mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_PING_RESPONSE, NULL, mqtt_client->ctx); + } + break; + default: + break; + } + } + } +} + +MQTT_CLIENT_HANDLE mqtt_client_init(ON_MQTT_MESSAGE_RECV_CALLBACK msgRecv, ON_MQTT_OPERATION_CALLBACK opCallback, void* opCallbackCtx, ON_MQTT_ERROR_CALLBACK onErrorCallBack, void* errorCBCtx) +{ + MQTT_CLIENT* result; + /*Codes_SRS_MQTT_CLIENT_07_001: [If the parameters ON_MQTT_MESSAGE_RECV_CALLBACK is NULL then mqttclient_init shall return NULL.]*/ + if (msgRecv == NULL) + { + result = NULL; + } + else + { + result = malloc(sizeof(MQTT_CLIENT)); + if (result == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "mqtt_client_init failure: Allocation Failure"); + } + else + { + memset(result, 0, sizeof(MQTT_CLIENT)); + /*Codes_SRS_MQTT_CLIENT_07_003: [mqttclient_init shall allocate MQTTCLIENT_DATA_INSTANCE and return the MQTTCLIENT_HANDLE on success.]*/ + result->packetState = UNKNOWN_TYPE; + result->fnOperationCallback = opCallback; + result->ctx = opCallbackCtx; + result->fnMessageRecv = msgRecv; + result->fnOnErrorCallBack = onErrorCallBack; + result->errorCBCtx = errorCBCtx; + result->qosValue = DELIVER_AT_MOST_ONCE; + result->packetTickCntr = tickcounter_create(); + result->maxPingRespTime = DEFAULT_MAX_PING_RESPONSE_TIME; + if (result->packetTickCntr == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "mqtt_client_init failure: tickcounter_create failure"); + free(result); + result = NULL; + } + else + { + result->codec_handle = mqtt_codec_create(recvCompleteCallback, result); + if (result->codec_handle == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "mqtt_client_init failure: mqtt_codec_create failure"); + tickcounter_destroy(result->packetTickCntr); + free(result); + result = NULL; + } + } + } + } + return result; +} + +void mqtt_client_deinit(MQTT_CLIENT_HANDLE handle) +{ + /*Codes_SRS_MQTT_CLIENT_07_004: [If the parameter handle is NULL then function mqtt_client_deinit shall do nothing.]*/ + if (handle != NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_005: [mqtt_client_deinit shall deallocate all memory allocated in this unit.]*/ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + tickcounter_destroy(mqtt_client->packetTickCntr); + mqtt_codec_destroy(mqtt_client->codec_handle); + clear_mqtt_options(mqtt_client); + free(mqtt_client); + } +} + +int mqtt_client_connect(MQTT_CLIENT_HANDLE handle, XIO_HANDLE xioHandle, MQTT_CLIENT_OPTIONS* mqttOptions) +{ + int result; + /*SRS_MQTT_CLIENT_07_006: [If any of the parameters handle, ioHandle, or mqttOptions are NULL then mqtt_client_connect shall return a non-zero value.]*/ + if (handle == NULL || mqttOptions == NULL) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "mqtt_client_connect: NULL argument (handle = %p, mqttOptions = %p)", handle, mqttOptions); + result = __FAILURE__; + } + else + { + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (xioHandle == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_007: [If any failure is encountered then mqtt_client_connect shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqttcodec_connect failed"); + result = __FAILURE__; + } + else + { + mqtt_client->xioHandle = xioHandle; + mqtt_client->packetState = UNKNOWN_TYPE; + mqtt_client->qosValue = mqttOptions->qualityOfServiceValue; + mqtt_client->keepAliveInterval = mqttOptions->keepAliveInterval; + mqtt_client->maxPingRespTime = (DEFAULT_MAX_PING_RESPONSE_TIME < mqttOptions->keepAliveInterval/2) ? DEFAULT_MAX_PING_RESPONSE_TIME : mqttOptions->keepAliveInterval/2; + if (cloneMqttOptions(mqtt_client, mqttOptions) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: Clone Mqtt Options failed"); + result = __FAILURE__; + } + /*Codes_SRS_MQTT_CLIENT_07_008: [mqtt_client_connect shall open the XIO_HANDLE by calling into the xio_open interface.]*/ + else if (xio_open(xioHandle, onOpenComplete, mqtt_client, onBytesReceived, mqtt_client, onIoError, mqtt_client) != 0) + { + /*Codes_SRS_MQTT_CLIENT_07_007: [If any failure is encountered then mqtt_client_connect shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: io_open failed"); + result = __FAILURE__; + // Remove cloned options + clear_mqtt_options(mqtt_client); + } + else + { + result = 0; + } + } + } + return result; +} + +int mqtt_client_publish(MQTT_CLIENT_HANDLE handle, MQTT_MESSAGE_HANDLE msgHandle) +{ + int result; + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (mqtt_client == NULL || msgHandle == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_019: [If one of the parameters handle or msgHandle is NULL then mqtt_client_publish shall return a non-zero value.]*/ + LogError("Invalid parameter specified mqtt_client: %p, msgHandle: %p", mqtt_client, msgHandle); + result = __FAILURE__; + } + else + { + /*Codes_SRS_MQTT_CLIENT_07_021: [mqtt_client_publish shall get the message information from the MQTT_MESSAGE_HANDLE.]*/ + const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle); + if (payload == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqttmessage_getApplicationMsg failed"); + result = __FAILURE__; + } + else + { + STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); + + QOS_VALUE qos = mqttmessage_getQosType(msgHandle); + bool isDuplicate = mqttmessage_getIsDuplicateMsg(msgHandle); + bool isRetained = mqttmessage_getIsRetained(msgHandle); + uint16_t packetId = mqttmessage_getPacketId(msgHandle); + const char* topicName = mqttmessage_getTopicName(msgHandle); + BUFFER_HANDLE publishPacket = mqtt_codec_publish(qos, isDuplicate, isRetained, packetId, topicName, payload->message, payload->length, trace_log); + if (publishPacket == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_codec_publish failed"); + result = __FAILURE__; + } + else + { + mqtt_client->packetState = PUBLISH_TYPE; + + /*Codes_SRS_MQTT_CLIENT_07_022: [On success mqtt_client_publish shall send the MQTT SUBCRIBE packet to the endpoint.]*/ + size_t size = BUFFER_length(publishPacket); + if (sendPacketItem(mqtt_client, BUFFER_u_char(publishPacket), size) != 0) + { + /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client_publish send failed"); + result = __FAILURE__; + } + else + { + log_outgoing_trace(mqtt_client, trace_log); + result = 0; + } + BUFFER_delete(publishPacket); + } + if (trace_log != NULL) + { + STRING_delete(trace_log); + } + } + } + return result; +} + +int mqtt_client_subscribe(MQTT_CLIENT_HANDLE handle, uint16_t packetId, SUBSCRIBE_PAYLOAD* subscribeList, size_t count) +{ + int result; + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (mqtt_client == NULL || subscribeList == NULL || count == 0 || packetId == 0) + { + /*Codes_SRS_MQTT_CLIENT_07_013: [If any of the parameters handle, subscribeList is NULL or count is 0 then mqtt_client_subscribe shall return a non-zero value.]*/ + LogError("Invalid parameter specified mqtt_client: %p, subscribeList: %p, count: %d, packetId: %d", mqtt_client, subscribeList, count, packetId); + result = __FAILURE__; + } + else + { + STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); + + BUFFER_HANDLE subPacket = mqtt_codec_subscribe(packetId, subscribeList, count, trace_log); + if (subPacket == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_014: [If any failure is encountered then mqtt_client_subscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_codec_subscribe failed"); + result = __FAILURE__; + } + else + { + mqtt_client->packetState = SUBSCRIBE_TYPE; + + size_t size = BUFFER_length(subPacket); + /*Codes_SRS_MQTT_CLIENT_07_015: [On success mqtt_client_subscribe shall send the MQTT SUBCRIBE packet to the endpoint.]*/ + if (sendPacketItem(mqtt_client, BUFFER_u_char(subPacket), size) != 0) + { + /*Codes_SRS_MQTT_CLIENT_07_014: [If any failure is encountered then mqtt_client_subscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client_subscribe send failed"); + result = __FAILURE__; + } + else + { + log_outgoing_trace(mqtt_client, trace_log); + result = 0; + } + BUFFER_delete(subPacket); + } + if (trace_log != NULL) + { + STRING_delete(trace_log); + } + } + return result; +} + +int mqtt_client_unsubscribe(MQTT_CLIENT_HANDLE handle, uint16_t packetId, const char** unsubscribeList, size_t count) +{ + int result; + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (mqtt_client == NULL || unsubscribeList == NULL || count == 0 || packetId == 0) + { + /*Codes_SRS_MQTT_CLIENT_07_016: [If any of the parameters handle, unsubscribeList is NULL or count is 0 then mqtt_client_unsubscribe shall return a non-zero value.]*/ + LogError("Invalid parameter specified mqtt_client: %p, unsubscribeList: %p, count: %d, packetId: %d", mqtt_client, unsubscribeList, count, packetId); + result = __FAILURE__; + } + else + { + STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); + + BUFFER_HANDLE unsubPacket = mqtt_codec_unsubscribe(packetId, unsubscribeList, count, trace_log); + if (unsubPacket == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_017: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_codec_unsubscribe failed"); + result = __FAILURE__; + } + else + { + mqtt_client->packetState = UNSUBSCRIBE_TYPE; + + size_t size = BUFFER_length(unsubPacket); + /*Codes_SRS_MQTT_CLIENT_07_018: [On success mqtt_client_unsubscribe shall send the MQTT SUBCRIBE packet to the endpoint.]*/ + if (sendPacketItem(mqtt_client, BUFFER_u_char(unsubPacket), size) != 0) + { + /*Codes_SRS_MQTT_CLIENT_07_017: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.].]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client_unsubscribe send failed"); + result = __FAILURE__; + } + else + { + log_outgoing_trace(mqtt_client, trace_log); + result = 0; + } + BUFFER_delete(unsubPacket); + } + if (trace_log != NULL) + { + STRING_delete(trace_log); + } + } + return result; +} + +int mqtt_client_disconnect(MQTT_CLIENT_HANDLE handle, ON_MQTT_DISCONNECTED_CALLBACK callback, void* ctx) +{ + int result; + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (mqtt_client == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_010: [If the parameters handle is NULL then mqtt_client_disconnect shall return a non-zero value.]*/ + result = __FAILURE__; + } + else + { + if (mqtt_client->clientConnected) + { + BUFFER_HANDLE disconnectPacket = mqtt_codec_disconnect(); + if (disconnectPacket == NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_011: [If any failure is encountered then mqtt_client_disconnect shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client_disconnect failed"); + mqtt_client->packetState = PACKET_TYPE_ERROR; + result = __FAILURE__; + } + else + { + /* Codes_SRS_MQTT_CLIENT_07_037: [ if callback is not NULL callback shall be called once the mqtt connection has been disconnected ] */ + mqtt_client->disconnect_cb = callback; + mqtt_client->disconnect_ctx = ctx; + mqtt_client->packetState = DISCONNECT_TYPE; + + size_t size = BUFFER_length(disconnectPacket); + /*Codes_SRS_MQTT_CLIENT_07_012: [On success mqtt_client_disconnect shall send the MQTT DISCONNECT packet to the endpoint.]*/ + if (sendPacketItem(mqtt_client, BUFFER_u_char(disconnectPacket), size) != 0) + { + /*Codes_SRS_MQTT_CLIENT_07_011: [If any failure is encountered then mqtt_client_disconnect shall return a non-zero value.]*/ + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: mqtt_client_disconnect send failed"); + result = __FAILURE__; + } + else + { + if (mqtt_client->logTrace) + { + STRING_HANDLE trace_log = STRING_construct("DISCONNECT"); + log_outgoing_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + result = 0; + } + BUFFER_delete(disconnectPacket); + } + clear_mqtt_options(mqtt_client); + mqtt_client->xioHandle = NULL; + } + else + { + // If the client is not connected then just close the underlying socket + mqtt_client->disconnect_cb = callback; + mqtt_client->disconnect_ctx = ctx; + + close_connection(mqtt_client); + clear_mqtt_options(mqtt_client); + result = 0; + } + } + return result; +} + +void mqtt_client_dowork(MQTT_CLIENT_HANDLE handle) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + /*Codes_SRS_MQTT_CLIENT_18_001: [If the client is disconnected, mqtt_client_dowork shall do nothing.]*/ + /*Codes_SRS_MQTT_CLIENT_07_023: [If the parameter handle is NULL then mqtt_client_dowork shall do nothing.]*/ + if (mqtt_client != NULL && mqtt_client->xioHandle != NULL) + { + /*Codes_SRS_MQTT_CLIENT_07_024: [mqtt_client_dowork shall call the xio_dowork function to complete operations.]*/ + xio_dowork(mqtt_client->xioHandle); + + /*Codes_SRS_MQTT_CLIENT_07_025: [mqtt_client_dowork shall retrieve the the last packet send value and ...]*/ + if (mqtt_client->socketConnected && mqtt_client->clientConnected && mqtt_client->keepAliveInterval > 0) + { + tickcounter_ms_t current_ms; + if (tickcounter_get_current_ms(mqtt_client->packetTickCntr, ¤t_ms) != 0) + { + LOG(AZ_LOG_ERROR, LOG_LINE, "Error: tickcounter_get_current_ms failed"); + } + else + { + /* Codes_SRS_MQTT_CLIENT_07_035: [If the timeSincePing has expired past the maxPingRespTime then mqtt_client_dowork shall call the Error Callback function with the message MQTT_CLIENT_NO_PING_RESPONSE] */ + if (mqtt_client->timeSincePing > 0 && ((current_ms - mqtt_client->timeSincePing)/1000) > mqtt_client->maxPingRespTime) + { + // We haven't gotten a ping response in the alloted time + set_error_callback(mqtt_client, MQTT_CLIENT_NO_PING_RESPONSE); + mqtt_client->timeSincePing = 0; + mqtt_client->packetSendTimeMs = 0; + mqtt_client->packetState = UNKNOWN_TYPE; + } + else if (((current_ms - mqtt_client->packetSendTimeMs) / 1000) >= mqtt_client->keepAliveInterval) + { + /*Codes_SRS_MQTT_CLIENT_07_026: [if keepAliveInternal is > 0 and the send time is greater than the MQTT KeepAliveInterval then it shall construct an MQTT PINGREQ packet.]*/ + BUFFER_HANDLE pingPacket = mqtt_codec_ping(); + if (pingPacket != NULL) + { + size_t size = BUFFER_length(pingPacket); + (void)sendPacketItem(mqtt_client, BUFFER_u_char(pingPacket), size); + BUFFER_delete(pingPacket); + (void)tickcounter_get_current_ms(mqtt_client->packetTickCntr, &mqtt_client->timeSincePing); + + if (mqtt_client->logTrace) + { + STRING_HANDLE trace_log = STRING_construct("PINGREQ"); + log_outgoing_trace(mqtt_client, trace_log); + STRING_delete(trace_log); + } + } + } + } + } + } +} + +void mqtt_client_set_trace(MQTT_CLIENT_HANDLE handle, bool traceOn, bool rawBytesOn) +{ + MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; + if (mqtt_client != NULL) + { + mqtt_client->logTrace = traceOn; + mqtt_client->rawBytesTrace = rawBytesOn; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/src/mqtt_codec.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,1134 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include <limits.h> +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_umqtt_c/mqtt_codec.h" +#include <inttypes.h> + +#define PAYLOAD_OFFSET 5 +#define PACKET_TYPE_BYTE(p) (CONTROL_PACKET_TYPE)((uint8_t)(((uint8_t)(p)) & 0xf0)) +#define FLAG_VALUE_BYTE(p) ((uint8_t)(((uint8_t)(p)) & 0xf)) + +#define USERNAME_FLAG 0x80 +#define PASSWORD_FLAG 0x40 +#define WILL_RETAIN_FLAG 0x20 +#define WILL_QOS_FLAG_ 0x18 +#define WILL_FLAG_FLAG 0x04 +#define CLEAN_SESSION_FLAG 0x02 + +#define NEXT_128_CHUNK 0x80 +#define PUBLISH_DUP_FLAG 0x8 +#define PUBLISH_QOS_EXACTLY_ONCE 0x4 +#define PUBLISH_QOS_AT_LEAST_ONCE 0x2 +#define PUBLISH_QOS_RETAIN 0x1 + +#define PROTOCOL_NUMBER 4 +#define CONN_FLAG_BYTE_OFFSET 7 + +#define CONNECT_FIXED_HEADER_SIZE 2 +#define CONNECT_VARIABLE_HEADER_SIZE 10 +#define SUBSCRIBE_FIXED_HEADER_FLAG 0x2 +#define UNSUBSCRIBE_FIXED_HEADER_FLAG 0x2 + +#define MAX_SEND_SIZE 0xFFFFFF7F + +#define CODEC_STATE_VALUES \ + CODEC_STATE_FIXED_HEADER, \ + CODEC_STATE_VAR_HEADER, \ + CODEC_STATE_PAYLOAD + +static const char* const TRUE_CONST = "true"; +static const char* const FALSE_CONST = "false"; + +DEFINE_ENUM(CODEC_STATE_RESULT, CODEC_STATE_VALUES); + +typedef struct MQTTCODEC_INSTANCE_TAG +{ + CONTROL_PACKET_TYPE currPacket; + CODEC_STATE_RESULT codecState; + size_t bufferOffset; + int headerFlags; + BUFFER_HANDLE headerData; + ON_PACKET_COMPLETE_CALLBACK packetComplete; + void* callContext; + uint8_t storeRemainLen[4]; + size_t remainLenIndex; +} MQTTCODEC_INSTANCE; + +typedef struct PUBLISH_HEADER_INFO_TAG +{ + const char* topicName; + uint16_t packetId; + const char* msgBuffer; + QOS_VALUE qualityOfServiceValue; +} PUBLISH_HEADER_INFO; + +static const char* retrieve_qos_value(QOS_VALUE value) +{ + switch (value) + { + case DELIVER_AT_MOST_ONCE: + return "DELIVER_AT_MOST_ONCE"; + case DELIVER_AT_LEAST_ONCE: + return "DELIVER_AT_LEAST_ONCE"; + case DELIVER_EXACTLY_ONCE: + default: + return "DELIVER_EXACTLY_ONCE"; + } +} + +static void byteutil_writeByte(uint8_t** buffer, uint8_t value) +{ + if (buffer != NULL) + { + **buffer = value; + (*buffer)++; + } +} + +static void byteutil_writeInt(uint8_t** buffer, uint16_t value) +{ + if (buffer != NULL) + { + **buffer = (char)(value / 256); + (*buffer)++; + **buffer = (char)(value % 256); + (*buffer)++; + } +} + +static void byteutil_writeUTF(uint8_t** buffer, const char* stringData, uint16_t len) +{ + if (buffer != NULL) + { + byteutil_writeInt(buffer, len); + (void)memcpy(*buffer, stringData, len); + *buffer += len; + } +} + +static CONTROL_PACKET_TYPE processControlPacketType(uint8_t pktByte, int* flags) +{ + CONTROL_PACKET_TYPE result; + result = PACKET_TYPE_BYTE(pktByte); + if (flags != NULL) + { + *flags = FLAG_VALUE_BYTE(pktByte); + } + return result; +} + +static int addListItemsToUnsubscribePacket(BUFFER_HANDLE ctrlPacket, const char** payloadList, size_t payloadCount, STRING_HANDLE trace_log) +{ + int result = 0; + if (payloadList == NULL || ctrlPacket == NULL) + { + result = __FAILURE__; + } + else + { + size_t index = 0; + for (index = 0; index < payloadCount && result == 0; index++) + { + // Add the Payload + size_t offsetLen = BUFFER_length(ctrlPacket); + size_t topicLen = strlen(payloadList[index]); + if (topicLen > USHRT_MAX) + { + result = __FAILURE__; + } + else if (BUFFER_enlarge(ctrlPacket, topicLen + 2) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(ctrlPacket); + iterator += offsetLen; + byteutil_writeUTF(&iterator, payloadList[index], (uint16_t)topicLen); + } + if (trace_log != NULL) + { + STRING_sprintf(trace_log, " | TOPIC_NAME: %s", payloadList[index]); + } + } + } + return result; +} + +static int addListItemsToSubscribePacket(BUFFER_HANDLE ctrlPacket, SUBSCRIBE_PAYLOAD* payloadList, size_t payloadCount, STRING_HANDLE trace_log) +{ + int result = 0; + if (payloadList == NULL || ctrlPacket == NULL) + { + result = __FAILURE__; + } + else + { + size_t index = 0; + for (index = 0; index < payloadCount && result == 0; index++) + { + // Add the Payload + size_t offsetLen = BUFFER_length(ctrlPacket); + size_t topicLen = strlen(payloadList[index].subscribeTopic); + if (topicLen > USHRT_MAX) + { + result = __FAILURE__; + } + else if (BUFFER_enlarge(ctrlPacket, topicLen + 2 + 1) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(ctrlPacket); + iterator += offsetLen; + byteutil_writeUTF(&iterator, payloadList[index].subscribeTopic, (uint16_t)topicLen); + *iterator = payloadList[index].qosReturn; + + if (trace_log != NULL) + { + STRING_sprintf(trace_log, " | TOPIC_NAME: %s | QOS: %d", payloadList[index].subscribeTopic, (int)payloadList[index].qosReturn); + } + } + } + } + return result; +} + +static int constructConnectVariableHeader(BUFFER_HANDLE ctrlPacket, const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) +{ + int result = 0; + if (BUFFER_enlarge(ctrlPacket, CONNECT_VARIABLE_HEADER_SIZE) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(ctrlPacket); + if (iterator == NULL) + { + result = __FAILURE__; + } + else + { + if (trace_log != NULL) + { + STRING_sprintf(trace_log, " | VER: %d | KEEPALIVE: %d | FLAGS:", PROTOCOL_NUMBER, mqttOptions->keepAliveInterval); + } + byteutil_writeUTF(&iterator, "MQTT", 4); + byteutil_writeByte(&iterator, PROTOCOL_NUMBER); + byteutil_writeByte(&iterator, 0); // Flags will be entered later + byteutil_writeInt(&iterator, mqttOptions->keepAliveInterval); + result = 0; + } + } + return result; +} + +static int constructPublishVariableHeader(BUFFER_HANDLE ctrlPacket, const PUBLISH_HEADER_INFO* publishHeader, STRING_HANDLE trace_log) +{ + int result = 0; + size_t topicLen = 0; + size_t spaceLen = 0; + size_t idLen = 0; + + size_t currLen = BUFFER_length(ctrlPacket); + + topicLen = strlen(publishHeader->topicName); + spaceLen += 2; + + if (publishHeader->qualityOfServiceValue != DELIVER_AT_MOST_ONCE) + { + // Packet Id is only set if the QOS is not 0 + idLen = 2; + } + + if (topicLen > USHRT_MAX) + { + result = __FAILURE__; + } + else if (BUFFER_enlarge(ctrlPacket, topicLen + idLen + spaceLen) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(ctrlPacket); + if (iterator == NULL) + { + result = __FAILURE__; + } + else + { + iterator += currLen; + /* The Topic Name MUST be present as the first field in the PUBLISH Packet Variable header.It MUST be 792 a UTF-8 encoded string [MQTT-3.3.2-1] as defined in section 1.5.3.*/ + byteutil_writeUTF(&iterator, publishHeader->topicName, (uint16_t)topicLen); + if (trace_log != NULL) + { + STRING_sprintf(trace_log, " | TOPIC_NAME: %s", publishHeader->topicName); + } + if (idLen > 0) + { + if (trace_log != NULL) + { + STRING_sprintf(trace_log, " | PACKET_ID: %"PRIu16, publishHeader->packetId); + } + byteutil_writeInt(&iterator, publishHeader->packetId); + } + result = 0; + } + } + return result; +} + +static int constructSubscibeTypeVariableHeader(BUFFER_HANDLE ctrlPacket, uint16_t packetId) +{ + int result = 0; + if (BUFFER_enlarge(ctrlPacket, 2) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(ctrlPacket); + if (iterator == NULL) + { + result = __FAILURE__; + } + else + { + byteutil_writeInt(&iterator, packetId); + result = 0; + } + } + return result; +} + +static BUFFER_HANDLE constructPublishReply(CONTROL_PACKET_TYPE type, uint8_t flags, uint16_t packetId) +{ + BUFFER_HANDLE result = BUFFER_new(); + if (result != NULL) + { + if (BUFFER_pre_build(result, 4) != 0) + { + BUFFER_delete(result); + result = NULL; + } + else + { + uint8_t* iterator = BUFFER_u_char(result); + if (iterator == NULL) + { + BUFFER_delete(result); + result = NULL; + } + else + { + *iterator = (uint8_t)type | flags; + iterator++; + *iterator = 0x2; + iterator++; + byteutil_writeInt(&iterator, packetId); + } + } + } + return result; +} + +static int constructFixedHeader(BUFFER_HANDLE ctrlPacket, CONTROL_PACKET_TYPE packetType, uint8_t flags) +{ + int result; + if (ctrlPacket == NULL) + { + return __FAILURE__; + } + else + { + size_t packetLen = BUFFER_length(ctrlPacket); + uint8_t remainSize[4] ={ 0 }; + size_t index = 0; + + // Calculate the length of packet + do + { + uint8_t encode = packetLen % 128; + packetLen /= 128; + // if there are more data to encode, set the top bit of this byte + if (packetLen > 0) + { + encode |= NEXT_128_CHUNK; + } + remainSize[index++] = encode; + } while (packetLen > 0); + + BUFFER_HANDLE fixedHeader = BUFFER_new(); + if (fixedHeader == NULL) + { + result = __FAILURE__; + } + else if (BUFFER_pre_build(fixedHeader, index + 1) != 0) + { + BUFFER_delete(fixedHeader); + result = __FAILURE__; + } + else + { + uint8_t* iterator = BUFFER_u_char(fixedHeader); + *iterator = (uint8_t)packetType | flags; + iterator++; + (void)memcpy(iterator, remainSize, index); + + result = BUFFER_prepend(ctrlPacket, fixedHeader); + BUFFER_delete(fixedHeader); + } + } + return result; +} + +static int constructConnPayload(BUFFER_HANDLE ctrlPacket, const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) +{ + int result = 0; + if (mqttOptions == NULL || ctrlPacket == NULL) + { + result = __FAILURE__; + } + else + { + size_t clientLen = 0; + size_t usernameLen = 0; + size_t passwordLen = 0; + size_t willMessageLen = 0; + size_t willTopicLen = 0; + size_t spaceLen = 0; + + if (mqttOptions->clientId != NULL) + { + spaceLen += 2; + clientLen = strlen(mqttOptions->clientId); + } + if (mqttOptions->username != NULL) + { + spaceLen += 2; + usernameLen = strlen(mqttOptions->username); + } + if (mqttOptions->password != NULL) + { + spaceLen += 2; + passwordLen = strlen(mqttOptions->password); + } + if (mqttOptions->willMessage != NULL) + { + spaceLen += 2; + willMessageLen = strlen(mqttOptions->willMessage); + } + if (mqttOptions->willTopic != NULL) + { + spaceLen += 2; + willTopicLen = strlen(mqttOptions->willTopic); + } + + size_t currLen = BUFFER_length(ctrlPacket); + size_t totalLen = clientLen + usernameLen + passwordLen + willMessageLen + willTopicLen + spaceLen; + + // Validate the Username & Password + if (clientLen > USHRT_MAX) + { + result = __FAILURE__; + } + else if (usernameLen == 0 && passwordLen > 0) + { + result = __FAILURE__; + } + else if ((willMessageLen > 0 && willTopicLen == 0) || (willTopicLen > 0 && willMessageLen == 0)) + { + result = __FAILURE__; + } + else if (BUFFER_enlarge(ctrlPacket, totalLen) != 0) + { + result = __FAILURE__; + } + else + { + uint8_t* packet = BUFFER_u_char(ctrlPacket); + uint8_t* iterator = packet; + + iterator += currLen; + byteutil_writeUTF(&iterator, mqttOptions->clientId, (uint16_t)clientLen); + + // TODO: Read on the Will Topic + if (willMessageLen > USHRT_MAX || willTopicLen > USHRT_MAX || usernameLen > USHRT_MAX || passwordLen > USHRT_MAX) + { + result = __FAILURE__; + } + else + { + STRING_HANDLE connect_payload_trace = NULL; + if (trace_log != NULL) + { + connect_payload_trace = STRING_new(); + } + if (willMessageLen > 0 && willTopicLen > 0) + { + if (trace_log != NULL) + { + (void)STRING_sprintf(connect_payload_trace, " | WILL_TOPIC: %s", mqttOptions->willTopic); + } + packet[CONN_FLAG_BYTE_OFFSET] |= WILL_FLAG_FLAG; + byteutil_writeUTF(&iterator, mqttOptions->willTopic, (uint16_t)willTopicLen); + packet[CONN_FLAG_BYTE_OFFSET] |= (mqttOptions->qualityOfServiceValue << 3); + if (mqttOptions->messageRetain) + { + packet[CONN_FLAG_BYTE_OFFSET] |= WILL_RETAIN_FLAG; + } + byteutil_writeUTF(&iterator, mqttOptions->willMessage, (uint16_t)willMessageLen); + } + if (usernameLen > 0) + { + packet[CONN_FLAG_BYTE_OFFSET] |= USERNAME_FLAG; + byteutil_writeUTF(&iterator, mqttOptions->username, (uint16_t)usernameLen); + if (trace_log != NULL) + { + (void)STRING_sprintf(connect_payload_trace, " | USERNAME: %s", mqttOptions->username); + } + } + if (passwordLen > 0) + { + packet[CONN_FLAG_BYTE_OFFSET] |= PASSWORD_FLAG; + byteutil_writeUTF(&iterator, mqttOptions->password, (uint16_t)passwordLen); + if (trace_log != NULL) + { + (void)STRING_sprintf(connect_payload_trace, " | PWD: XXXX"); + } + } + // TODO: Get the rest of the flags + if (trace_log != NULL) + { + (void)STRING_sprintf(connect_payload_trace, " | CLEAN: %s", mqttOptions->useCleanSession ? "1" : "0"); + } + if (mqttOptions->useCleanSession) + { + packet[CONN_FLAG_BYTE_OFFSET] |= CLEAN_SESSION_FLAG; + } + if (trace_log != NULL) + { + (void)STRING_sprintf(trace_log, " %lu", packet[CONN_FLAG_BYTE_OFFSET]); + (void)STRING_concat_with_STRING(trace_log, connect_payload_trace); + STRING_delete(connect_payload_trace); + } + result = 0; + } + } + } + return result; +} + +static int prepareheaderDataInfo(MQTTCODEC_INSTANCE* codecData, uint8_t remainLen) +{ + int result; + if (codecData == NULL) + { + result = __FAILURE__; + } + else + { + result = 0; + codecData->storeRemainLen[codecData->remainLenIndex++] = remainLen; + if (remainLen <= 0x7f) + { + int multiplier = 1; + int totalLen = 0; + size_t index = 0; + uint8_t encodeByte = 0; + do + { + encodeByte = codecData->storeRemainLen[index++]; + totalLen += (encodeByte & 127) * multiplier; + multiplier *= NEXT_128_CHUNK; + + if (multiplier > 128 * 128 * 128) + { + result = __FAILURE__; + break; + } + } while ((encodeByte & NEXT_128_CHUNK) != 0); + + codecData->codecState = CODEC_STATE_VAR_HEADER; + + // Reset remainLen Index + codecData->remainLenIndex = 0; + memset(codecData->storeRemainLen, 0, 4 * sizeof(uint8_t)); + + if (totalLen > 0) + { + codecData->bufferOffset = 0; + codecData->headerData = BUFFER_new(); + if (codecData->headerData == NULL) + { + /* Codes_SRS_MQTT_CODEC_07_035: [ If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value. ] */ + LogError("Failed BUFFER_new"); + result = __FAILURE__; + } + else + { + if (BUFFER_pre_build(codecData->headerData, totalLen) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_035: [ If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value. ] */ + LogError("Failed BUFFER_pre_build"); + result = __FAILURE__; + } + + } + } + } + } + return result; +} + +static void completePacketData(MQTTCODEC_INSTANCE* codecData) +{ + if (codecData) + { + if (codecData->packetComplete != NULL) + { + codecData->packetComplete(codecData->callContext, codecData->currPacket, codecData->headerFlags, codecData->headerData); + } + + // Clean up data + codecData->currPacket = UNKNOWN_TYPE; + codecData->codecState = CODEC_STATE_FIXED_HEADER; + codecData->headerFlags = 0; + BUFFER_delete(codecData->headerData); + codecData->headerData = NULL; + } +} + +MQTTCODEC_HANDLE mqtt_codec_create(ON_PACKET_COMPLETE_CALLBACK packetComplete, void* callbackCtx) +{ + MQTTCODEC_HANDLE result; + result = malloc(sizeof(MQTTCODEC_INSTANCE)); + /* Codes_SRS_MQTT_CODEC_07_001: [If a failure is encountered then mqtt_codec_create shall return NULL.] */ + if (result != NULL) + { + /* Codes_SRS_MQTT_CODEC_07_002: [On success mqtt_codec_create shall return a MQTTCODEC_HANDLE value.] */ + result->currPacket = UNKNOWN_TYPE; + result->codecState = CODEC_STATE_FIXED_HEADER; + result->headerFlags = 0; + result->bufferOffset = 0; + result->packetComplete = packetComplete; + result->callContext = callbackCtx; + result->headerData = NULL; + memset(result->storeRemainLen, 0, 4 * sizeof(uint8_t)); + result->remainLenIndex = 0; + } + return result; +} + +void mqtt_codec_destroy(MQTTCODEC_HANDLE handle) +{ + /* Codes_SRS_MQTT_CODEC_07_003: [If the handle parameter is NULL then mqtt_codec_destroy shall do nothing.] */ + if (handle != NULL) + { + MQTTCODEC_INSTANCE* codecData = (MQTTCODEC_INSTANCE*)handle; + /* Codes_SRS_MQTT_CODEC_07_004: [mqtt_codec_destroy shall deallocate all memory that has been allocated by this object.] */ + BUFFER_delete(codecData->headerData); + free(codecData); + } +} + +BUFFER_HANDLE mqtt_codec_connect(const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) +{ + BUFFER_HANDLE result; + /* Codes_SRS_MQTT_CODEC_07_008: [If the parameters mqttOptions is NULL then mqtt_codec_connect shall return a null value.] */ + if (mqttOptions == NULL) + { + result = NULL; + } + else + { + /* Codes_SRS_MQTT_CODEC_07_009: [mqtt_codec_connect shall construct a BUFFER_HANDLE that represents a MQTT CONNECT packet.] */ + result = BUFFER_new(); + if (result != NULL) + { + STRING_HANDLE varible_header_log = NULL; + if (trace_log != NULL) + { + varible_header_log = STRING_new(); + } + // Add Variable Header Information + if (constructConnectVariableHeader(result, mqttOptions, varible_header_log) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (constructConnPayload(result, mqttOptions, varible_header_log) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_copy(trace_log, "CONNECT"); + } + if (constructFixedHeader(result, CONNECT_TYPE, 0) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_concat_with_STRING(trace_log, varible_header_log); + } + } + } + if (varible_header_log != NULL) + { + STRING_delete(varible_header_log); + } + } + } + } + return result; +} + +BUFFER_HANDLE mqtt_codec_disconnect() +{ + /* Codes_SRS_MQTT_CODEC_07_011: [On success mqtt_codec_disconnect shall construct a BUFFER_HANDLE that represents a MQTT DISCONNECT packet.] */ + BUFFER_HANDLE result = BUFFER_new(); + if (result != NULL) + { + if (BUFFER_enlarge(result, 2) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_012: [If any error is encountered mqtt_codec_disconnect shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + uint8_t* iterator = BUFFER_u_char(result); + if (iterator == NULL) + { + /* Codes_SRS_MQTT_CODEC_07_012: [If any error is encountered mqtt_codec_disconnect shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + iterator[0] = DISCONNECT_TYPE; + iterator[1] = 0; + } + } + } + return result; +} + +BUFFER_HANDLE mqtt_codec_publish(QOS_VALUE qosValue, bool duplicateMsg, bool serverRetain, uint16_t packetId, const char* topicName, const uint8_t* msgBuffer, size_t buffLen, STRING_HANDLE trace_log) +{ + BUFFER_HANDLE result; + /* Codes_SRS_MQTT_CODEC_07_005: [If the parameters topicName is NULL then mqtt_codec_publish shall return NULL.] */ + if (topicName == NULL) + { + result = NULL; + } + /* Codes_SRS_MQTT_CODEC_07_036: [mqtt_codec_publish shall return NULL if the buffLen variable is greater than the MAX_SEND_SIZE (0xFFFFFF7F).] */ + else if (buffLen > MAX_SEND_SIZE) + { + /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ + result = NULL; + } + else + { + PUBLISH_HEADER_INFO publishInfo ={ 0 }; + publishInfo.topicName = topicName; + publishInfo.packetId = packetId; + publishInfo.qualityOfServiceValue = qosValue; + + uint8_t headerFlags = 0; + if (duplicateMsg) headerFlags |= PUBLISH_DUP_FLAG; + if (serverRetain) headerFlags |= PUBLISH_QOS_RETAIN; + if (qosValue != DELIVER_AT_MOST_ONCE) + { + if (qosValue == DELIVER_AT_LEAST_ONCE) + { + headerFlags |= PUBLISH_QOS_AT_LEAST_ONCE; + } + else + { + headerFlags |= PUBLISH_QOS_EXACTLY_ONCE; + } + } + + /* Codes_SRS_MQTT_CODEC_07_007: [mqtt_codec_publish shall return a BUFFER_HANDLE that represents a MQTT PUBLISH message.] */ + result = BUFFER_new(); + if (result != NULL) + { + STRING_HANDLE varible_header_log = NULL; + if (trace_log != NULL) + { + varible_header_log = STRING_construct_sprintf(" | IS_DUP: %s | RETAIN: %d | QOS: %s", duplicateMsg ? TRUE_CONST : FALSE_CONST, + serverRetain ? 1 : 0, + retrieve_qos_value(publishInfo.qualityOfServiceValue) ); + } + + if (constructPublishVariableHeader(result, &publishInfo, varible_header_log) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + size_t payloadOffset = BUFFER_length(result); + if (buffLen > 0) + { + if (BUFFER_enlarge(result, buffLen) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + uint8_t* iterator = BUFFER_u_char(result); + if (iterator == NULL) + { + /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + iterator += payloadOffset; + // Write Message + (void)memcpy(iterator, msgBuffer, buffLen); + if (trace_log) + { + STRING_sprintf(varible_header_log, " | PAYLOAD_LEN: %lu", buffLen); + } + } + } + } + + if (result != NULL) + { + if (trace_log != NULL) + { + (void)STRING_copy(trace_log, "PUBLISH"); + } + if (constructFixedHeader(result, PUBLISH_TYPE, headerFlags) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_concat_with_STRING(trace_log, varible_header_log); + } + } + } + } + if (varible_header_log != NULL) + { + STRING_delete(varible_header_log); + } + } + } + return result; +} + +BUFFER_HANDLE mqtt_codec_publishAck(uint16_t packetId) +{ + /* Codes_SRS_MQTT_CODEC_07_013: [On success mqtt_codec_publishAck shall return a BUFFER_HANDLE representation of a MQTT PUBACK packet.] */ + /* Codes_SRS_MQTT_CODEC_07_014 : [If any error is encountered then mqtt_codec_publishAck shall return NULL.] */ + BUFFER_HANDLE result = constructPublishReply(PUBACK_TYPE, 0, packetId); + return result; +} + +BUFFER_HANDLE mqtt_codec_publishReceived(uint16_t packetId) +{ + /* Codes_SRS_MQTT_CODEC_07_015: [On success mqtt_codec_publishRecieved shall return a BUFFER_HANDLE representation of a MQTT PUBREC packet.] */ + /* Codes_SRS_MQTT_CODEC_07_016 : [If any error is encountered then mqtt_codec_publishRecieved shall return NULL.] */ + BUFFER_HANDLE result = constructPublishReply(PUBREC_TYPE, 0, packetId); + return result; +} + +BUFFER_HANDLE mqtt_codec_publishRelease(uint16_t packetId) +{ + /* Codes_SRS_MQTT_CODEC_07_017: [On success mqtt_codec_publishRelease shall return a BUFFER_HANDLE representation of a MQTT PUBREL packet.] */ + /* Codes_SRS_MQTT_CODEC_07_018 : [If any error is encountered then mqtt_codec_publishRelease shall return NULL.] */ + BUFFER_HANDLE result = constructPublishReply(PUBREL_TYPE, 2, packetId); + return result; +} + +BUFFER_HANDLE mqtt_codec_publishComplete(uint16_t packetId) +{ + /* Codes_SRS_MQTT_CODEC_07_019: [On success mqtt_codec_publishComplete shall return a BUFFER_HANDLE representation of a MQTT PUBCOMP packet.] */ + /* Codes_SRS_MQTT_CODEC_07_020 : [If any error is encountered then mqtt_codec_publishComplete shall return NULL.] */ + BUFFER_HANDLE result = constructPublishReply(PUBCOMP_TYPE, 0, packetId); + return result; +} + +BUFFER_HANDLE mqtt_codec_ping() +{ + /* Codes_SRS_MQTT_CODEC_07_021: [On success mqtt_codec_ping shall construct a BUFFER_HANDLE that represents a MQTT PINGREQ packet.] */ + BUFFER_HANDLE result = BUFFER_new(); + if (result != NULL) + { + if (BUFFER_enlarge(result, 2) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_022: [If any error is encountered mqtt_codec_ping shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + uint8_t* iterator = BUFFER_u_char(result); + if (iterator == NULL) + { + /* Codes_SRS_MQTT_CODEC_07_022: [If any error is encountered mqtt_codec_ping shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + iterator[0] = PINGREQ_TYPE; + iterator[1] = 0; + } + } + } + return result; +} + +BUFFER_HANDLE mqtt_codec_subscribe(uint16_t packetId, SUBSCRIBE_PAYLOAD* subscribeList, size_t count, STRING_HANDLE trace_log) +{ + BUFFER_HANDLE result; + /* Codes_SRS_MQTT_CODEC_07_023: [If the parameters subscribeList is NULL or if count is 0 then mqtt_codec_subscribe shall return NULL.] */ + if (subscribeList == NULL || count == 0) + { + result = NULL; + } + else + { + /* Codes_SRS_MQTT_CODEC_07_026: [mqtt_codec_subscribe shall return a BUFFER_HANDLE that represents a MQTT SUBSCRIBE message.]*/ + result = BUFFER_new(); + if (result != NULL) + { + if (constructSubscibeTypeVariableHeader(result, packetId) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + STRING_HANDLE sub_trace = NULL; + if (trace_log != NULL) + { + sub_trace = STRING_construct_sprintf(" | PACKET_ID: %"PRIu16, packetId); + } + /* Codes_SRS_MQTT_CODEC_07_024: [mqtt_codec_subscribe shall iterate through count items in the subscribeList.] */ + if (addListItemsToSubscribePacket(result, subscribeList, count, sub_trace) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + + if (trace_log != NULL) + { + STRING_concat(trace_log, "SUBSCRIBE"); + } + if (constructFixedHeader(result, SUBSCRIBE_TYPE, SUBSCRIBE_FIXED_HEADER_FLAG) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_concat_with_STRING(trace_log, sub_trace); + } + } + } + if (sub_trace != NULL) + { + STRING_delete(sub_trace); + } + } + } + } + return result; +} + +BUFFER_HANDLE mqtt_codec_unsubscribe(uint16_t packetId, const char** unsubscribeList, size_t count, STRING_HANDLE trace_log) +{ + BUFFER_HANDLE result; + /* Codes_SRS_MQTT_CODEC_07_027: [If the parameters unsubscribeList is NULL or if count is 0 then mqtt_codec_unsubscribe shall return NULL.] */ + if (unsubscribeList == NULL || count == 0) + { + result = NULL; + } + else + { + /* Codes_SRS_MQTT_CODEC_07_030: [mqtt_codec_unsubscribe shall return a BUFFER_HANDLE that represents a MQTT SUBSCRIBE message.] */ + result = BUFFER_new(); + if (result != NULL) + { + if (constructSubscibeTypeVariableHeader(result, packetId) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + STRING_HANDLE unsub_trace = NULL; + if (trace_log != NULL) + { + unsub_trace = STRING_construct_sprintf(" | PACKET_ID: %"PRIu16, packetId); + } + /* Codes_SRS_MQTT_CODEC_07_028: [mqtt_codec_unsubscribe shall iterate through count items in the unsubscribeList.] */ + if (addListItemsToUnsubscribePacket(result, unsubscribeList, count, unsub_trace) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_copy(trace_log, "UNSUBSCRIBE"); + } + if (constructFixedHeader(result, UNSUBSCRIBE_TYPE, UNSUBSCRIBE_FIXED_HEADER_FLAG) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ + BUFFER_delete(result); + result = NULL; + } + else + { + if (trace_log != NULL) + { + (void)STRING_concat_with_STRING(trace_log, unsub_trace); + } + } + } + if (unsub_trace != NULL) + { + STRING_delete(unsub_trace); + } + } + } + } + return result; +} + +int mqtt_codec_bytesReceived(MQTTCODEC_HANDLE handle, const unsigned char* buffer, size_t size) +{ + int result; + MQTTCODEC_INSTANCE* codec_Data = (MQTTCODEC_INSTANCE*)handle; + /* Codes_SRS_MQTT_CODEC_07_031: [If the parameters handle or buffer is NULL then mqtt_codec_bytesReceived shall return a non-zero value.] */ + if (codec_Data == NULL) + { + result = __FAILURE__; + } + /* Codes_SRS_MQTT_CODEC_07_031: [If the parameters handle or buffer is NULL then mqtt_codec_bytesReceived shall return a non-zero value.] */ + /* Codes_SRS_MQTT_CODEC_07_032: [If the parameters size is zero then mqtt_codec_bytesReceived shall return a non-zero value.] */ + else if (buffer == NULL || size == 0) + { + codec_Data->currPacket = PACKET_TYPE_ERROR; + result = __FAILURE__; + } + else + { + /* Codes_SRS_MQTT_CODEC_07_033: [mqtt_codec_bytesReceived constructs a sequence of bytes into the corresponding MQTT packets and on success returns zero.] */ + result = 0; + size_t index = 0; + for (index = 0; index < size && result == 0; index++) + { + uint8_t iterator = ((int8_t*)buffer)[index]; + if (codec_Data->codecState == CODEC_STATE_FIXED_HEADER) + { + if (codec_Data->currPacket == UNKNOWN_TYPE) + { + codec_Data->currPacket = processControlPacketType(iterator, &codec_Data->headerFlags); + } + else + { + if (prepareheaderDataInfo(codec_Data, iterator) != 0) + { + /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ + codec_Data->currPacket = PACKET_TYPE_ERROR; + result = __FAILURE__; + } + if (codec_Data->currPacket == PINGRESP_TYPE) + { + /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */ + completePacketData(codec_Data); + } + } + } + else if (codec_Data->codecState == CODEC_STATE_VAR_HEADER) + { + if (codec_Data->headerData == NULL) + { + codec_Data->codecState = CODEC_STATE_PAYLOAD; + } + else + { + uint8_t* dataBytes = BUFFER_u_char(codec_Data->headerData); + if (dataBytes == NULL) + { + /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ + codec_Data->currPacket = PACKET_TYPE_ERROR; + result = __FAILURE__; + } + else + { + // Increment the data + dataBytes += codec_Data->bufferOffset++; + *dataBytes = iterator; + + size_t totalLen = BUFFER_length(codec_Data->headerData); + if (codec_Data->bufferOffset >= totalLen) + { + /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */ + completePacketData(codec_Data); + } + } + } + } + else + { + /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ + codec_Data->currPacket = PACKET_TYPE_ERROR; + result = __FAILURE__; + } + } + } + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/umqtt/src/mqtt_message.c Thu Aug 23 06:52:14 2018 +0000 @@ -0,0 +1,323 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#include "azure_umqtt_c/mqtt_message.h" +#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" + +typedef struct MQTT_MESSAGE_TAG +{ + uint16_t packetId; + QOS_VALUE qosInfo; + + char* topicName; + APP_PAYLOAD appPayload; + + const char* const_topic_name; + APP_PAYLOAD const_payload; + + bool isDuplicateMsg; + bool isMessageRetained; +} MQTT_MESSAGE; + +MQTT_MESSAGE_HANDLE mqttmessage_create_in_place(uint16_t packetId, const char* topicName, QOS_VALUE qosValue, const uint8_t* appMsg, size_t appMsgLength) +{ + /* Codes_SRS_MQTTMESSAGE_07_026: [If the parameters topicName is NULL then mqttmessage_create_in_place shall return NULL.].] */ + MQTT_MESSAGE* result; + if (topicName == NULL) + { + LogError("Invalid Parameter topicName: %p, packetId: %d.", topicName, packetId); + result = NULL; + } + else + { + result = malloc(sizeof(MQTT_MESSAGE)); + if (result != NULL) + { + memset(result, 0, sizeof(MQTT_MESSAGE) ); + result->const_topic_name = topicName; + + result->packetId = packetId; + result->isDuplicateMsg = false; + result->isMessageRetained = false; + result->qosInfo = qosValue; + + /* Codes_SRS_MQTTMESSAGE_07_027: [mqttmessage_create_in_place shall use the a pointer to topicName or appMsg .] */ + result->const_payload.length = appMsgLength; + if (result->const_payload.length > 0) + { + result->const_payload.message = (uint8_t*)appMsg; + } + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_028: [If any memory allocation fails mqttmessage_create_in_place shall free any allocated memory and return NULL.] */ + LogError("Failure unable to allocate MQTT Message."); + } + } + /* Codes_SRS_MQTTMESSAGE_07_029: [ Upon success, mqttmessage_create_in_place shall return a NON-NULL MQTT_MESSAGE_HANDLE value.] */ + return (MQTT_MESSAGE_HANDLE)result; +} + +MQTT_MESSAGE_HANDLE mqttmessage_create(uint16_t packetId, const char* topicName, QOS_VALUE qosValue, const uint8_t* appMsg, size_t appMsgLength) +{ + /* Codes_SRS_MQTTMESSAGE_07_001:[If the parameters topicName is NULL is zero then mqttmessage_create shall return NULL.] */ + MQTT_MESSAGE* result; + if (topicName == NULL) + { + LogError("Invalid Parameter topicName: %p, packetId: %d.", topicName, packetId); + result = NULL; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_002: [mqttmessage_create shall allocate and copy the topicName and appMsg parameters.] */ + result = malloc(sizeof(MQTT_MESSAGE)); + if (result != NULL) + { + memset(result, 0, sizeof(MQTT_MESSAGE)); + if (mallocAndStrcpy_s(&result->topicName, topicName) != 0) + { + /* Codes_SRS_MQTTMESSAGE_07_003: [If any memory allocation fails mqttmessage_create shall free any allocated memory and return NULL.] */ + LogError("Failure allocating topic name"); + free(result); + result = NULL; + } + else + { + result->packetId = packetId; + result->isDuplicateMsg = false; + result->isMessageRetained = false; + result->qosInfo = qosValue; + + /* Codes_SRS_MQTTMESSAGE_07_002: [mqttmessage_create shall allocate and copy the topicName and appMsg parameters.] */ + result->appPayload.length = appMsgLength; + if (result->appPayload.length > 0) + { + result->appPayload.message = malloc(appMsgLength); + if (result->appPayload.message == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_003: [If any memory allocation fails mqttmessage_create shall free any allocated memory and return NULL.] */ + LogError("Failure allocating message value of %uz", appMsgLength); + free(result->topicName); + free(result); + result = NULL; + } + else + { + (void)memcpy(result->appPayload.message, appMsg, appMsgLength); + } + } + else + { + result->appPayload.message = NULL; + } + } + } + } + /* Codes_SRS_MQTTMESSAGE_07_004: [If mqttmessage_createMessage succeeds the it shall return a NON-NULL MQTT_MESSAGE_HANDLE value.] */ + return (MQTT_MESSAGE_HANDLE)result; +} + +void mqttmessage_destroy(MQTT_MESSAGE_HANDLE handle) +{ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + /* Codes_SRS_MQTTMESSAGE_07_005: [If the handle parameter is NULL then mqttmessage_destroyMessage shall do nothing] */ + if (msgInfo != NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_006: [mqttmessage_destroyMessage shall free all resources associated with the MQTT_MESSAGE_HANDLE value] */ + if (msgInfo->topicName != NULL) + { + free(msgInfo->topicName); + } + if (msgInfo->appPayload.message != NULL) + { + free(msgInfo->appPayload.message); + } + free(msgInfo); + } +} + +MQTT_MESSAGE_HANDLE mqttmessage_clone(MQTT_MESSAGE_HANDLE handle) +{ + MQTT_MESSAGE_HANDLE result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_007: [If handle parameter is NULL then mqttmessage_clone shall return NULL.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = NULL; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_008: [mqttmessage_clone shall create a new MQTT_MESSAGE_HANDLE with data content identical of the handle value.] */ + MQTT_MESSAGE* mqtt_message = (MQTT_MESSAGE*)handle; + result = mqttmessage_create(mqtt_message->packetId, mqtt_message->topicName, mqtt_message->qosInfo, mqtt_message->appPayload.message, mqtt_message->appPayload.length); + if (result != NULL) + { + (void)mqttmessage_setIsDuplicateMsg(result, mqtt_message->isDuplicateMsg); + (void)mqttmessage_setIsRetained(result, mqtt_message->isMessageRetained); + } + } + return result; +} + +uint16_t mqttmessage_getPacketId(MQTT_MESSAGE_HANDLE handle) +{ + uint16_t result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_010: [If handle is NULL then mqttmessage_getPacketId shall return 0.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = 0; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_011: [mqttmessage_getPacketId shall return the packetId value contained in MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + result = msgInfo->packetId; + } + return result; +} + +const char* mqttmessage_getTopicName(MQTT_MESSAGE_HANDLE handle) +{ + const char* result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_012: [If handle is NULL then mqttmessage_getTopicName shall return a NULL string.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = NULL; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_013: [mqttmessage_getTopicName shall return the topicName contained in MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + if (msgInfo->topicName == NULL) + { + result = msgInfo->const_topic_name; + } + else + { + result = msgInfo->topicName; + } + } + return result; +} + +QOS_VALUE mqttmessage_getQosType(MQTT_MESSAGE_HANDLE handle) +{ + QOS_VALUE result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_014: [If handle is NULL then mqttmessage_getQosType shall return the default DELIVER_AT_MOST_ONCE value.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = DELIVER_AT_MOST_ONCE; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_015: [mqttmessage_getQosType shall return the QOS Type value contained in MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + result = msgInfo->qosInfo; + } + return result; +} + +bool mqttmessage_getIsDuplicateMsg(MQTT_MESSAGE_HANDLE handle) +{ + bool result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_016: [If handle is NULL then mqttmessage_getIsDuplicateMsg shall return false.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = false; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_017: [mqttmessage_getIsDuplicateMsg shall return the isDuplicateMsg value contained in MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + result = msgInfo->isDuplicateMsg; + } + return result; +} + +bool mqttmessage_getIsRetained(MQTT_MESSAGE_HANDLE handle) +{ + bool result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_018: [If handle is NULL then mqttmessage_getIsRetained shall return false.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = false; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_019: [mqttmessage_getIsRetained shall return the isRetained value contained in MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + result = msgInfo->isMessageRetained; + } + return result; +} + +int mqttmessage_setIsDuplicateMsg(MQTT_MESSAGE_HANDLE handle, bool duplicateMsg) +{ + int result; + /* Codes_SRS_MQTTMESSAGE_07_022: [If handle is NULL then mqttmessage_setIsDuplicateMsg shall return a non-zero value.] */ + if (handle == NULL) + { + LogError("Invalid Parameter handle: %p.", handle); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_023: [mqttmessage_setIsDuplicateMsg shall store the duplicateMsg value in the MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + msgInfo->isDuplicateMsg = duplicateMsg; + result = 0; + } + return result; +} + +int mqttmessage_setIsRetained(MQTT_MESSAGE_HANDLE handle, bool retainMsg) +{ + int result; + /* Codes_SRS_MQTTMESSAGE_07_024: [If handle is NULL then mqttmessage_setIsRetained shall return a non-zero value.] */ + if (handle == NULL) + { + LogError("Invalid Parameter handle: %p.", handle); + result = __FAILURE__; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_025: [mqttmessage_setIsRetained shall store the retainMsg value in the MQTT_MESSAGE_HANDLE handle.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + msgInfo->isMessageRetained = retainMsg; + result = 0; + } + return result; +} + +const APP_PAYLOAD* mqttmessage_getApplicationMsg(MQTT_MESSAGE_HANDLE handle) +{ + const APP_PAYLOAD* result; + if (handle == NULL) + { + /* Codes_SRS_MQTTMESSAGE_07_020: [If handle is NULL or if msgLen is 0 then mqttmessage_getApplicationMsg shall return NULL.] */ + LogError("Invalid Parameter handle: %p.", handle); + result = NULL; + } + else + { + /* Codes_SRS_MQTTMESSAGE_07_021: [mqttmessage_getApplicationMsg shall return the applicationMsg value contained in MQTT_MESSAGE_HANDLE handle and the length of the appMsg in the msgLen parameter.] */ + MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; + if (msgInfo->const_payload.length > 0) + { + result = &msgInfo->const_payload; + } + else + { + result = &msgInfo->appPayload; + } + } + return result; +}