Mark Radbourne / Mbed 2 deprecated FXOS8700CQ_To_Azure_IoT

Dependencies:   azure_umqtt_c iothub_mqtt_transport mbed-rtos mbed wolfSSL Socket lwip-eth lwip-sys lwip

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers iothub_client_ll.c Source File

iothub_client_ll.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 #include <stdlib.h>
00005 #ifdef _CRTDBG_MAP_ALLOC
00006 #include <crtdbg.h>
00007 #endif
00008 #include <string.h>
00009 #include "azure_c_shared_utility/gballoc.h"
00010 #include "azure_c_shared_utility/string_tokenizer.h"
00011 #include "azure_c_shared_utility/doublylinkedlist.h"
00012 #include "azure_c_shared_utility/xlogging.h"
00013 #include "azure_c_shared_utility/tickcounter.h"
00014 #include "azure_c_shared_utility/constbuffer.h"
00015 
00016 #include "iothub_client_ll.h"
00017 #include "iothub_client_private.h"
00018 #include "iothub_client_version.h"
00019 #include "iothub_transport_ll.h"
00020 #include <stdint.h>
00021 
00022 #ifndef DONT_USE_UPLOADTOBLOB
00023 #include "iothub_client_ll_uploadtoblob.h"
00024 #endif
00025 
00026 #define LOG_ERROR_RESULT LogError("result = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result));
00027 #define INDEFINITE_TIME ((time_t)(-1))
00028 
00029 DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES);
00030 DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES);
00031 
00032 typedef struct IOTHUB_CLIENT_LL_HANDLE_DATA_TAG
00033 {
00034     DLIST_ENTRY waitingToSend;
00035     DLIST_ENTRY iot_msg_queue;
00036     DLIST_ENTRY iot_ack_queue;
00037     TRANSPORT_LL_HANDLE transportHandle;
00038     bool isSharedTransport;
00039     IOTHUB_DEVICE_HANDLE deviceHandle;
00040     TRANSPORT_PROVIDER_FIELDS;
00041     IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback;
00042     void* messageUserContextCallback;
00043     IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK conStatusCallback;
00044     void* conStatusUserContextCallback;
00045     time_t lastMessageReceiveTime;
00046     TICK_COUNTER_HANDLE tickCounter; /*shared tickcounter used to track message timeouts in waitingToSend list*/
00047     uint64_t currentMessageTimeout;
00048     uint64_t current_device_twin_timeout;
00049     IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback;
00050     void* deviceTwinContextCallback;
00051     IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback;
00052     void* deviceMethodUserContextCallback;
00053     IOTHUB_CLIENT_RETRY_POLICY retryPolicy;
00054     size_t retryTimeoutLimitInSeconds;
00055 #ifndef DONT_USE_UPLOADTOBLOB
00056     IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE uploadToBlobHandle;
00057 #endif
00058     uint32_t data_msg_id;
00059     bool complete_twin_update_encountered;
00060 }IOTHUB_CLIENT_LL_HANDLE_DATA;
00061 
00062 static const char HOSTNAME_TOKEN[] = "HostName";
00063 static const char DEVICEID_TOKEN[] = "DeviceId";
00064 static const char X509_TOKEN[] = "x509";
00065 static const char X509_TOKEN_ONLY_ACCEPTABLE_VALUE[] = "true";
00066 static const char DEVICEKEY_TOKEN[] = "SharedAccessKey";
00067 static const char DEVICESAS_TOKEN[] = "SharedAccessSignature";
00068 static const char PROTOCOL_GATEWAY_HOST[] = "GatewayHostName";
00069 
00070 static void device_twin_data_destroy(IOTHUB_DEVICE_TWIN* client_item)
00071 {
00072     CONSTBUFFER_Destroy(client_item->report_data_handle);
00073     free(client_item);
00074 }
00075 
00076 static uint32_t get_next_item_id(IOTHUB_CLIENT_LL_HANDLE_DATA* handleData)
00077 {    
00078     if (handleData->data_msg_id+1 >= UINT32_MAX)
00079     {
00080         handleData->data_msg_id = 1;
00081     }
00082     else
00083     {
00084         handleData->data_msg_id++;
00085     }
00086     return handleData->data_msg_id;
00087 }
00088 
00089 static IOTHUB_DEVICE_TWIN* dev_twin_data_create(IOTHUB_CLIENT_LL_HANDLE_DATA* handleData, uint32_t id, const unsigned char* reportedState, size_t size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback)
00090 {
00091     IOTHUB_DEVICE_TWIN* result = (IOTHUB_DEVICE_TWIN*)malloc(sizeof(IOTHUB_DEVICE_TWIN) );
00092     if (result != NULL)
00093     {
00094         result->report_data_handle = CONSTBUFFER_Create(reportedState, size);
00095         if (result->report_data_handle == NULL)
00096         {
00097             LogError("Failure allocating reported state data");
00098             free(result);
00099             result = NULL;
00100         }
00101         else if (tickcounter_get_current_ms(handleData->tickCounter, &result->ms_timesOutAfter) != 0)
00102         {
00103             LogError("Failure getting tickcount info");
00104             CONSTBUFFER_Destroy(result->report_data_handle);
00105             free(result);
00106             result = NULL;
00107         }
00108         else
00109         {
00110             result->item_id = id;
00111             result->ms_timesOutAfter = 0;
00112             result->context = userContextCallback;
00113             result->reported_state_callback = reportedStateCallback;
00114         }
00115     }
00116     else
00117     {
00118         LogError("Failure allocating device twin information");
00119     }
00120     return result;
00121 }
00122 
00123 IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol)
00124 {
00125     IOTHUB_CLIENT_LL_HANDLE result;
00126 
00127     /*Codes_SRS_IOTHUBCLIENT_LL_05_001: [IoTHubClient_LL_CreateFromConnectionString shall obtain the version string by a call to IoTHubClient_GetVersionString.]*/
00128     /*Codes_SRS_IOTHUBCLIENT_LL_05_002: [IoTHubClient_LL_CreateFromConnectionString shall print the version string to standard output.]*/
00129     LogInfo("IoT Hub SDK for C, version %s", IoTHubClient_GetVersionString());
00130 
00131     /* Codes_SRS_IOTHUBCLIENT_LL_12_003: [IoTHubClient_LL_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL] */
00132     if (connectionString == NULL)
00133     {
00134         LogError("Input parameter is NULL: connectionString");
00135         result = NULL;
00136     }
00137     else if (protocol == NULL)
00138     {
00139         LogError("Input parameter is NULL: protocol");
00140         result = NULL;
00141     }
00142     else
00143     {
00144         /* Codes_SRS_IOTHUBCLIENT_LL_12_004: [IoTHubClient_LL_CreateFromConnectionString shall allocate IOTHUB_CLIENT_CONFIG structure] */
00145         IOTHUB_CLIENT_CONFIG* config = (IOTHUB_CLIENT_CONFIG*) malloc(sizeof(IOTHUB_CLIENT_CONFIG));
00146         if (config == NULL)
00147         {
00148             /* Codes_SRS_IOTHUBCLIENT_LL_12_012: [If the allocation failed IoTHubClient_LL_CreateFromConnectionString  returns NULL]  */
00149             LogError("Malloc failed");
00150             result = NULL;
00151         }
00152         else
00153         {
00154             STRING_TOKENIZER_HANDLE tokenizer1 = NULL;
00155             STRING_HANDLE connString = NULL;
00156             STRING_HANDLE tokenString = NULL;
00157             STRING_HANDLE valueString = NULL;
00158             STRING_HANDLE hostNameString = NULL;
00159             STRING_HANDLE hostSuffixString = NULL;
00160             STRING_HANDLE deviceIdString = NULL;
00161             STRING_HANDLE deviceKeyString = NULL;
00162             STRING_HANDLE deviceSasTokenString = NULL;
00163             STRING_HANDLE protocolGateway = NULL;
00164 
00165             config->protocol = protocol;
00166 
00167             config->iotHubName = NULL;
00168             config->iotHubSuffix = NULL;
00169             config->deviceId = NULL;
00170             config->deviceKey = NULL;
00171             config->deviceSasToken = NULL;
00172 
00173             /* Codes_SRS_IOTHUBCLIENT_LL_04_002: [If it does not, it shall pass the protocolGatewayHostName NULL.] */
00174             config->protocolGatewayHostName = NULL;
00175 
00176             if ((connString = STRING_construct(connectionString)) == NULL)
00177             {
00178                 LogError("Error constructing connectiong String");
00179                 result = NULL;
00180             }
00181             else if ((tokenizer1 = STRING_TOKENIZER_create(connString)) == NULL)
00182             {
00183                 LogError("Error creating Tokenizer");
00184                 result = NULL;
00185             }
00186             else if ((tokenString = STRING_new()) == NULL)
00187             {
00188                 LogError("Error creating Token String");
00189                 result = NULL;
00190             }
00191             else if ((valueString = STRING_new()) == NULL)
00192             {
00193                 LogError("Error creating Value String");
00194                 result = NULL;
00195             }
00196             else if ((hostNameString = STRING_new()) == NULL)
00197             {
00198                 LogError("Error creating HostName String");
00199                 result = NULL;
00200             }
00201             else if ((hostSuffixString = STRING_new()) == NULL)
00202             {
00203                 LogError("Error creating HostSuffix String");
00204                 result = NULL;
00205             }
00206             /* Codes_SRS_IOTHUBCLIENT_LL_12_005: [IoTHubClient_LL_CreateFromConnectionString shall try to parse the connectionString input parameter for the following structure: "Key1=value1;key2=value2;key3=value3..."] */
00207             /* Codes_SRS_IOTHUBCLIENT_LL_12_006: [IoTHubClient_LL_CreateFromConnectionString shall verify the existence of the following Key/Value pairs in the connection string: HostName, DeviceId, SharedAccessKey, SharedAccessSignature or x509]  */
00208             else
00209             {
00210                 int isx509found = 0;
00211                 while ((STRING_TOKENIZER_get_next_token(tokenizer1, tokenString, "=") == 0))
00212                 {
00213                     if (STRING_TOKENIZER_get_next_token(tokenizer1, valueString, ";") != 0)
00214                     {
00215                         LogError("Tokenizer error");
00216                         break;
00217                     }
00218                     else
00219                     {
00220                         if (tokenString != NULL)
00221                         {
00222                             /* Codes_SRS_IOTHUBCLIENT_LL_12_010: [IoTHubClient_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] */
00223                             const char* s_token = STRING_c_str(tokenString);
00224                             if (strcmp(s_token, HOSTNAME_TOKEN) == 0)
00225                             {
00226                                 /* Codes_SRS_IOTHUBCLIENT_LL_12_009: [IoTHubClient_LL_CreateFromConnectionString shall split the value of HostName to Name and Suffix using the first "." as a separator] */
00227                                 STRING_TOKENIZER_HANDLE tokenizer2 = NULL;
00228                                 if ((tokenizer2 = STRING_TOKENIZER_create(valueString)) == NULL)
00229                                 {
00230                                     LogError("Error creating Tokenizer");
00231                                     break;
00232                                 }
00233                                 else
00234                                 {
00235                                     /* Codes_SRS_IOTHUBCLIENT_LL_12_015: [If the string split failed, IoTHubClient_LL_CreateFromConnectionString returns NULL ] */
00236                                     if (STRING_TOKENIZER_get_next_token(tokenizer2, hostNameString, ".") != 0)
00237                                     {
00238                                         LogError("Tokenizer error");
00239                                         STRING_TOKENIZER_destroy(tokenizer2);
00240                                         break;
00241                                     }
00242                                     else
00243                                     {
00244                                         config->iotHubName = STRING_c_str(hostNameString);
00245                                         if (STRING_TOKENIZER_get_next_token(tokenizer2, hostSuffixString, ";") != 0)
00246                                         {
00247                                             LogError("Tokenizer error");
00248                                             STRING_TOKENIZER_destroy(tokenizer2);
00249                                             break;
00250                                         }
00251                                         else
00252                                         {
00253                                             config->iotHubSuffix = STRING_c_str(hostSuffixString);
00254                                         }
00255                                     }
00256                                     STRING_TOKENIZER_destroy(tokenizer2);
00257                                 }
00258                             }
00259                             else if (strcmp(s_token, DEVICEID_TOKEN) == 0)
00260                             {
00261                                 deviceIdString = STRING_clone(valueString);
00262                                 if (deviceIdString != NULL)
00263                                 {
00264                                     config->deviceId = STRING_c_str(deviceIdString);
00265                                 }
00266                                 else
00267                                 {
00268                                     LogError("Failure cloning device id string");
00269                                     break;
00270                                 }
00271                             }
00272                             else if (strcmp(s_token, DEVICEKEY_TOKEN) == 0)
00273                             {
00274                                 deviceKeyString = STRING_clone(valueString);
00275                                 if (deviceKeyString != NULL)
00276                                 {
00277                                     config->deviceKey = STRING_c_str(deviceKeyString);
00278                                 }
00279                                 else
00280                                 {
00281                                     LogError("Failure cloning device key string");
00282                                     break;
00283                                 }
00284                             }
00285                             else if (strcmp(s_token, DEVICESAS_TOKEN) == 0)
00286                             {
00287                                 deviceSasTokenString = STRING_clone(valueString);
00288                                 if (deviceSasTokenString != NULL)
00289                                 {
00290                                     config->deviceSasToken = STRING_c_str(deviceSasTokenString);
00291                                 }
00292                                 else
00293                                 {
00294                                     LogError("Failure cloning device sasToken string");
00295                                     break;
00296                                 }
00297                             }
00298                             else if (strcmp(s_token, X509_TOKEN) == 0)
00299                             {
00300                                 if (strcmp(STRING_c_str(valueString), X509_TOKEN_ONLY_ACCEPTABLE_VALUE) != 0)
00301                                 {
00302                                     LogError("x509 option has wrong value, the only acceptable one is \"true\"");
00303                                     break;
00304                                 }
00305                                 else
00306                                 {
00307                                     isx509found = 1;
00308                                 }
00309                             }
00310                             /* Codes_SRS_IOTHUBCLIENT_LL_04_001: [IoTHubClient_LL_CreateFromConnectionString shall verify the existence of key/value pair GatewayHostName. If it does exist it shall pass the value to IoTHubClient_LL_Create API.] */
00311                             else if (strcmp(s_token, PROTOCOL_GATEWAY_HOST) == 0)
00312                             {
00313                                 protocolGateway = STRING_clone(valueString);
00314                                 if (protocolGateway != NULL)
00315                                 {
00316                                     config->protocolGatewayHostName = STRING_c_str(protocolGateway);
00317                                 }
00318                                 else
00319                                 {
00320                                     LogError("Failure cloning protocol Gateway Name");
00321                                     break;
00322                                 }
00323                             }
00324                         }
00325                     }
00326                 }
00327                 /* parsing is done - check the result */
00328                 if (config->iotHubName == NULL)
00329                 {
00330                     LogError("iotHubName is not found");
00331                     result = NULL;
00332                 }
00333                 else if (config->iotHubSuffix == NULL)
00334                 {
00335                     LogError("iotHubSuffix is not found");
00336                     result = NULL;
00337                 }
00338                 else if (config->deviceId == NULL)
00339                 {
00340                     LogError("deviceId is not found");
00341                     result = NULL;
00342                 }
00343                 else if (!(
00344                     ((!isx509found) && (config->deviceSasToken == NULL) ^ (config->deviceKey == NULL)) ||
00345                     ((isx509found) && (config->deviceSasToken == NULL) && (config->deviceKey == NULL))
00346                     ))
00347                 {
00348                     LogError("invalid combination of x509, deviceSasToken and deviceKey");
00349                     result = NULL;
00350                 }
00351                 else
00352                 {
00353                     /* Codes_SRS_IOTHUBCLIENT_LL_12_011: [IoTHubClient_LL_CreateFromConnectionString shall call into the IoTHubClient_LL_Create API with the current structure and returns with the return value of it] */
00354                     
00355                     result = IoTHubClient_LL_Create(config);
00356                     if (result == NULL)
00357                     {
00358                         LogError("IoTHubClient_LL_Create failed");
00359                     }
00360                     else
00361                     {
00362                         /*return as is*/
00363                     }
00364                 }
00365             }
00366             if (deviceSasTokenString != NULL)
00367                 STRING_delete(deviceSasTokenString);
00368             if (deviceKeyString != NULL)
00369                 STRING_delete(deviceKeyString);
00370             if (deviceIdString != NULL)
00371                 STRING_delete(deviceIdString);
00372             if (hostSuffixString != NULL)
00373                 STRING_delete(hostSuffixString);
00374             if (hostNameString != NULL)
00375                 STRING_delete(hostNameString);
00376             if (valueString != NULL)
00377                 STRING_delete(valueString);
00378             if (tokenString != NULL)
00379                 STRING_delete(tokenString);
00380             if (connString != NULL)
00381                 STRING_delete(connString);
00382             if (protocolGateway != NULL)
00383                 STRING_delete(protocolGateway);
00384 
00385             if (tokenizer1 != NULL)
00386                 STRING_TOKENIZER_destroy(tokenizer1);
00387 
00388             free(config);
00389         }
00390     }
00391     return result;
00392 }
00393 
00394 static void setTransportProtocol(IOTHUB_CLIENT_LL_HANDLE_DATA* handleData, TRANSPORT_PROVIDER* protocol)
00395 {
00396     handleData->IoTHubTransport_GetHostname = protocol->IoTHubTransport_GetHostname;
00397     handleData->IoTHubTransport_SetOption = protocol->IoTHubTransport_SetOption;
00398     handleData->IoTHubTransport_Create = protocol->IoTHubTransport_Create;
00399     handleData->IoTHubTransport_Destroy = protocol->IoTHubTransport_Destroy;
00400     handleData->IoTHubTransport_Register = protocol->IoTHubTransport_Register;
00401     handleData->IoTHubTransport_Unregister = protocol->IoTHubTransport_Unregister;
00402     handleData->IoTHubTransport_Subscribe = protocol->IoTHubTransport_Subscribe;
00403     handleData->IoTHubTransport_Unsubscribe = protocol->IoTHubTransport_Unsubscribe;
00404     handleData->IoTHubTransport_DoWork = protocol->IoTHubTransport_DoWork;
00405     handleData->IoTHubTransport_SetRetryPolicy = protocol->IoTHubTransport_SetRetryPolicy;
00406     handleData->IoTHubTransport_GetSendStatus = protocol->IoTHubTransport_GetSendStatus;
00407     handleData->IoTHubTransport_ProcessItem = protocol->IoTHubTransport_ProcessItem;
00408     handleData->IoTHubTransport_Subscribe_DeviceTwin = protocol->IoTHubTransport_Subscribe_DeviceTwin;
00409     handleData->IoTHubTransport_Unsubscribe_DeviceTwin = protocol->IoTHubTransport_Unsubscribe_DeviceTwin;
00410     handleData->IoTHubTransport_Subscribe_DeviceMethod = protocol->IoTHubTransport_Subscribe_DeviceMethod;
00411     handleData->IoTHubTransport_Unsubscribe_DeviceMethod = protocol->IoTHubTransport_Unsubscribe_DeviceMethod;
00412 }
00413 
00414 IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_Create(const IOTHUB_CLIENT_CONFIG* config)
00415 {
00416     IOTHUB_CLIENT_LL_HANDLE result;
00417     /*Codes_SRS_IOTHUBCLIENT_LL_02_001: [IoTHubClient_LL_Create shall return NULL if config parameter is NULL or protocol field is NULL.]*/
00418     if( 
00419         (config == NULL) ||
00420         (config->protocol == NULL)
00421         )
00422     {
00423         result = NULL;
00424         LogError("invalid configuration (NULL detected)");
00425     }
00426     else
00427     {
00428         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)malloc(sizeof(IOTHUB_CLIENT_LL_HANDLE_DATA));
00429         if (handleData == NULL)
00430         {
00431             LogError("malloc failed");
00432             result = NULL;
00433         }
00434         else
00435         {
00436 #ifndef DONT_USE_UPLOADTOBLOB
00437             /*Codes_SRS_IOTHUBCLIENT_LL_02_094: [ IoTHubClient_LL_Create shall create a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE from IOTHUB_CLIENT_CONFIG. ]*/
00438             /*Codes_SRS_IOTHUBCLIENT_LL_02_092: [ IoTHubClient_LL_CreateFromConnectionString shall create a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE from IOTHUB_CLIENT_CONFIG. ]*/
00439             handleData->uploadToBlobHandle = IoTHubClient_LL_UploadToBlob_Create(config);
00440             if (handleData->uploadToBlobHandle == NULL)
00441             {
00442                 /*Codes_SRS_IOTHUBCLIENT_LL_02_093: [ If creating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClient_LL_CreateFromConnectionString shall fail and return NULL. ]*/
00443                 /*Codes_SRS_IOTHUBCLIENT_LL_02_095: [ If creating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClient_LL_Create shall fail and return NULL. ]*/
00444                 LogError("unable to IoTHubClient_LL_UploadToBlob_Create");
00445                 free(handleData);
00446                 result = NULL;
00447             }
00448             else
00449 #endif
00450             {
00451                 /*Codes_SRS_IOTHUBCLIENT_LL_02_045: [ Otherwise IoTHubClient_LL_Create shall create a new TICK_COUNTER_HANDLE ]*/
00452                 if ((handleData->tickCounter = tickcounter_create()) == NULL)
00453                 {
00454 #ifndef DONT_USE_UPLOADTOBLOB
00455                     /*Codes_SRS_IOTHUBCLIENT_LL_02_046: [ If creating the TICK_COUNTER_HANDLE fails then IoTHubClient_LL_Create shall fail and return NULL. ]*/
00456                     IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00457 #endif
00458                     LogError("unable to get a tickcounter");
00459                     free(handleData);
00460                     result = NULL;
00461                 }
00462                 else
00463                 {
00464                     /*Codes_SRS_IOTHUBCLIENT_LL_02_004: [Otherwise IoTHubClient_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*.]*/
00465                     IOTHUBTRANSPORT_CONFIG lowerLayerConfig;
00466                     DList_InitializeListHead(&(handleData->waitingToSend));
00467                     DList_InitializeListHead(&(handleData->iot_msg_queue));
00468                     DList_InitializeListHead(&(handleData->iot_ack_queue));
00469                     setTransportProtocol(handleData, (TRANSPORT_PROVIDER*)config->protocol());
00470                     handleData->messageCallback = NULL;
00471                     handleData->messageUserContextCallback = NULL;
00472                     handleData->deviceTwinCallback = NULL;
00473                     handleData->deviceTwinContextCallback = NULL;
00474                     handleData->deviceMethodCallback = NULL;
00475                     handleData->deviceMethodUserContextCallback = NULL;
00476                     handleData->lastMessageReceiveTime = INDEFINITE_TIME;
00477                     handleData->data_msg_id = 1;
00478                     handleData->complete_twin_update_encountered = false;
00479                     handleData->conStatusCallback = NULL;
00480                     handleData->conStatusUserContextCallback = NULL;
00481                     handleData->lastMessageReceiveTime = INDEFINITE_TIME;
00482 
00483                     /*Codes_SRS_IOTHUBCLIENT_LL_02_006: [IoTHubClient_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.]*/
00484                     lowerLayerConfig.upperConfig = config;
00485                     lowerLayerConfig.waitingToSend = &(handleData->waitingToSend);
00486                     /*Codes_SRS_IOTHUBCLIENT_LL_02_007: [If the underlaying layer _Create function fails them IoTHubClient_LL_Create shall fail and return NULL.] */
00487                     if ((handleData->transportHandle = handleData->IoTHubTransport_Create(&lowerLayerConfig)) == NULL)
00488                     {
00489                         LogError("underlying transport failed");
00490 #ifndef DONT_USE_UPLOADTOBLOB
00491                         IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00492 #endif
00493                         tickcounter_destroy(handleData->tickCounter);
00494                         free(handleData);
00495                         result = NULL;
00496                     }
00497                     else
00498                     {
00499                         IOTHUB_DEVICE_CONFIG deviceConfig;
00500 
00501                         deviceConfig.deviceId = config->deviceId;
00502                         deviceConfig.deviceKey = config->deviceKey;
00503                         deviceConfig.deviceSasToken = config->deviceSasToken;
00504 
00505                         /*Codes_SRS_IOTHUBCLIENT_LL_17_008: [IoTHubClient_LL_Create shall call the transport _Register function with a populated structure of type IOTHUB_DEVICE_CONFIG and waitingToSend list.] */
00506                         if ((handleData->deviceHandle = handleData->IoTHubTransport_Register(handleData->transportHandle, &deviceConfig, handleData, &(handleData->waitingToSend))) == NULL)
00507                         {
00508                             /*Codes_SRS_IOTHUBCLIENT_LL_17_009: [If the _Register function fails, this function shall fail and return NULL.]*/
00509                             LogError("Registering device in transport failed");
00510                             handleData->IoTHubTransport_Destroy(handleData->transportHandle);
00511 #ifndef DONT_USE_UPLOADTOBLOB
00512                             IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00513 #endif
00514                             tickcounter_destroy(handleData->tickCounter);
00515                             free(handleData);
00516                             result = NULL;
00517                         }
00518                         else
00519                         {
00520                             /*Codes_SRS_IOTHUBCLIENT_LL_02_008: [Otherwise, IoTHubClient_LL_Create shall succeed and return a non-NULL handle.] */
00521                             handleData->isSharedTransport = false;
00522                             /*Codes_SRS_IOTHUBCLIENT_LL_02_042: [ By default, messages shall not timeout. ]*/
00523                             handleData->currentMessageTimeout = 0;
00524                             handleData->current_device_twin_timeout = 0;
00525                             result = handleData;
00526                             /*Codes_SRS_IOTHUBCLIENT_LL_25_124: [ `IoTHubClient_LL_Create` shall set the default retry policy as Exponential backoff with jitter and if succeed and return a `non-NULL` handle. ]*/
00527                             if (IoTHubClient_LL_SetRetryPolicy(handleData, IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, 0) != IOTHUB_CLIENT_OK)
00528                             {
00529                                 LogError("Setting default retry policy in transport failed");
00530                                 IoTHubClient_LL_Destroy(handleData);
00531                                 result = NULL;
00532                             }
00533                         }
00534                     }
00535                 }
00536             }
00537         }
00538     }
00539 
00540     return result;
00541 }
00542 
00543 IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateWithTransport(const IOTHUB_CLIENT_DEVICE_CONFIG * config)
00544 {
00545     IOTHUB_CLIENT_LL_HANDLE result;
00546     /*Codes_SRS_IOTHUBCLIENT_LL_17_001: [IoTHubClient_LL_CreateWithTransport shall return NULL if config parameter is NULL, or protocol field is NULL or transportHandle is NULL.]*/
00547     if (
00548         (config == NULL) ||
00549         (config->protocol == NULL) ||
00550         (config->transportHandle == NULL) ||
00551         /*Codes_SRS_IOTHUBCLIENT_LL_02_098: [ IoTHubClient_LL_CreateWithTransport shall fail and return NULL if both config->deviceKey AND config->deviceSasToken are NULL. ]*/
00552         ((config->deviceKey == NULL) && (config->deviceSasToken == NULL))
00553         )
00554     {
00555         result = NULL;
00556         LogError("invalid configuration (NULL detected)");
00557     }
00558     else
00559     {
00560         /*Codes_SRS_IOTHUBCLIENT_LL_17_002: [IoTHubClient_LL_CreateWithTransport shall allocate data for the IOTHUB_CLIENT_LL_HANDLE.]*/
00561         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)malloc(sizeof(IOTHUB_CLIENT_LL_HANDLE_DATA));
00562         if (handleData == NULL)
00563         {
00564             /*Codes_SRS_IOTHUBCLIENT_LL_17_003: [If allocation fails, the function shall fail and return NULL.] */
00565             LogError("malloc failed");
00566             result = NULL;
00567         }
00568         else
00569         {
00570             handleData->transportHandle = config->transportHandle;
00571             setTransportProtocol(handleData, (TRANSPORT_PROVIDER*)config->protocol());
00572 
00573 #ifndef DONT_USE_UPLOADTOBLOB
00574             const char* hostname = STRING_c_str(handleData->IoTHubTransport_GetHostname(handleData->transportHandle));
00575             /*Codes_SRS_IOTHUBCLIENT_LL_02_096: [ IoTHubClient_LL_CreateWithTransport shall create the data structures needed to instantiate a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE. ]*/
00576             /*the first '.' says where the iothubname finishes*/
00577             const char* whereIsDot = strchr(hostname, '.');
00578             if (whereIsDot == NULL)
00579             {
00580                 /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClient_LL_CreateWithTransport shall fail and return NULL. ]*/
00581                 LogError("unable to determine the IoTHub name");
00582                 free(handleData);
00583                 result = NULL;
00584             }
00585             else
00586             {
00587                 /*Codes_SRS_IOTHUBCLIENT_LL_02_096: [ IoTHubClient_LL_CreateWithTransport shall create the data structures needed to instantiate a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE. ]*/
00588                 char* IoTHubName = (char*) malloc(whereIsDot - hostname + 1);
00589                 if (IoTHubName == NULL)
00590                 {
00591                     /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClient_LL_CreateWithTransport shall fail and return NULL. ]*/
00592                     LogError("unable to malloc");
00593                     free(handleData);
00594                     result = NULL;
00595                 }
00596                 else
00597                 {
00598                     const char* IotHubSuffix = whereIsDot + 1;
00599                     memcpy(IoTHubName, hostname, whereIsDot - hostname);
00600                     IoTHubName[whereIsDot - hostname ] = '\0';
00601                     
00602                     IOTHUB_CLIENT_CONFIG temp;
00603                     temp.deviceId = config->deviceId;
00604                     temp.deviceKey = config->deviceKey;
00605                     temp.deviceSasToken = config->deviceSasToken;
00606                     temp.iotHubName = IoTHubName;
00607                     temp.iotHubSuffix = IotHubSuffix;
00608                     temp.protocol = NULL; /*irrelevant to IoTHubClient_LL_UploadToBlob*/
00609                     temp.protocolGatewayHostName = NULL; /*irrelevant to IoTHubClient_LL_UploadToBlob*/
00610 
00611                     /*Codes_SRS_IOTHUBCLIENT_LL_02_097: [ If creating the data structures fails or instantiating the IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE fails then IoTHubClient_LL_CreateWithTransport shall fail and return NULL. ]*/
00612                     handleData->uploadToBlobHandle = IoTHubClient_LL_UploadToBlob_Create(&temp);
00613                     if (handleData->uploadToBlobHandle == NULL)
00614                     {
00615                         /*Codes_SRS_IOTHUBCLIENT_LL_02_096: [ IoTHubClient_LL_CreateWithTransport shall create the data structures needed to instantiate a IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE. ]*/
00616                         LogError("unable to IoTHubClient_LL_UploadToBlob_Create");
00617                         free(handleData);
00618                         result = NULL;
00619                     }
00620                     else
00621 #endif
00622                     {
00623                         /*Codes_SRS_IOTHUBCLIENT_LL_02_047: [ IoTHubClient_LL_CreateWithTransport shall create a TICK_COUNTER_HANDLE. ]*/
00624                         if ((handleData->tickCounter = tickcounter_create()) == NULL)
00625                         {
00626                             /*Codes_SRS_IOTHUBCLIENT_LL_02_048: [ If creating the handle fails, then IoTHubClient_LL_CreateWithTransport shall fail and return NULL ]*/
00627                             LogError("unable to get a tickcounter");
00628 #ifndef DONT_USE_UPLOADTOBLOB
00629                             IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00630 #endif
00631                             free(handleData);
00632                             result = NULL;
00633                         }
00634                         else
00635                         {
00636                             /*Codes_SRS_IOTHUBCLIENT_LL_17_004: [IoTHubClient_LL_CreateWithTransport 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*.]*/
00637                             DList_InitializeListHead(&(handleData->waitingToSend));
00638                             DList_InitializeListHead(&(handleData->iot_msg_queue));
00639                             DList_InitializeListHead(&(handleData->iot_ack_queue));
00640                             handleData->messageCallback = NULL;
00641                             handleData->messageUserContextCallback = NULL;
00642                             handleData->deviceTwinCallback = NULL;
00643                             handleData->deviceTwinContextCallback = NULL;
00644                             handleData->deviceMethodCallback = NULL;
00645                             handleData->deviceMethodUserContextCallback = NULL;
00646                             handleData->lastMessageReceiveTime = INDEFINITE_TIME;
00647                             handleData->data_msg_id = 1;
00648                             handleData->complete_twin_update_encountered = false;
00649 
00650                             IOTHUB_DEVICE_CONFIG deviceConfig;
00651 
00652                             deviceConfig.deviceId = config->deviceId;
00653                             deviceConfig.deviceKey = config->deviceKey;
00654                             deviceConfig.deviceSasToken = config->deviceSasToken;
00655 
00656                             /*Codes_SRS_IOTHUBCLIENT_LL_17_006: [IoTHubClient_LL_CreateWithTransport shall call the transport _Register function with the IOTHUB_DEVICE_CONFIG populated structure and waitingToSend list.]*/
00657                             if ((handleData->deviceHandle = handleData->IoTHubTransport_Register(config->transportHandle, &deviceConfig, handleData, &(handleData->waitingToSend))) == NULL)
00658                             {
00659                                 /*Codes_SRS_IOTHUBCLIENT_LL_17_007: [If the _Register function fails, this function shall fail and return NULL.]*/
00660                                 LogError("Registering device in transport failed");
00661 #ifndef DONT_USE_UPLOADTOBLOB
00662                                 IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00663 #endif
00664                                 tickcounter_destroy(handleData->tickCounter);
00665                                 free(handleData);
00666                                 result = NULL;
00667                             }
00668                             else
00669                             {
00670                                 /*Codes_SRS_IOTHUBCLIENT_LL_17_005: [IoTHubClient_LL_CreateWithTransport shall save the transport handle and mark this transport as shared.]*/
00671                                 handleData->isSharedTransport = true;
00672                                 /*Codes_SRS_IOTHUBCLIENT_LL_02_042: [ By default, messages shall not timeout. ]*/
00673                                 handleData->currentMessageTimeout = 0;
00674                                 handleData->current_device_twin_timeout = 0;
00675                                 result = handleData;
00676                                 /*Codes_SRS_IOTHUBCLIENT_LL_25_125: [ `IoTHubClient_LL_CreateWithTransport` shall set the default retry policy as Exponential backoff with jitter and if succeed and return a `non-NULL` handle. ]*/
00677                                 if (IoTHubClient_LL_SetRetryPolicy(handleData, IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF_WITH_JITTER, 0) != IOTHUB_CLIENT_OK)
00678                                 {
00679                                     LogError("Setting default retry policy in transport failed");
00680                                     IoTHubClient_LL_Destroy(handleData);
00681                                     result = NULL;
00682                                 }
00683                             }
00684                         }
00685                     }
00686 #ifndef DONT_USE_UPLOADTOBLOB
00687                     free(IoTHubName);
00688                 }
00689             }
00690 #endif
00691         }
00692     }
00693 
00694     return result;
00695 }
00696 
00697 void IoTHubClient_LL_Destroy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle)
00698 {
00699     /*Codes_SRS_IOTHUBCLIENT_LL_02_009: [IoTHubClient_LL_Destroy shall do nothing if parameter iotHubClientHandle is NULL.]*/
00700     if (iotHubClientHandle != NULL)
00701     {
00702         PDLIST_ENTRY unsend;
00703         /*Codes_SRS_IOTHUBCLIENT_LL_17_010: [IoTHubClient_LL_Destroy  shall call the underlaying layer's _Unregister function] */
00704         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
00705         handleData->IoTHubTransport_Unregister(handleData->deviceHandle);
00706         if (handleData->isSharedTransport == false)
00707         {
00708             /*Codes_SRS_IOTHUBCLIENT_LL_02_010: [If iotHubClientHandle was not created by IoTHubClient_LL_CreateWithTransport, IoTHubClient_LL_Destroy  shall call the underlaying layer's _Destroy function.] */
00709             handleData->IoTHubTransport_Destroy(handleData->transportHandle);
00710         }
00711         /*if any, remove the items currently not send*/
00712         while ((unsend = DList_RemoveHeadList(&(handleData->waitingToSend))) != &(handleData->waitingToSend))
00713         {
00714             IOTHUB_MESSAGE_LIST* temp = containingRecord(unsend, IOTHUB_MESSAGE_LIST, entry);
00715             /*Codes_SRS_IOTHUBCLIENT_LL_02_033: [Otherwise, IoTHubClient_LL_Destroy shall complete all the event message callbacks that are in the waitingToSend list with the result IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY.] */
00716             if (temp->callback != NULL)
00717             {
00718                 temp->callback(IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY, temp->context);
00719             }
00720             IoTHubMessage_Destroy(temp->messageHandle);
00721             free(temp);
00722         }
00723 
00724         /* Codes_SRS_IOTHUBCLIENT_LL_07_007: [ IoTHubClient_LL_Destroy shall iterate the device twin queues and destroy any remaining items. ] */
00725         while ((unsend = DList_RemoveHeadList(&(handleData->iot_msg_queue))) != &(handleData->iot_msg_queue))
00726         {
00727             IOTHUB_DEVICE_TWIN* temp = containingRecord(unsend, IOTHUB_DEVICE_TWIN, entry);
00728             device_twin_data_destroy(temp);
00729         }
00730         while ((unsend = DList_RemoveHeadList(&(handleData->iot_ack_queue))) != &(handleData->iot_ack_queue))
00731         {
00732             IOTHUB_DEVICE_TWIN* temp = containingRecord(unsend, IOTHUB_DEVICE_TWIN, entry);
00733             device_twin_data_destroy(temp);
00734         }
00735 
00736         /*Codes_SRS_IOTHUBCLIENT_LL_17_011: [IoTHubClient_LL_Destroy  shall free the resources allocated by IoTHubClient (if any).] */
00737         tickcounter_destroy(handleData->tickCounter);
00738 #ifndef DONT_USE_UPLOADTOBLOB
00739         IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle);
00740 #endif
00741         free(handleData);
00742     }
00743 }
00744 
00745 /*Codes_SRS_IOTHUBCLIENT_LL_02_044: [ Messages already delivered to IoTHubClient_LL shall not have their timeouts modified by a new call to IoTHubClient_LL_SetOption. ]*/
00746 /*returns 0 on success, any other value is error*/
00747 static int attach_ms_timesOutAfter(IOTHUB_CLIENT_LL_HANDLE_DATA* handleData, IOTHUB_MESSAGE_LIST *newEntry)
00748 {
00749     int result;
00750     /*Codes_SRS_IOTHUBCLIENT_LL_02_043: [ Calling IoTHubClient_LL_SetOption with value set to "0" shall disable the timeout mechanism for all new messages. ]*/
00751     if (handleData->currentMessageTimeout == 0)
00752     {
00753         newEntry->ms_timesOutAfter = 0; /*do not timeout*/
00754         result = 0;
00755     }
00756     else
00757     {
00758         /*Codes_SRS_IOTHUBCLIENT_LL_02_039: [ "messageTimeout" - once IoTHubClient_LL_SendEventAsync is called the message shall timeout after value miliseconds. Value is a pointer to a uint64. ]*/
00759         if (tickcounter_get_current_ms(handleData->tickCounter, &newEntry->ms_timesOutAfter) != 0)
00760         {
00761             result = __LINE__;
00762             LogError("unable to get the current relative tickcount");
00763         }
00764         else
00765         {
00766             newEntry->ms_timesOutAfter += handleData->currentMessageTimeout;
00767             result = 0;
00768         }
00769     }
00770     return result;
00771 }
00772 
00773 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SendEventAsync(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback)
00774 {
00775     IOTHUB_CLIENT_RESULT result;
00776     /*Codes_SRS_IOTHUBCLIENT_LL_02_011: [IoTHubClient_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle or eventMessageHandle is NULL.]*/
00777     if (
00778         (iotHubClientHandle == NULL) ||
00779         (eventMessageHandle == NULL) ||
00780         /*Codes_SRS_IOTHUBCLIENT_LL_02_012: [IoTHubClient_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter eventConfirmationCallback is NULL and userContextCallback is not NULL.] */
00781         ((eventConfirmationCallback == NULL) && (userContextCallback != NULL))
00782         )
00783     {
00784         result = IOTHUB_CLIENT_INVALID_ARG;
00785         LOG_ERROR_RESULT;
00786     }
00787     else
00788     {
00789         IOTHUB_MESSAGE_LIST *newEntry = (IOTHUB_MESSAGE_LIST*)malloc(sizeof(IOTHUB_MESSAGE_LIST));
00790         if (newEntry == NULL)
00791         {
00792             result = IOTHUB_CLIENT_ERROR;
00793             LOG_ERROR_RESULT;
00794         }
00795         else
00796         {
00797             IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
00798 
00799             if (attach_ms_timesOutAfter(handleData, newEntry) != 0)
00800             {
00801                 result = IOTHUB_CLIENT_ERROR;
00802                 LOG_ERROR_RESULT;
00803                 free(newEntry);
00804             }
00805             else
00806             {
00807                 /*Codes_SRS_IOTHUBCLIENT_LL_02_013: [IoTHubClient_SendEventAsync shall add the DLIST waitingToSend a new record cloning the information from eventMessageHandle, eventConfirmationCallback, userContextCallback.]*/
00808                 if ((newEntry->messageHandle = IoTHubMessage_Clone(eventMessageHandle)) == NULL)
00809                 {
00810                     /*Codes_SRS_IOTHUBCLIENT_LL_02_014: [If cloning and/or adding the information fails for any reason, IoTHubClient_LL_SendEventAsync shall fail and return IOTHUB_CLIENT_ERROR.] */
00811                     result = IOTHUB_CLIENT_ERROR;
00812                     free(newEntry);
00813                     LOG_ERROR_RESULT;
00814                 }
00815                 else
00816                 {
00817                     /*Codes_SRS_IOTHUBCLIENT_LL_02_013: [IoTHubClient_SendEventAsync shall add the DLIST waitingToSend a new record cloning the information from eventMessageHandle, eventConfirmationCallback, userContextCallback.]*/
00818                     newEntry->callback = eventConfirmationCallback;
00819                     newEntry->context = userContextCallback;
00820                     DList_InsertTailList(&(iotHubClientHandle->waitingToSend), &(newEntry->entry));
00821                     /*Codes_SRS_IOTHUBCLIENT_LL_02_015: [Otherwise IoTHubClient_LL_SendEventAsync shall succeed and return IOTHUB_CLIENT_OK.] */
00822                     result = IOTHUB_CLIENT_OK;
00823                 }
00824             }
00825         }
00826     }
00827     return result;
00828 }
00829 
00830 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetMessageCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback)
00831 {
00832     IOTHUB_CLIENT_RESULT result;
00833     /*Codes_SRS_IOTHUBCLIENT_LL_02_016: [IoTHubClient_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.] */
00834     if (iotHubClientHandle == NULL)
00835     {
00836         result = IOTHUB_CLIENT_INVALID_ARG;
00837         LOG_ERROR_RESULT;
00838     }
00839     else
00840     {
00841         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
00842         if (messageCallback == NULL)
00843         {
00844             /*Codes_SRS_IOTHUBCLIENT_LL_02_019: [If parameter messageCallback is NULL then IoTHubClient_LL_SetMessageCallback shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */
00845             handleData->IoTHubTransport_Unsubscribe(handleData->deviceHandle);
00846             handleData->messageCallback = NULL;
00847             handleData->messageUserContextCallback = NULL;
00848             result = IOTHUB_CLIENT_OK;
00849         }
00850         else
00851         {
00852             /*Codes_SRS_IOTHUBCLIENT_LL_02_017: [If parameter messageCallback is non-NULL then IoTHubClient_LL_SetMessageCallback shall call the underlying layer's _Subscribe function.]*/
00853             if (handleData->IoTHubTransport_Subscribe(handleData->deviceHandle) == 0)
00854             {
00855                 handleData->messageCallback = messageCallback;
00856                 handleData->messageUserContextCallback = userContextCallback;
00857                 result = IOTHUB_CLIENT_OK;
00858             }
00859             else
00860             {
00861                 handleData->messageCallback = NULL;
00862                 handleData->messageUserContextCallback = NULL;
00863                 /*Codes_SRS_IOTHUBCLIENT_LL_02_018: [If the underlying layer's _Subscribe function fails, then IoTHubClient_LL_SetMessageCallback shall fail and return IOTHUB_CLIENT_ERROR. Otherwise IoTHubClient_LL_SetMessageCallback shall succeed and return IOTHUB_CLIENT_OK.]*/
00864                 result = IOTHUB_CLIENT_ERROR;
00865             }
00866         }
00867     }
00868 
00869     return result;
00870 }
00871 
00872 static void DoTimeouts(IOTHUB_CLIENT_LL_HANDLE_DATA* handleData)
00873 {
00874     uint64_t nowTick;
00875     if (tickcounter_get_current_ms(handleData->tickCounter, &nowTick) != 0)
00876     {
00877         LogError("unable to get the current ms, timeouts will not be processed");
00878     }
00879     else
00880     {
00881         DLIST_ENTRY* currentItemInWaitingToSend = handleData->waitingToSend.Flink;
00882         while (currentItemInWaitingToSend != &(handleData->waitingToSend)) /*while we are not at the end of the list*/
00883         {
00884             IOTHUB_MESSAGE_LIST* fullEntry = containingRecord(currentItemInWaitingToSend, IOTHUB_MESSAGE_LIST, entry);
00885             /*Codes_SRS_IOTHUBCLIENT_LL_02_041: [ If more than value miliseconds have passed since the call to IoTHubClient_LL_SendEventAsync then the message callback shall be called with a status code of IOTHUB_CLIENT_CONFIRMATION_TIMEOUT. ]*/
00886             if ((fullEntry->ms_timesOutAfter != 0) && (fullEntry->ms_timesOutAfter < nowTick))
00887             {
00888                 PDLIST_ENTRY theNext = currentItemInWaitingToSend->Flink; /*need to save the next item, because the below operations are destructive*/
00889                 DList_RemoveEntryList(currentItemInWaitingToSend);
00890                 if (fullEntry->callback != NULL)
00891                 {
00892                     fullEntry->callback(IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT, fullEntry->context);
00893                 }
00894                 IoTHubMessage_Destroy(fullEntry->messageHandle); /*because it has been cloned*/
00895                 free(fullEntry);
00896                 currentItemInWaitingToSend = theNext;
00897             }
00898             else
00899             {
00900                 currentItemInWaitingToSend = currentItemInWaitingToSend->Flink;
00901             }
00902         }
00903     }
00904 }
00905 
00906 void IoTHubClient_LL_DoWork(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle)
00907 {
00908     /*Codes_SRS_IOTHUBCLIENT_LL_02_020: [If parameter iotHubClientHandle is NULL then IoTHubClient_LL_DoWork shall not perform any action.] */
00909     if (iotHubClientHandle != NULL)
00910     {
00911         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
00912         DoTimeouts(handleData);
00913 
00914         /*Codes_SRS_IOTHUBCLIENT_LL_07_008: [ IoTHubClient_LL_DoWork shall iterate the message queue and execute the underlying transports IoTHubTransport_ProcessItem function for each item. ] */
00915         DLIST_ENTRY* client_item = handleData->iot_msg_queue.Flink;
00916         while (client_item != &(handleData->iot_msg_queue)) /*while we are not at the end of the list*/
00917         {
00918             PDLIST_ENTRY next_item = client_item->Flink;
00919 
00920             IOTHUB_DEVICE_TWIN* queue_data = containingRecord(client_item, IOTHUB_DEVICE_TWIN, entry);
00921             IOTHUB_IDENTITY_INFO identity_info;
00922             identity_info.device_twin = queue_data;
00923             IOTHUB_PROCESS_ITEM_RESULT process_results =  handleData->IoTHubTransport_ProcessItem(handleData->transportHandle, IOTHUB_TYPE_DEVICE_TWIN, &identity_info);
00924             if (process_results == IOTHUB_PROCESS_CONTINUE || process_results == IOTHUB_PROCESS_NOT_CONNECTED)
00925             {
00926                 /*Codes_SRS_IOTHUBCLIENT_LL_07_010: [ If 'IoTHubTransport_ProcessItem' returns IOTHUB_PROCESS_CONTINUE or IOTHUB_PROCESS_NOT_CONNECTED IoTHubClient_LL_DoWork shall continue on to call the underlaying layer's _DoWork function. ]*/
00927                 break;
00928             }
00929             else 
00930             {
00931                 DList_RemoveEntryList(client_item);
00932                 if (process_results == IOTHUB_PROCESS_OK)
00933                 {
00934                     /*Codes_SRS_IOTHUBCLIENT_LL_07_011: [ If 'IoTHubTransport_ProcessItem' returns IOTHUB_PROCESS_OK IoTHubClient_LL_DoWork shall add the IOTHUB_DEVICE_TWIN to the ack queue. ]*/
00935                     DList_InsertTailList(&(iotHubClientHandle->iot_ack_queue), &(queue_data->entry));
00936                 }
00937                 else
00938                 {
00939                     /*Codes_SRS_IOTHUBCLIENT_LL_07_012: [ If 'IoTHubTransport_ProcessItem' returns any other value IoTHubClient_LL_DoWork shall destroy the IOTHUB_DEVICE_TWIN item. ]*/
00940                     LogError("Failure queue processing item");
00941                     device_twin_data_destroy(queue_data);
00942                 }
00943             }
00944             // Move along to the next item
00945             client_item = next_item;
00946         }
00947 
00948         /*Codes_SRS_IOTHUBCLIENT_LL_02_021: [Otherwise, IoTHubClient_LL_DoWork shall invoke the underlaying layer's _DoWork function.]*/
00949         handleData->IoTHubTransport_DoWork(handleData->transportHandle, iotHubClientHandle);
00950     }
00951 }
00952 
00953 IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetSendStatus(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus)
00954 {
00955     IOTHUB_CLIENT_RESULT result;
00956 
00957     /* Codes_SRS_IOTHUBCLIENT_09_007: [IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter] */
00958     if (iotHubClientHandle == NULL || iotHubClientStatus == NULL)
00959     {
00960         result = IOTHUB_CLIENT_INVALID_ARG;
00961         LOG_ERROR_RESULT;
00962     }
00963     else
00964     {
00965         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
00966 
00967         /* 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] */
00968         /* 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] */
00969         result = handleData->IoTHubTransport_GetSendStatus(handleData->deviceHandle, iotHubClientStatus);
00970     }
00971 
00972     return result;
00973 }
00974 
00975 void IoTHubClient_LL_SendComplete(IOTHUB_CLIENT_LL_HANDLE handle, PDLIST_ENTRY completed, IOTHUB_CLIENT_CONFIRMATION_RESULT result)
00976 {
00977     /*Codes_SRS_IOTHUBCLIENT_LL_02_022: [If parameter completed is NULL, or parameter handle is NULL then IoTHubClient_LL_SendBatch shall return.]*/
00978     if (
00979         (handle == NULL) ||
00980         (completed == NULL)
00981         )
00982     {
00983         /*"shall return"*/
00984         LogError("invalid arg");
00985     }
00986     else
00987     {
00988         /*Codes_SRS_IOTHUBCLIENT_LL_02_027: [If parameter result is IOTHUB_CLIENT_CONFIRMATION_ERROR then IoTHubClient_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.] */
00989         /*Codes_SRS_IOTHUBCLIENT_LL_02_025: [If parameter result is IOTHUB_CLIENT_CONFIRMATION_OK then IoTHubClient_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.]*/
00990         PDLIST_ENTRY oldest;
00991         while ((oldest = DList_RemoveHeadList(completed)) != completed)
00992         {
00993             IOTHUB_MESSAGE_LIST* messageList = (IOTHUB_MESSAGE_LIST*)containingRecord(oldest, IOTHUB_MESSAGE_LIST, entry);
00994             /*Codes_SRS_IOTHUBCLIENT_LL_02_026: [If any callback is NULL then there shall not be a callback call.]*/
00995             if (messageList->callback != NULL)
00996             {
00997                 messageList->callback(result, messageList->context);
00998             }
00999             IoTHubMessage_Destroy(messageList->messageHandle);
01000             free(messageList);
01001         }
01002     }
01003 }
01004 
01005 int IoTHubClient_LL_DeviceMethodComplete(IOTHUB_CLIENT_LL_HANDLE handle, const char* method_name, const unsigned char* payLoad, size_t size, BUFFER_HANDLE response)
01006 {
01007     int result;
01008     if (handle == NULL || response == NULL)
01009     {
01010         /* Codes_SRS_IOTHUBCLIENT_LL_07_017: [ If handle or response is NULL then IoTHubClient_LL_DeviceMethodComplete shall return 500. ] */
01011         LogError("Invalid argument handle=%p", handle);
01012         result = 500;
01013     }
01014     else
01015     {
01016         /* Codes_SRS_IOTHUBCLIENT_LL_07_018: [ If deviceMethodCallback is not NULL IoTHubClient_LL_DeviceMethodComplete shall execute deviceMethodCallback and return the status. ] */
01017         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)handle;
01018         if (handleData->deviceMethodCallback)
01019         {
01020             unsigned char* payload_resp = NULL;
01021             size_t resp_size = 0;
01022             result = handleData->deviceMethodCallback(method_name, payLoad, size, &payload_resp, &resp_size, handleData->deviceMethodUserContextCallback);
01023             /* Codes_SRS_IOTHUBCLIENT_LL_07_020: [ deviceMethodCallback shall buil the BUFFER_HANDLE with the response payload from the IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC callback. ] */
01024             if (payload_resp != NULL && resp_size > 0)
01025             {
01026                 if (BUFFER_build(response, payload_resp, resp_size) != 0)
01027                 {
01028                     result = 500;
01029                 }
01030             }
01031             if (payload_resp != NULL)
01032             {
01033                 free(payload_resp);
01034             }
01035         }
01036         else
01037         {
01038             /* Codes_SRS_IOTHUBCLIENT_LL_07_019: [ If deviceMethodCallback is NULL IoTHubClient_LL_DeviceMethodComplete shall return 404. ] */
01039             result = 404;
01040         }
01041     }
01042     return result;
01043 }
01044 
01045 void IoTHubClient_LL_RetrievePropertyComplete(IOTHUB_CLIENT_LL_HANDLE handle, DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size)
01046 {
01047     if (handle == NULL)
01048     {
01049         /* Codes_SRS_IOTHUBCLIENT_LL_07_013: [ If handle is NULL then IoTHubClient_LL_RetrievePropertyComplete shall do nothing.] */
01050         LogError("Invalid argument handle=%p", handle);
01051     }
01052     else
01053     {
01054         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)handle;
01055         /* Codes_SRS_IOTHUBCLIENT_LL_07_014: [ If deviceTwinCallback is NULL then IoTHubClient_LL_RetrievePropertyComplete shall do nothing.] */
01056         if (handleData->deviceTwinCallback)
01057         {
01058             /* 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 IoTHubClient_LL_RetrievePropertyComplete shall do nothing.] */
01059             if (update_state == DEVICE_TWIN_UPDATE_COMPLETE)
01060             {
01061                 handleData->complete_twin_update_encountered = true;
01062             }
01063             if (handleData->complete_twin_update_encountered)
01064             {
01065                 /* Codes_SRS_IOTHUBCLIENT_LL_07_016: [ If deviceTwinCallback is set and DEVICE_TWIN_UPDATE_COMPLETE has been encountered then IoTHubClient_LL_RetrievePropertyComplete shall call deviceTwinCallback.] */
01066                 handleData->deviceTwinCallback(update_state, payLoad, size, handleData->deviceTwinContextCallback);
01067             }
01068         }
01069     }
01070 }
01071 
01072 void IoTHubClient_LL_ReportedStateComplete(IOTHUB_CLIENT_LL_HANDLE handle, uint32_t item_id, int status_code)
01073 {
01074     /* Codes_SRS_IOTHUBCLIENT_LL_07_002: [ if handle or queue_handle are NULL then IoTHubClient_LL_ReportedStateComplete shall do nothing. ] */
01075     if (handle == NULL)
01076     {
01077         /*"shall return"*/
01078         LogError("Invalid argument handle=%p", handle);
01079     }
01080     else
01081     {
01082         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)handle;
01083 
01084         /* Codes_SRS_IOTHUBCLIENT_LL_07_003: [ IoTHubClient_LL_ReportedStateComplete shall enumerate through the IOTHUB_DEVICE_TWIN structures in queue_handle. ]*/
01085         DLIST_ENTRY* client_item = handleData->iot_ack_queue.Flink;
01086         while (client_item != &(handleData->iot_ack_queue)) /*while we are not at the end of the list*/
01087         {
01088             PDLIST_ENTRY next_item = client_item->Flink;
01089             IOTHUB_DEVICE_TWIN* queue_data = containingRecord(client_item, IOTHUB_DEVICE_TWIN, entry);
01090             if (queue_data->item_id == item_id)
01091             {
01092                 if (queue_data->reported_state_callback != NULL)
01093                 {
01094                     queue_data->reported_state_callback(status_code, queue_data->context);
01095                 }
01096                 /*Codes_SRS_IOTHUBCLIENT_LL_07_009: [ IoTHubClient_LL_ReportedStateComplete shall remove the IOTHUB_DEVICE_TWIN item from the ack queue.]*/
01097                 DList_RemoveEntryList(client_item);
01098                 device_twin_data_destroy(queue_data);
01099                 break;
01100             }
01101             client_item = next_item;
01102         }
01103     }
01104 }
01105 
01106 IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubClient_LL_MessageCallback(IOTHUB_CLIENT_LL_HANDLE handle, IOTHUB_MESSAGE_HANDLE message)
01107 {
01108     int result;
01109     /*Codes_SRS_IOTHUBCLIENT_LL_02_029: [If parameter handle is NULL then IoTHubClient_LL_MessageCallback shall return IOTHUBMESSAGE_ABANDONED.] */
01110     if (handle == NULL)
01111     {
01112         LogError("invalid argument");
01113         result = IOTHUBMESSAGE_ABANDONED;
01114     }
01115     else
01116     {
01117         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)handle;
01118 
01119         /* Codes_SRS_IOTHUBCLIENT_LL_09_004: [IoTHubClient_LL_GetLastMessageReceiveTime shall return lastMessageReceiveTime in localtime] */
01120         handleData->lastMessageReceiveTime = get_time(NULL);
01121 
01122         /*Codes_SRS_IOTHUBCLIENT_LL_02_030: [IoTHubClient_LL_MessageCallback shall invoke the last callback function (the parameter messageCallback to IoTHubClient_LL_SetMessageCallback) passing the message and the passed userContextCallback.]*/
01123         if (handleData->messageCallback != NULL)
01124         {
01125             result = handleData->messageCallback(message, handleData->messageUserContextCallback);
01126         }
01127         else
01128         {
01129             /*Codes_SRS_IOTHUBCLIENT_LL_02_032: [If the last callback function was NULL, then IoTHubClient_LL_MessageCallback  shall return IOTHUBMESSAGE_ABANDONED.] */
01130             LogError("user callback was NULL");
01131             result = IOTHUBMESSAGE_ABANDONED;
01132         }
01133     }
01134     /*Codes_SRS_IOTHUBCLIENT_LL_02_031: [Then IoTHubClient_LL_MessageCallback shall return what the user function returns.]*/
01135     return (IOTHUBMESSAGE_DISPOSITION_RESULT) result;
01136 }
01137 
01138 void IotHubClient_LL_ConnectionStatusCallBack(IOTHUB_CLIENT_LL_HANDLE handle, IOTHUB_CLIENT_CONNECTION_STATUS status, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason)
01139 {
01140     /*Codes_SRS_IOTHUBCLIENT_LL_25_113: [If parameter connectionStatus is NULL or parameter handle is NULL then IotHubClient_LL_ConnectionStatusCallBack shall return.]*/
01141     if (handle == NULL)
01142     {
01143         /*"shall return"*/
01144         LogError("invalid arg");
01145     }
01146     else
01147     {
01148         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)handle;
01149 
01150         /*Codes_SRS_IOTHUBCLIENT_LL_25_114: [IotHubClient_LL_ConnectionStatusCallBack shall call non-callback set by the user from IoTHubClient_LL_SetConnectionStatusCallback passing the status, reason and the passed userContextCallback.]*/
01151         if (handleData->conStatusCallback != NULL)
01152         {
01153             handleData->conStatusCallback(status, reason, handleData->conStatusUserContextCallback);
01154         }
01155     }
01156 
01157 }
01158 
01159 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetConnectionStatusCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connectionStatusCallback, void * userContextCallback)
01160 {
01161     IOTHUB_CLIENT_RESULT result;
01162     /*Codes_SRS_IOTHUBCLIENT_LL_25_111: [IoTHubClient_LL_SetConnectionStatusCallback shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter iotHubClientHandle**]** */
01163     if (iotHubClientHandle == NULL)
01164     {
01165         result = IOTHUB_CLIENT_INVALID_ARG;
01166         LOG_ERROR_RESULT;
01167     }
01168     else
01169     {
01170         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01171         /*Codes_SRS_IOTHUBCLIENT_LL_25_112: [IoTHubClient_LL_SetConnectionStatusCallback shall return IOTHUB_CLIENT_OK and save the callback and userContext as a member of the handle.] */
01172         handleData->conStatusCallback = connectionStatusCallback;
01173         handleData->conStatusUserContextCallback = userContextCallback;
01174         result = IOTHUB_CLIENT_OK;
01175     }
01176 
01177     return result;
01178 }
01179 
01180 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetRetryPolicy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds)
01181 {
01182     IOTHUB_CLIENT_RESULT result;
01183     IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01184 
01185     /* Codes_SRS_IOTHUBCLIENT_LL_25_116: [**IoTHubClient_LL_SetRetryPolicy shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL iotHubClientHandle]*/
01186     if (handleData == NULL)
01187     {
01188         result = IOTHUB_CLIENT_INVALID_ARG;
01189         LOG_ERROR_RESULT;
01190     }
01191     else
01192     {
01193         if (handleData->transportHandle == NULL)
01194         {
01195             result = IOTHUB_CLIENT_ERROR;
01196             LOG_ERROR_RESULT;
01197         }
01198         else
01199         {
01200             if (handleData->IoTHubTransport_SetRetryPolicy(handleData->transportHandle, retryPolicy, retryTimeoutLimitInSeconds) != 0)
01201             {
01202                 result = IOTHUB_CLIENT_ERROR;
01203                 LOG_ERROR_RESULT;
01204             }
01205             else
01206             {
01207                 /*Codes_SRS_IOTHUBCLIENT_LL_25_118: [**IoTHubClient_LL_SetRetryPolicy shall save connection retry policies specified by the user to retryPolicy in struct IOTHUB_CLIENT_LL_HANDLE_DATA]*/
01208                 /*Codes_SRS_IOTHUBCLIENT_LL_25_119: [**IoTHubClient_LL_SetRetryPolicy shall save retryTimeoutLimitInSeconds in seconds to retryTimeout in struct IOTHUB_CLIENT_LL_HANDLE_DATA]*/
01209                 handleData->retryPolicy = retryPolicy;
01210                 handleData->retryTimeoutLimitInSeconds = retryTimeoutLimitInSeconds;
01211                 result = IOTHUB_CLIENT_OK;
01212             }
01213         }
01214     }
01215     return result;
01216 }
01217 
01218 IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetRetryPolicy(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds)
01219 {
01220     IOTHUB_CLIENT_RESULT result = IOTHUB_CLIENT_OK;
01221     IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01222 
01223     /* Codes_SRS_IOTHUBCLIENT_LL_09_001: [IoTHubClient_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG if any of the arguments is NULL] */
01224     if (handleData == NULL || retryPolicy == NULL || retryTimeoutLimitInSeconds == NULL)
01225     {
01226         result = IOTHUB_CLIENT_INVALID_ARG;
01227         LOG_ERROR_RESULT;
01228     }
01229     else
01230     {
01231         *retryPolicy = handleData->retryPolicy;
01232         *retryTimeoutLimitInSeconds = handleData->retryTimeoutLimitInSeconds;
01233         result = IOTHUB_CLIENT_OK;
01234     }
01235 
01236     return result;
01237 }
01238 
01239 IOTHUB_CLIENT_RESULT IoTHubClient_LL_GetLastMessageReceiveTime(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime)
01240 {
01241     IOTHUB_CLIENT_RESULT result;
01242     IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01243 
01244     /* Codes_SRS_IOTHUBCLIENT_LL_09_001: [IoTHubClient_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG if any of the arguments is NULL] */
01245     if (handleData == NULL || lastMessageReceiveTime == NULL)
01246     {
01247         result = IOTHUB_CLIENT_INVALID_ARG;
01248         LOG_ERROR_RESULT;
01249     }
01250     else
01251     {
01252         /* Codes_SRS_IOTHUBCLIENT_LL_09_002: [IoTHubClient_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INDEFINITE_TIME - and not set 'lastMessageReceiveTime' - if it is unable to provide the time for the last commands] */
01253         if (handleData->lastMessageReceiveTime == INDEFINITE_TIME)
01254         {
01255             result = IOTHUB_CLIENT_INDEFINITE_TIME;
01256             LOG_ERROR_RESULT;
01257         }
01258         else
01259         {
01260             /* Codes_SRS_IOTHUBCLIENT_LL_09_003: [IoTHubClient_LL_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_OK if it wrote in the lastMessageReceiveTime the time when the last command was received] */
01261             /* Codes_SRS_IOTHUBCLIENT_LL_09_004: [IoTHubClient_LL_GetLastMessageReceiveTime shall return lastMessageReceiveTime in localtime] */
01262             *lastMessageReceiveTime = handleData->lastMessageReceiveTime;
01263             result = IOTHUB_CLIENT_OK;
01264         }
01265     }
01266 
01267     return result;
01268 }
01269 
01270 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetOption(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* optionName, const void* value)
01271 {
01272 
01273     IOTHUB_CLIENT_RESULT result;
01274     /*Codes_SRS_IOTHUBCLIENT_LL_02_034: [If iotHubClientHandle is NULL then IoTHubClient_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.]*/
01275     /*Codes_SRS_IOTHUBCLIENT_LL_02_035: [If optionName is NULL then IoTHubClient_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
01276     /*Codes_SRS_IOTHUBCLIENT_LL_02_036: [If value is NULL then IoTHubClient_LL_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
01277     if (
01278         (iotHubClientHandle == NULL) ||
01279         (optionName == NULL) ||
01280         (value == NULL)
01281         )
01282     {
01283         result = IOTHUB_CLIENT_INVALID_ARG;
01284         LogError("invalid argument (NULL)");
01285     }
01286     else
01287     {
01288         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01289 
01290         /*Codes_SRS_IOTHUBCLIENT_LL_02_039: [ "messageTimeout" - once IoTHubClient_LL_SendEventAsync is called the message shall timeout after value miliseconds. Value is a pointer to a uint64. ]*/
01291         if (strcmp(optionName, "messageTimeout") == 0)
01292         {
01293             /*this is an option handled by IoTHubClient_LL*/
01294             /*Codes_SRS_IOTHUBCLIENT_LL_02_043: [ Calling IoTHubClient_LL_SetOption with value set to "0" shall disable the timeout mechanism for all new messages. ]*/
01295             handleData->currentMessageTimeout = *(const uint64_t*)value;
01296             result = IOTHUB_CLIENT_OK;
01297         }
01298         else
01299         {
01300 
01301             /*Codes_SRS_IOTHUBCLIENT_LL_02_099: [ IoTHubClient_LL_SetOption shall return according to the table below ]*/
01302             IOTHUB_CLIENT_RESULT uploadToBlob_result; 
01303 #ifndef DONT_USE_UPLOADTOBLOB
01304             uploadToBlob_result = IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value);
01305             if(uploadToBlob_result == IOTHUB_CLIENT_ERROR)
01306             {
01307                 LogError("unable to IoTHubClient_LL_UploadToBlob_SetOption");
01308                 result = IOTHUB_CLIENT_ERROR;
01309             }
01310 #else
01311             uploadToBlob_result = IOTHUB_CLIENT_INVALID_ARG; /*harmless value (IOTHUB_CLIENT_INVALID_ARG)in the case when uploadtoblob is not compiled in, otherwise whatever IoTHubClient_LL_UploadToBlob_SetOption returned*/
01312 #endif /*DONT_USE_UPLOADTOBLOB*/
01313 
01314                 
01315             result =
01316                 /*based on uploadToBlob_result value this is what happens:*/
01317                 /*IOTHUB_CLIENT_INVALID_ARG always returns what IoTHubTransport_SetOption returns*/
01318                 /*IOTHUB_CLIENT_ERROR always returns IOTHUB_CLIENT_ERROR */
01319                 /*IOTHUB_CLIENT_OK returns OK
01320                     IOTHUB_CLIENT_OK if IoTHubTransport_SetOption returns OK or INVALID_ARG
01321                     IOTHUB_CLIENT_ERROR if IoTHubTransport_SetOption returns ERROR*/
01322 
01323                 (uploadToBlob_result == IOTHUB_CLIENT_INVALID_ARG) ? handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value) :
01324                 (uploadToBlob_result == IOTHUB_CLIENT_ERROR) ? IOTHUB_CLIENT_ERROR :
01325                 (handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value) == IOTHUB_CLIENT_ERROR) ? IOTHUB_CLIENT_ERROR : IOTHUB_CLIENT_OK;
01326 
01327             if (result != IOTHUB_CLIENT_OK)
01328             {
01329                 LogError("underlying transport failed, returned = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result));
01330             }
01331         }
01332     }
01333     return result;
01334 }
01335 
01336 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetDeviceTwinCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback)
01337 {
01338     IOTHUB_CLIENT_RESULT result;
01339     /* Codes_SRS_IOTHUBCLIENT_LL_10_001: [ IoTHubClient_LL_SetDeviceTwinCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL.] */
01340     if (iotHubClientHandle == NULL)
01341     {
01342         result = IOTHUB_CLIENT_INVALID_ARG;
01343         LogError("Invalid argument specified iothubClientHandle=%p", iotHubClientHandle);
01344     }
01345     else
01346     {
01347         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01348         if (deviceTwinCallback == NULL)
01349         {
01350             /* Codes_SRS_IOTHUBCLIENT_LL_10_006: [ If deviceTwinCallback is NULL, then IoTHubClient_LL_SetDeviceTwinCallback shall call the underlying layer's _Unsubscribe function and return IOTHUB_CLIENT_OK.] */
01351             handleData->IoTHubTransport_Unsubscribe_DeviceTwin(handleData->transportHandle);
01352             handleData->deviceTwinCallback = NULL;
01353             result = IOTHUB_CLIENT_OK;
01354         }
01355         else
01356         {
01357             /* Codes_SRS_IOTHUBCLIENT_LL_10_002: [ If deviceTwinCallback is not NULL, then IoTHubClient_LL_SetDeviceTwinCallback shall call the underlying layer's _Subscribe function.] */
01358             if (handleData->IoTHubTransport_Subscribe_DeviceTwin(handleData->transportHandle) == 0)
01359             {
01360                 handleData->deviceTwinCallback = deviceTwinCallback;
01361                 handleData->deviceTwinContextCallback = userContextCallback;
01362                 /* Codes_SRS_IOTHUBCLIENT_LL_10_005: [ Otherwise IoTHubClient_LL_SetDeviceTwinCallback shall succeed and return IOTHUB_CLIENT_OK.] */
01363                 result = IOTHUB_CLIENT_OK;
01364             }
01365             else
01366             {
01367                 /* Codes_SRS_IOTHUBCLIENT_LL_10_003: [ If the underlying layer's _Subscribe function fails, then IoTHubClient_LL_SetDeviceTwinCallback shall fail and return IOTHUB_CLIENT_ERROR.] */
01368                 result = IOTHUB_CLIENT_ERROR;
01369             }
01370         }
01371     }
01372     return result;
01373 }
01374 
01375 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)
01376 {
01377     IOTHUB_CLIENT_RESULT result;
01378     /* Codes_SRS_IOTHUBCLIENT_LL_10_012: [ IoTHubClient_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL. ] */
01379     /* Codes_SRS_IOTHUBCLIENT_LL_10_013: [ IoTHubClient_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter reportedState is NULL] */
01380     /* Codes_SRS_IOTHUBCLIENT_LL_07_005: [ IoTHubClient_LL_SendReportedState shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter size is equal to 0. ] */
01381     if (iotHubClientHandle == NULL || (reportedState == NULL || size == 0) )
01382     {
01383         result = IOTHUB_CLIENT_INVALID_ARG;
01384         LogError("Invalid argument specified iothubClientHandle=%p, reportedState=%p, size=%zu", iotHubClientHandle, reportedState, size);
01385     }
01386     else
01387     {
01388         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01389         /* Codes_SRS_IOTHUBCLIENT_LL_10_014: [IoTHubClient_LL_SendReportedState shall construct and queue the reported a Device_Twin structure for transmition by the underlying transport.] */
01390         IOTHUB_DEVICE_TWIN* client_data = dev_twin_data_create(handleData, get_next_item_id(handleData), reportedState, size, reportedStateCallback, userContextCallback);
01391         if (client_data == NULL)
01392         {
01393             /* Codes_SRS_IOTHUBCLIENT_LL_10_015: [If any error is encountered IoTHubClient_LL_SendReportedState shall return IOTHUB_CLIENT_ERROR.] */
01394             LogError("Failure constructing device twin data");
01395             result = IOTHUB_CLIENT_ERROR;
01396         }
01397         else
01398         {
01399             if (handleData->IoTHubTransport_Subscribe_DeviceTwin(handleData->transportHandle) != 0)
01400             {
01401                 LogError("Failure adding device twin data to queue");
01402                 device_twin_data_destroy(client_data);
01403                 result = IOTHUB_CLIENT_ERROR;
01404             }
01405             else
01406             {
01407                 /* Codes_SRS_IOTHUBCLIENT_LL_07_001: [ IoTHubClient_LL_SendReportedState shall queue the constructed reportedState data to be consumed by the targeted transport. ] */
01408                 DList_InsertTailList(&(iotHubClientHandle->iot_msg_queue), &(client_data->entry));
01409 
01410                 /* Codes_SRS_IOTHUBCLIENT_LL_10_016: [ Otherwise IoTHubClient_LL_SendReportedState shall succeed and return IOTHUB_CLIENT_OK.] */
01411                 result = IOTHUB_CLIENT_OK;
01412             }
01413         }
01414     }
01415     return result;
01416 }
01417 
01418 IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetDeviceMethodCallback(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback)
01419 {
01420     IOTHUB_CLIENT_RESULT result;
01421 
01422     /*Codes_SRS_IOTHUBCLIENT_LL_12_017: [ IoTHubClient_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_INVALID_ARG if parameter iotHubClientHandle is NULL. ] */
01423     if (iotHubClientHandle == NULL)
01424     {
01425         result = IOTHUB_CLIENT_INVALID_ARG;
01426         LOG_ERROR_RESULT;
01427     }
01428     else
01429     {
01430         IOTHUB_CLIENT_LL_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_HANDLE_DATA*)iotHubClientHandle;
01431         if (deviceMethodCallback == NULL)
01432         {
01433             /*Codes_SRS_IOTHUBCLIENT_LL_12_018: [If deviceMethodCallback is NULL, then IoTHubClient_LL_SetDeviceMethodCallback shall call the underlying layer's IoTHubTransport_Unsubscribe_DeviceMethod function and return IOTHUB_CLIENT_OK. ] */
01434             /*Codes_SRS_IOTHUBCLIENT_LL_12_022: [ Otherwise IoTHubClient_LL_SetDeviceMethodCallback shall succeed and return IOTHUB_CLIENT_OK. ]*/
01435             handleData->IoTHubTransport_Unsubscribe_DeviceMethod(handleData->transportHandle);
01436             handleData->deviceMethodCallback = NULL;
01437             result = IOTHUB_CLIENT_OK;
01438         }
01439         else
01440         {
01441             /*Codes_SRS_IOTHUBCLIENT_LL_12_019: [ If deviceMethodCallback is not NULL, then IoTHubClient_LL_SetDeviceMethodCallback shall call the underlying layer's IoTHubTransport_Subscribe_DeviceMethod function. ]*/
01442             if (handleData->IoTHubTransport_Subscribe_DeviceMethod(handleData->deviceHandle) == 0)
01443             {
01444                 /*Codes_SRS_IOTHUBCLIENT_LL_12_022: [ Otherwise IoTHubClient_LL_SetDeviceMethodCallback shall succeed and return IOTHUB_CLIENT_OK. ]*/
01445                 handleData->deviceMethodCallback = deviceMethodCallback;
01446                 handleData->deviceMethodUserContextCallback = userContextCallback;
01447                 result = IOTHUB_CLIENT_OK;
01448             }
01449             else
01450             {
01451                 /*Codes_SRS_IOTHUBCLIENT_LL_12_020: [ If the underlying layer's IoTHubTransport_Subscribe_DeviceMethod function fails, then IoTHubClient_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ]*/
01452                 /*Codes_SRS_IOTHUBCLIENT_LL_12_021: [ If adding the information fails for any reason, IoTHubClient_LL_SetDeviceMethodCallback shall fail and return IOTHUB_CLIENT_ERROR. ]*/
01453                 result = IOTHUB_CLIENT_ERROR;
01454             }
01455         }
01456     }
01457     return result;
01458 }
01459 
01460 #ifndef DONT_USE_UPLOADTOBLOB
01461 IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size)
01462 {
01463     IOTHUB_CLIENT_RESULT result;
01464     /*Codes_SRS_IOTHUBCLIENT_LL_02_061: [ If iotHubClientHandle is NULL then IoTHubClient_LL_UploadToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/
01465     /*Codes_SRS_IOTHUBCLIENT_LL_02_062: [ If destinationFileName is NULL then IoTHubClient_LL_UploadToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/
01466     if (
01467         (iotHubClientHandle == NULL) ||
01468         (destinationFileName == NULL) ||
01469         ((source == NULL) && (size >0))
01470         )
01471     {
01472         LogError("invalid parameters IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle=%p, const char* destinationFileName=%s, const unsigned char* source=%p, size_t size=%zu", iotHubClientHandle, destinationFileName, source, size);
01473         result = IOTHUB_CLIENT_INVALID_ARG;
01474     }
01475     else
01476     {
01477         result = IoTHubClient_LL_UploadToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, source, size);
01478     }
01479     return result;
01480 }
01481 #endif