Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: iothub_client/src/iothub_client_ll_uploadtoblob.c
- Revision:
- 0:f7f1f0d76dd6
--- /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*/ + +