Azure IoT / serializer

Dependents:   sht15_remote_monitoring f767zi_mqtt remote_monitoring simplesample_amqp ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers commanddecoder.c Source File

commanddecoder.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 #include "azure_c_shared_utility/optimize_size.h"
00006 #include "azure_c_shared_utility/gballoc.h"
00007 
00008 #include <stddef.h>
00009 
00010 #include "commanddecoder.h"
00011 #include "multitree.h"
00012 #include "azure_c_shared_utility/crt_abstractions.h"
00013 #include "azure_c_shared_utility/xlogging.h"
00014 #include "schema.h"
00015 #include "codefirst.h"
00016 #include "jsondecoder.h"
00017 
00018 DEFINE_ENUM_STRINGS(COMMANDDECODER_RESULT, COMMANDDECODER_RESULT_VALUES);
00019 
00020 typedef struct COMMAND_DECODER_HANDLE_DATA_TAG
00021 {
00022     METHOD_CALLBACK_FUNC methodCallback;
00023     void* methodCallbackContext;
00024     SCHEMA_MODEL_TYPE_HANDLE ModelHandle;
00025     ACTION_CALLBACK_FUNC ActionCallback;
00026     void* ActionCallbackContext;
00027 } COMMAND_DECODER_HANDLE_DATA;
00028 
00029 static int DecodeValueFromNode(SCHEMA_HANDLE schemaHandle, AGENT_DATA_TYPE* agentDataType, MULTITREE_HANDLE node, const char* edmTypeName)
00030 {
00031     /* because "pottentially uninitialized variable on MS compiler" */
00032     int result = 0;
00033     const char* argStringValue;
00034     AGENT_DATA_TYPE_TYPE primitiveType;
00035 
00036     /* Codes_SRS_COMMAND_DECODER_99_029:[ If the argument type is complex then a complex type value shall be built from the child nodes.] */
00037     if ((primitiveType = CodeFirst_GetPrimitiveType(edmTypeName)) == EDM_NO_TYPE)
00038     {
00039         SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle;
00040         size_t propertyCount;
00041 
00042         /* Codes_SRS_COMMAND_DECODER_99_033:[ In order to determine which are the members of a complex types, Schema APIs for structure types shall be used.] */
00043         if (((structTypeHandle = Schema_GetStructTypeByName(schemaHandle, edmTypeName)) == NULL) ||
00044             (Schema_GetStructTypePropertyCount(structTypeHandle, &propertyCount) != SCHEMA_OK))
00045         {
00046             /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00047             result = __FAILURE__;
00048             LogError("Getting Struct information failed.");
00049         }
00050         else
00051         {
00052             if (propertyCount == 0)
00053             {
00054                 /* Codes_SRS_COMMAND_DECODER_99_034:[ If Schema APIs indicate that a complex type has 0 members then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00055                 result = __FAILURE__;
00056                 LogError("Struct type with 0 members is not allowed");
00057             }
00058             else
00059             {
00060                 AGENT_DATA_TYPE* memberValues = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* propertyCount);
00061                 if (memberValues == NULL)
00062                 {
00063                     /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00064                     result = __FAILURE__;
00065                     LogError("Failed allocating member values for command argument");
00066                 }
00067                 else
00068                 {
00069                     const char** memberNames = (const char**)malloc(sizeof(const char*)* propertyCount);
00070                     if (memberNames == NULL)
00071                     {
00072                         /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00073                         result = __FAILURE__;
00074                         LogError("Failed allocating member names for command argument.");
00075                     }
00076                     else
00077                     {
00078                         size_t j;
00079                         size_t k;
00080 
00081                         for (j = 0; j < propertyCount; j++)
00082                         {
00083                             SCHEMA_PROPERTY_HANDLE propertyHandle;
00084                             MULTITREE_HANDLE memberNode;
00085                             const char* propertyName;
00086                             const char* propertyType;
00087 
00088                             if ((propertyHandle = Schema_GetStructTypePropertyByIndex(structTypeHandle, j)) == NULL)
00089                             {
00090                                 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00091                                 result = __FAILURE__;
00092                                 LogError("Getting struct member failed.");
00093                                 break;
00094                             }
00095                             else if (((propertyName = Schema_GetPropertyName(propertyHandle)) == NULL) ||
00096                                      ((propertyType = Schema_GetPropertyType(propertyHandle)) == NULL))
00097                             {
00098                                 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00099                                 result = __FAILURE__;
00100                                 LogError("Getting the struct member information failed.");
00101                                 break;
00102                             }
00103                             else
00104                             {
00105                                 memberNames[j] = propertyName;
00106 
00107                                 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
00108                                 if (MultiTree_GetChildByName(node, memberNames[j], &memberNode) != MULTITREE_OK)
00109                                 {
00110                                     /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00111                                     result = __FAILURE__;
00112                                     LogError("Getting child %s failed", propertyName);
00113                                     break;
00114                                 }
00115                                 /* Codes_SRS_COMMAND_DECODER_99_032:[ Nesting shall be supported for complex type.] */
00116                                 else if ((result = DecodeValueFromNode(schemaHandle, &memberValues[j], memberNode, propertyType)) != 0)
00117                                 {
00118                                     break;
00119                                 }
00120                             }
00121                         }
00122 
00123                         if (j == propertyCount)
00124                         {
00125                             /* Codes_SRS_COMMAND_DECODER_99_031:[ The complex type value that aggregates the children shall be built by using the Create_AGENT_DATA_TYPE_from_Members.] */
00126                             if (Create_AGENT_DATA_TYPE_from_Members(agentDataType, edmTypeName, propertyCount, (const char* const*)memberNames, memberValues) != AGENT_DATA_TYPES_OK)
00127                             {
00128                                 /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00129                                 result = __FAILURE__;
00130                                 LogError("Creating the agent data type from members failed.");
00131                             }
00132                             else
00133                             {
00134                                 result = 0;
00135                             }
00136                         }
00137 
00138                         for (k = 0; k < j; k++)
00139                         {
00140                             Destroy_AGENT_DATA_TYPE(&memberValues[k]);
00141                         }
00142 
00143                         free((void*)memberNames);
00144                     }
00145 
00146                     free(memberValues);
00147                 }
00148             }
00149         }
00150     }
00151     else
00152     {
00153         /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
00154         if (MultiTree_GetValue(node, (const void **)&argStringValue) != MULTITREE_OK)
00155         {
00156             /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00157             result = __FAILURE__;
00158             LogError("Getting the string from the multitree failed.");
00159         }
00160         /* Codes_SRS_COMMAND_DECODER_99_027:[ The value for an argument of primitive type shall be decoded by using the CreateAgentDataType_From_String API.] */
00161         else if (CreateAgentDataType_From_String(argStringValue, primitiveType, agentDataType) != AGENT_DATA_TYPES_OK)
00162         {
00163             /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00164             result = __FAILURE__;
00165             LogError("Failed parsing node %s.", argStringValue);
00166         }
00167     }
00168 
00169     return result;
00170 }
00171 
00172 static EXECUTE_COMMAND_RESULT DecodeAndExecuteModelAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeActionPath, const char* actionName, MULTITREE_HANDLE commandNode)
00173 {
00174     EXECUTE_COMMAND_RESULT result;
00175     char tempStr[128];
00176     size_t strLength = strlen(actionName);
00177 
00178     if (strLength <= 1)
00179     {
00180         /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00181         LogError("Invalid action name");
00182         result = EXECUTE_COMMAND_ERROR;
00183     }
00184     else if (strLength >= 128)
00185     {
00186         /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00187         LogError("Invalid action name length");
00188         result = EXECUTE_COMMAND_ERROR;
00189     }
00190     else
00191     {
00192         /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "Name" of the command JSON.] */
00193         SCHEMA_ACTION_HANDLE modelActionHandle;
00194         size_t argCount;
00195         MULTITREE_HANDLE parametersTreeNode;
00196 
00197         if (memcpy(tempStr, actionName, strLength-1) == NULL)
00198         {
00199             /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00200             LogError("Invalid action name.");
00201             result = EXECUTE_COMMAND_ERROR;
00202         }
00203         /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
00204         else if (MultiTree_GetChildByName(commandNode, "Parameters", &parametersTreeNode) != MULTITREE_OK)
00205         {
00206             /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00207             LogError("Error getting Parameters node.");
00208             result = EXECUTE_COMMAND_ERROR;
00209         }
00210         else
00211         {
00212             tempStr[strLength-1] = 0;
00213             /* Codes_SRS_COMMAND_DECODER_99_009:[ CommandDecoder shall call Schema_GetModelActionByName to obtain the information about a specific action.] */
00214             if (((modelActionHandle = Schema_GetModelActionByName(modelHandle, tempStr)) == NULL) ||
00215                 (Schema_GetModelActionArgumentCount(modelActionHandle, &argCount) != SCHEMA_OK))
00216             {
00217                 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00218                 LogError("Failed reading action %s from the schema", tempStr);
00219                 result = EXECUTE_COMMAND_ERROR;
00220             }
00221             else
00222             {
00223                 AGENT_DATA_TYPE* arguments = NULL;
00224 
00225                 if (argCount > 0)
00226                 {
00227                     arguments = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* argCount);
00228                 }
00229 
00230                 if ((argCount > 0) &&
00231                     (arguments == NULL))
00232                 {
00233                     /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00234                     LogError("Failed allocating arguments array");
00235                     result = EXECUTE_COMMAND_ERROR;
00236                 }
00237                 else
00238                 {
00239                     size_t i;
00240                     size_t j;
00241                     result = EXECUTE_COMMAND_ERROR;
00242 
00243                     /* Codes_SRS_COMMAND_DECODER_99_011:[ CommandDecoder shall attempt to extract from the command text the value for each action argument.] */
00244                     for (i = 0; i < argCount; i++)
00245                     {
00246                         SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle;
00247                         MULTITREE_HANDLE argumentNode;
00248                         const char* argName;
00249                         const char* argType;
00250 
00251                         if (((actionArgumentHandle = Schema_GetModelActionArgumentByIndex(modelActionHandle, i)) == NULL) ||
00252                             ((argName = Schema_GetActionArgumentName(actionArgumentHandle)) == NULL) ||
00253                             ((argType = Schema_GetActionArgumentType(actionArgumentHandle)) == NULL))
00254                         {
00255                             LogError("Failed getting the argument information from the schema");
00256                             result = EXECUTE_COMMAND_ERROR;
00257                             break;
00258                         }
00259                         /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
00260                         /* Codes_SRS_COMMAND_DECODER_01_008: [Each argument shall be looked up as a field, member of the "Parameters" node.]  */
00261                         else if (MultiTree_GetChildByName(parametersTreeNode, argName, &argumentNode) != MULTITREE_OK)
00262                         {
00263                             /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00264                             LogError("Missing argument %s", argName);
00265                             result = EXECUTE_COMMAND_ERROR;
00266                             break;
00267                         }
00268                         else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0)
00269                         {
00270                             result = EXECUTE_COMMAND_ERROR;
00271                             break;
00272                         }
00273                     }
00274 
00275                     if (i == argCount)
00276                     {
00277                         /* Codes_SRS_COMMAND_DECODER_99_005:[ If an Invoke Action is decoded successfully then the callback actionCallback shall be called, passing to it the callback action context, decoded name and arguments.] */
00278                         result = commandDecoderInstance->ActionCallback(commandDecoderInstance->ActionCallbackContext, relativeActionPath, tempStr, argCount, arguments);
00279                     }
00280 
00281                     for (j = 0; j < i; j++)
00282                     {
00283                         Destroy_AGENT_DATA_TYPE(&arguments[j]);
00284                     }
00285 
00286                     if (arguments != NULL)
00287                     {
00288                         free(arguments);
00289                     }
00290                 }
00291             }
00292         }
00293     }
00294     return result;
00295 }
00296 
00297 static METHODRETURN_HANDLE DecodeAndExecuteModelMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeMethodPath, const char* methodName, MULTITREE_HANDLE methodTree)
00298 {
00299     METHODRETURN_HANDLE result;
00300     size_t strLength = strlen(methodName);
00301 
00302     if (strLength == 0)
00303     {
00304         /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00305         LogError("Invalid method name");
00306         result = NULL;
00307     }
00308     else
00309     {
00310         SCHEMA_METHOD_HANDLE modelMethodHandle;
00311         size_t argCount;
00312 
00313 #ifdef _MSC_VER
00314 #pragma warning(suppress: 6324) /* We intentionally use here strncpy */
00315 #endif
00316 
00317         /*Codes_SRS_COMMAND_DECODER_02_020: [ CommandDecoder_ExecuteMethod shall verify that the model has a method called methodName. ]*/
00318         if (((modelMethodHandle = Schema_GetModelMethodByName(modelHandle, methodName)) == NULL) ||
00319             (Schema_GetModelMethodArgumentCount(modelMethodHandle, &argCount) != SCHEMA_OK))
00320         {
00321             /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00322             LogError("Failed reading method %s from the schema", methodName);
00323             result = NULL;
00324         }
00325         else
00326         {
00327             /*Codes_SRS_COMMAND_DECODER_02_021: [ For every argument of methodName, CommandDecoder_ExecuteMethod shall build an AGENT_DATA_TYPE from the node with the same name from the MULTITREE_HANDLE. ]*/
00328 
00329             if (argCount == 0)
00330             {
00331                 /*no need for any parameters*/
00332                 result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, 0, NULL);
00333             }
00334             else
00335             {
00336                 AGENT_DATA_TYPE* arguments;
00337                 arguments = (AGENT_DATA_TYPE*)malloc(sizeof(AGENT_DATA_TYPE)* argCount);
00338                 if (arguments == NULL)
00339                 {
00340                     LogError("Failed allocating arguments array");
00341                     result = NULL;
00342                 }
00343                 else
00344                 {
00345                     size_t i;
00346                     size_t j;
00347                     result = NULL;
00348 
00349                     for (i = 0; i < argCount; i++)
00350                     {
00351                         SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle;
00352                         MULTITREE_HANDLE argumentNode;
00353                         const char* argName;
00354                         const char* argType;
00355 
00356                         if (((methodArgumentHandle = Schema_GetModelMethodArgumentByIndex(modelMethodHandle, i)) == NULL) ||
00357                             ((argName = Schema_GetMethodArgumentName(methodArgumentHandle)) == NULL) ||
00358                             ((argType = Schema_GetMethodArgumentType(methodArgumentHandle)) == NULL))
00359                         {
00360                             /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00361                             LogError("Failed getting the argument information from the schema");
00362                             result = NULL;
00363                             break;
00364                         }
00365                         else if (MultiTree_GetChildByName(methodTree, argName, &argumentNode) != MULTITREE_OK)
00366                         {
00367                             /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00368                             LogError("Missing argument %s", argName);
00369                             result = NULL;
00370                             break;
00371                         }
00372                         else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0)
00373                         {
00374                             /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00375                             LogError("failure in DecodeValueFromNode");
00376                             result = NULL;
00377                             break;
00378                         }
00379                     }
00380 
00381                     if (i == argCount)
00382                     {
00383                         /*Codes_SRS_COMMAND_DECODER_02_022: [ CommandDecoder_ExecuteMethod shall call methodCallback passing the context, the methodName, number of arguments and the AGENT_DATA_TYPE. ]*/
00384                         /*Codes_SRS_COMMAND_DECODER_02_024: [ Otherwise, CommandDecoder_ExecuteMethod shall return what methodCallback returns. ]*/
00385                         result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, argCount, arguments);
00386                     }
00387 
00388                     for (j = 0; j < i; j++)
00389                     {
00390                         Destroy_AGENT_DATA_TYPE(&arguments[j]);
00391                     }
00392 
00393                     free(arguments);
00394                 }
00395 
00396             }
00397         }
00398 
00399     }
00400     return result;
00401 }
00402 
00403 
00404 static EXECUTE_COMMAND_RESULT ScanActionPathAndExecuteAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* actionPath, MULTITREE_HANDLE commandNode)
00405 {
00406     EXECUTE_COMMAND_RESULT result;
00407     char* relativeActionPath;
00408     const char* actionName = actionPath;
00409     SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle;
00410 
00411     /* Codes_SRS_COMMAND_DECODER_99_035:[ CommandDecoder_ExecuteCommand shall support paths to actions that are in child models (i.e. ChildModel/SomeAction.] */
00412     do
00413     {
00414         /* find the slash */
00415         const char* slashPos = strchr(actionName, '/');
00416         if (slashPos == NULL)
00417         {
00418             size_t relativeActionPathLength;
00419 
00420             /* Codes_SRS_COMMAND_DECODER_99_037:[ The relative path passed to the actionCallback shall be in the format "childModel1/childModel2/.../childModelN".] */
00421             if (actionName == actionPath)
00422             {
00423                 relativeActionPathLength = 0;
00424             }
00425             else
00426             {
00427                 relativeActionPathLength = actionName - actionPath - 1;
00428             }
00429 
00430             relativeActionPath = (char*)malloc(relativeActionPathLength + 1);
00431             if (relativeActionPath == NULL)
00432             {
00433                 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00434                 LogError("Failed allocating relative action path");
00435                 result = EXECUTE_COMMAND_ERROR;
00436             }
00437             else
00438             {
00439                 strncpy(relativeActionPath, actionPath, relativeActionPathLength);
00440                 relativeActionPath[relativeActionPathLength] = 0;
00441 
00442                 /* no slash found, this must be an action */
00443                 result = DecodeAndExecuteModelAction(commandDecoderInstance, schemaHandle, modelHandle, relativeActionPath, actionName, commandNode);
00444 
00445                 free(relativeActionPath);
00446                 actionName = NULL;
00447             }
00448             break;
00449         }
00450         else
00451         {
00452             /* found a slash, get the child model name */
00453             size_t modelLength = slashPos - actionName;
00454             char* childModelName = (char*)malloc(modelLength + 1);
00455             if (childModelName == NULL)
00456             {
00457                 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00458                 LogError("Failed allocating child model name");
00459                 result = EXECUTE_COMMAND_ERROR;
00460                 break;
00461             }
00462             else
00463             {
00464                 strncpy(childModelName, actionName, modelLength);
00465                 childModelName[modelLength] = 0;
00466 
00467                 /* find the model */
00468                 modelHandle = Schema_GetModelModelByName(modelHandle, childModelName);
00469                 if (modelHandle == NULL)
00470                 {
00471                     /* Codes_SRS_COMMAND_DECODER_99_036:[ If a child model cannot be found by using Schema APIs then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00472                     LogError("Getting the model %s failed", childModelName);
00473                     free(childModelName);
00474                     result = EXECUTE_COMMAND_ERROR;
00475                     break;
00476                 }
00477                 else
00478                 {
00479                     free(childModelName);
00480                     actionName = slashPos + 1;
00481                     result = EXECUTE_COMMAND_ERROR; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/
00482                 }
00483             }
00484         }
00485     } while (actionName != NULL);
00486     return result;
00487 }
00488 
00489 static METHODRETURN_HANDLE ScanMethodPathAndExecuteMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* fullMethodName, MULTITREE_HANDLE methodTree)
00490 {
00491     METHODRETURN_HANDLE result;
00492     char* relativeMethodPath;
00493     const char* methodName = fullMethodName;
00494     SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle;
00495 
00496     /*Codes_SRS_COMMAND_DECODER_02_018: [ CommandDecoder_ExecuteMethod shall validate that consecutive segments of the fullMethodName exist in the model. ]*/
00497     /*Codes_SRS_COMMAND_DECODER_02_019: [ CommandDecoder_ExecuteMethod shall locate the final model to which the methodName applies. ]*/
00498     do
00499     {
00500         /* find the slash */
00501         const char* slashPos = strchr(methodName, '/');
00502         if (slashPos == NULL)
00503         {
00504             size_t relativeMethodPathLength;
00505 
00506             if (methodName == fullMethodName)
00507             {
00508                 relativeMethodPathLength = 0;
00509             }
00510             else
00511             {
00512                 relativeMethodPathLength = methodName - fullMethodName - 1;
00513             }
00514 
00515             relativeMethodPath = (char*)malloc(relativeMethodPathLength + 1);
00516             if (relativeMethodPath == NULL)
00517             {
00518                 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00519                 LogError("Failed allocating relative method path");
00520                 result = NULL;
00521             }
00522             else
00523             {
00524                 strncpy(relativeMethodPath, fullMethodName, relativeMethodPathLength);
00525                 relativeMethodPath[relativeMethodPathLength] = 0;
00526 
00527                 /* no slash found, this must be an method */
00528                 result = DecodeAndExecuteModelMethod(commandDecoderInstance, schemaHandle, modelHandle, relativeMethodPath, methodName, methodTree);
00529 
00530                 free(relativeMethodPath);
00531                 methodName = NULL;
00532             }
00533             break;
00534         }
00535         else
00536         {
00537             /* found a slash, get the child model name */
00538             size_t modelLength = slashPos - methodName;
00539             char* childModelName = (char*)malloc(modelLength + 1);
00540             if (childModelName == NULL)
00541             {
00542                 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00543                 LogError("Failed allocating child model name");
00544                 result = NULL;
00545                 break;
00546             }
00547             else
00548             {
00549                 strncpy(childModelName, methodName, modelLength);
00550                 childModelName[modelLength] = 0;
00551 
00552                 /* find the model */
00553                 modelHandle = Schema_GetModelModelByName(modelHandle, childModelName);
00554                 if (modelHandle == NULL)
00555                 {
00556                     /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
00557                     LogError("Getting the model %s failed", childModelName);
00558                     free(childModelName);
00559                     result = NULL;
00560                     break;
00561                 }
00562                 else
00563                 {
00564                     free(childModelName);
00565                     methodName = slashPos + 1;
00566                     result = NULL; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/
00567                 }
00568             }
00569         }
00570     } while (methodName != NULL);
00571     return result;
00572 }
00573 
00574 static EXECUTE_COMMAND_RESULT DecodeCommand(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, MULTITREE_HANDLE commandNode)
00575 {
00576     EXECUTE_COMMAND_RESULT result;
00577     SCHEMA_HANDLE schemaHandle;
00578 
00579     /* Codes_SRS_COMMAND_DECODER_99_022:[ CommandDecoder shall use the Schema APIs to obtain the information about the entity set name and namespace] */
00580     if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL)
00581     {
00582         /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00583         LogError("Getting schema information failed");
00584         result = EXECUTE_COMMAND_ERROR;
00585     }
00586     else
00587     {
00588         const char* actionName;
00589         MULTITREE_HANDLE nameTreeNode;
00590 
00591         /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
00592         /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "name" of the command JSON.] */
00593         if ((MultiTree_GetChildByName(commandNode, "Name", &nameTreeNode) != MULTITREE_OK) ||
00594             (MultiTree_GetValue(nameTreeNode, (const void **)&actionName) != MULTITREE_OK))
00595         {
00596             /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00597             LogError("Getting action name failed.");
00598             result = EXECUTE_COMMAND_ERROR;
00599         }
00600         else if (strlen(actionName) < 2)
00601         {
00602             /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
00603             LogError("Invalid action name.");
00604             result = EXECUTE_COMMAND_ERROR;
00605         }
00606         else
00607         {
00608             actionName++;
00609             result = ScanActionPathAndExecuteAction(commandDecoderInstance, schemaHandle, actionName, commandNode);
00610         }
00611     }
00612     return result;
00613 }
00614 
00615 static METHODRETURN_HANDLE DecodeMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, const char* fullMethodName, MULTITREE_HANDLE methodTree)
00616 {
00617     METHODRETURN_HANDLE result;
00618     SCHEMA_HANDLE schemaHandle;
00619 
00620     /*Codes_SRS_COMMAND_DECODER_02_017: [ CommandDecoder_ExecuteMethod shall get the SCHEMA_HANDLE associated with the modelHandle passed at CommandDecoder_Create. ]*/
00621     if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL)
00622     {
00623         LogError("Getting schema information failed");
00624         result = NULL;
00625     }
00626     else
00627     {
00628         result = ScanMethodPathAndExecuteMethod(commandDecoderInstance, schemaHandle, fullMethodName, methodTree);
00629 
00630     }
00631     return result;
00632 }
00633 
00634 /*Codes_SRS_COMMAND_DECODER_01_009: [Whenever CommandDecoder_ExecuteCommand is the command shall be decoded and further dispatched to the actionCallback passed in CommandDecoder_Create.]*/
00635 EXECUTE_COMMAND_RESULT CommandDecoder_ExecuteCommand(COMMAND_DECODER_HANDLE handle, const char* command)
00636 {
00637     EXECUTE_COMMAND_RESULT result;
00638     COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
00639     /*Codes_SRS_COMMAND_DECODER_01_010: [If either the buffer or the receiveCallbackContext argument is NULL, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00640     if (
00641         (command == NULL) ||
00642         (commandDecoderInstance == NULL)
00643     )
00644     {
00645         LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* command=%p", handle, command);
00646         result = EXECUTE_COMMAND_ERROR;
00647     }
00648     else
00649     {
00650         size_t size = strlen(command);
00651         char* commandJSON;
00652 
00653         /* Codes_SRS_COMMAND_DECODER_01_011: [If the size of the command is 0 then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00654         if (size == 0)
00655         {
00656             LogError("Failed because command size is zero");
00657             result = EXECUTE_COMMAND_ERROR;
00658         }
00659         /*Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
00660         else if ((commandJSON = (char*)malloc(size + 1)) == NULL)
00661         {
00662             LogError("Failed to allocate temporary storage for the commands JSON");
00663             result = EXECUTE_COMMAND_ERROR;
00664         }
00665         else
00666         {
00667             MULTITREE_HANDLE commandsTree;
00668 
00669             (void)memcpy(commandJSON, command, size);
00670             commandJSON[size] = '\0';
00671 
00672             /* Codes_SRS_COMMAND_DECODER_01_012: [CommandDecoder shall decode the command JSON contained in buffer to a multi-tree by using JSONDecoder_JSON_To_MultiTree.] */
00673             if (JSONDecoder_JSON_To_MultiTree(commandJSON, &commandsTree) != JSON_DECODER_OK)
00674             {
00675                 /* Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
00676                 LogError("Decoding JSON to a multi tree failed");
00677                 result = EXECUTE_COMMAND_ERROR;
00678             }
00679             else
00680             {
00681                 result = DecodeCommand(commandDecoderInstance, commandsTree);
00682 
00683                 /* Codes_SRS_COMMAND_DECODER_01_016: [CommandDecoder shall ensure that the multi-tree resulting from JSONDecoder_JSON_To_MultiTree is freed after the commands are executed.] */
00684                 MultiTree_Destroy(commandsTree);
00685             }
00686 
00687             free(commandJSON);
00688         }
00689     }
00690     return result;
00691 }
00692 
00693 METHODRETURN_HANDLE CommandDecoder_ExecuteMethod(COMMAND_DECODER_HANDLE handle, const char* fullMethodName, const char* methodPayload)
00694 {
00695     METHODRETURN_HANDLE result;
00696     /*Codes_SRS_COMMAND_DECODER_02_014: [ If handle is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
00697     /*Codes_SRS_COMMAND_DECODER_02_015: [ If fulMethodName is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
00698     if (
00699         (handle == NULL) ||
00700         (fullMethodName == NULL) /*methodPayload can be NULL*/
00701         )
00702     {
00703         LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* fullMethodName=%p", handle, fullMethodName);
00704         result = NULL;
00705     }
00706     else
00707     {
00708         COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
00709         /*Codes_SRS_COMMAND_DECODER_02_025: [ If methodCallback is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
00710         if (commandDecoderInstance->methodCallback == NULL)
00711         {
00712             LogError("unable to execute a method when the methodCallback passed in CommandDecoder_Create is NULL");
00713             result = NULL;
00714         }
00715         else
00716         {
00717             /*Codes_SRS_COMMAND_DECODER_02_016: [ If methodPayload is not NULL then CommandDecoder_ExecuteMethod shall build a MULTITREE_HANDLE out of methodPayload. ]*/
00718             if (methodPayload == NULL)
00719             {
00720                 result = DecodeMethod(commandDecoderInstance, fullMethodName, NULL);
00721             }
00722             else
00723             {
00724                 char* methodJSON;
00725 
00726                 if (mallocAndStrcpy_s(&methodJSON, methodPayload) != 0)
00727                 {
00728                     LogError("Failed to allocate temporary storage for the method JSON");
00729                     result = NULL;
00730                 }
00731                 else
00732                 {
00733                     MULTITREE_HANDLE methodTree;
00734                     if (JSONDecoder_JSON_To_MultiTree(methodJSON, &methodTree) != JSON_DECODER_OK)
00735                     {
00736                         LogError("Decoding JSON to a multi tree failed");
00737                         result = NULL;
00738                     }
00739                     else
00740                     {
00741                         result = DecodeMethod(commandDecoderInstance, fullMethodName, methodTree);
00742                         MultiTree_Destroy(methodTree);
00743                     }
00744                     free(methodJSON);
00745                 }
00746             }
00747         }
00748     }
00749     return result;
00750 }
00751 
00752 
00753 COMMAND_DECODER_HANDLE CommandDecoder_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, ACTION_CALLBACK_FUNC actionCallback, void* actionCallbackContext, METHOD_CALLBACK_FUNC methodCallback, void* methodCallbackContext)
00754 {
00755     COMMAND_DECODER_HANDLE_DATA* result;
00756     /* Codes_SRS_COMMAND_DECODER_99_019:[ For all exposed APIs argument validity checks shall precede other checks.] */
00757     /* Codes_SRS_COMMAND_DECODER_01_003: [ If modelHandle is NULL, CommandDecoder_Create shall return NULL. ]*/
00758     if (modelHandle == NULL)
00759     {
00760         LogError("Invalid arguments: SCHEMA_MODEL_TYPE_HANDLE modelHandle=%p, ACTION_CALLBACK_FUNC actionCallback=%p, void* actionCallbackContext=%p, METHOD_CALLBACK_FUNC methodCallback=%p, void* methodCallbackContext=%p",
00761             modelHandle, actionCallback, actionCallbackContext, methodCallback, methodCallbackContext);
00762         result = NULL;
00763     }
00764     else
00765     {
00766         /* Codes_SRS_COMMAND_DECODER_01_001: [CommandDecoder_Create shall create a new instance of a CommandDecoder.] */
00767         result = malloc(sizeof(COMMAND_DECODER_HANDLE_DATA));
00768         if (result == NULL)
00769         {
00770             /* Codes_SRS_COMMAND_DECODER_01_004: [If any error is encountered during CommandDecoder_Create CommandDecoder_Create shall return NULL.] */
00771             /*return as is*/
00772         }
00773         else
00774         {
00775             result->ModelHandle = modelHandle;
00776             result->ActionCallback = actionCallback;
00777             result->ActionCallbackContext = actionCallbackContext;
00778             result->methodCallback = methodCallback;
00779             result->methodCallbackContext = methodCallbackContext;
00780         }
00781     }
00782 
00783     return result;
00784 }
00785 
00786 void CommandDecoder_Destroy(COMMAND_DECODER_HANDLE commandDecoderHandle)
00787 {
00788     /* Codes_SRS_COMMAND_DECODER_01_007: [If CommandDecoder_Destroy is called with a NULL handle, CommandDecoder_Destroy shall do nothing.] */
00789     if (commandDecoderHandle != NULL)
00790     {
00791         COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)commandDecoderHandle;
00792 
00793         /* Codes_SRS_COMMAND_DECODER_01_005: [CommandDecoder_Destroy shall free all resources associated with the commandDecoderHandle instance.] */
00794         free(commandDecoderInstance);
00795     }
00796 }
00797 
00798 DEFINE_ENUM_STRINGS(AGENT_DATA_TYPE_TYPE, AGENT_DATA_TYPE_TYPE_VALUES);
00799 
00800 /*validates that the multitree (coming from a JSON) is actually a serialization of the model (complete or incomplete)*/
00801 /*if the serialization contains more than the model, then it fails.*/
00802 /*if the serialization does not contain mandatory items from the model, it fails*/
00803 static bool validateModel_vs_Multitree(void* startAddress, SCHEMA_MODEL_TYPE_HANDLE modelHandle, MULTITREE_HANDLE desiredPropertiesTree, size_t offset)
00804 {
00805 
00806     bool result;
00807     size_t nChildren;
00808     size_t nProcessedChildren = 0;
00809     (void)MultiTree_GetChildCount(desiredPropertiesTree, &nChildren);
00810     for (size_t i = 0;i < nChildren;i++)
00811     {
00812         MULTITREE_HANDLE child;
00813         if (MultiTree_GetChild(desiredPropertiesTree, i, &child) != MULTITREE_OK)
00814         {
00815             LogError("failure in MultiTree_GetChild");
00816             i = nChildren;
00817         }
00818         else
00819         {
00820             STRING_HANDLE childName = STRING_new();
00821             if (childName == NULL)
00822             {
00823                 LogError("failure to STRING_new");
00824                 i = nChildren;
00825             }
00826             else
00827             {
00828                 if (MultiTree_GetName(child, childName) != MULTITREE_OK)
00829                 {
00830                     LogError("failure to MultiTree_GetName");
00831                     i = nChildren;
00832                 }
00833                 else
00834                 {
00835                     const char *childName_str = STRING_c_str(childName);
00836                     SCHEMA_MODEL_ELEMENT elementType = Schema_GetModelElementByName(modelHandle, childName_str);
00837                     switch (elementType.elementType)
00838                     {
00839                         default:
00840                         {
00841                             LogError("INTERNAL ERROR: unexpected function return");
00842                             i = nChildren;
00843                             break;
00844                         }
00845                         case (SCHEMA_PROPERTY):
00846                         {
00847                             LogError("cannot ingest name (WITH_DATA instead of WITH_DESIRED_PROPERTY): %s", childName_str);
00848                             i = nChildren;
00849                             break;
00850                         }
00851                         case (SCHEMA_REPORTED_PROPERTY):
00852                         {
00853                             LogError("cannot ingest name (WITH_REPORTED_PROPERTY instead of WITH_DESIRED_PROPERTY): %s", childName_str);
00854                             i = nChildren;
00855                             break;
00856                         }
00857                         case (SCHEMA_DESIRED_PROPERTY):
00858                         {
00859                             /*Codes_SRS_COMMAND_DECODER_02_007: [ If the child name corresponds to a desired property then an AGENT_DATA_TYPE shall be constructed from the MULTITREE node. ]*/
00860                             SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = elementType.elementHandle.desiredPropertyHandle;
00861 
00862                             const char* desiredPropertyType = Schema_GetModelDesiredPropertyType(desiredPropertyHandle);
00863                             AGENT_DATA_TYPE output;
00864                             if (DecodeValueFromNode(Schema_GetSchemaForModelType(modelHandle), &output, child, desiredPropertyType) != 0)
00865                             {
00866                                 LogError("failure in DecodeValueFromNode");
00867                                 i = nChildren;
00868                             }
00869                             else
00870                             {
00871                                 /*Codes_SRS_COMMAND_DECODER_02_008: [ The desired property shall be constructed in memory by calling pfDesiredPropertyFromAGENT_DATA_TYPE. ]*/
00872                                 pfDesiredPropertyFromAGENT_DATA_TYPE leFunction = Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(desiredPropertyHandle);
00873                                 if (leFunction(&output, (char*)startAddress + offset + Schema_GetModelDesiredProperty_offset(desiredPropertyHandle)) != 0)
00874                                 {
00875                                     LogError("failure in a function that converts from AGENT_DATA_TYPE to C data");
00876                                 }
00877                                 else
00878                                 {
00879                                     /*Codes_SRS_COMMAND_DECODER_02_013: [ If the desired property has a non-NULL pfOnDesiredProperty then it shall be called. ]*/
00880                                     pfOnDesiredProperty onDesiredProperty = Schema_GetModelDesiredProperty_pfOnDesiredProperty(desiredPropertyHandle);
00881                                     if (onDesiredProperty != NULL)
00882                                     {
00883                                         onDesiredProperty((char*)startAddress + offset);
00884                                     }
00885                                     nProcessedChildren++;
00886                                 }
00887                                 Destroy_AGENT_DATA_TYPE(&output);
00888                             }
00889 
00890                             break;
00891                         }
00892                         case(SCHEMA_MODEL_IN_MODEL):
00893                         {
00894                             SCHEMA_MODEL_TYPE_HANDLE modelModel = elementType.elementHandle.modelHandle;
00895 
00896                             /*Codes_SRS_COMMAND_DECODER_02_009: [ If the child name corresponds to a model in model then the function shall call itself recursively. ]*/
00897                             if (!validateModel_vs_Multitree(startAddress, modelModel, child, offset + Schema_GetModelModelByName_Offset(modelHandle, childName_str)))
00898                             {
00899                                 LogError("failure in validateModel_vs_Multitree");
00900                                 i = nChildren;
00901                             }
00902                             else
00903                             {
00904                                 /*if the model in model so happened to be a WITH_DESIRED_PROPERTY... (only those has non_NULL pfOnDesiredProperty) */
00905                                 /*Codes_SRS_COMMAND_DECODER_02_012: [ If the child model in model has a non-NULL pfOnDesiredProperty then pfOnDesiredProperty shall be called. ]*/
00906                                 pfOnDesiredProperty onDesiredProperty = Schema_GetModelModelByName_OnDesiredProperty(modelHandle, childName_str);
00907                                 if (onDesiredProperty != NULL)
00908                                 {
00909                                     onDesiredProperty((char*)startAddress + offset);
00910                                 }
00911 
00912                                 nProcessedChildren++;
00913                             }
00914 
00915                             break;
00916                         }
00917 
00918                     } /*switch*/
00919                 }
00920                 STRING_delete(childName);
00921             }
00922         }
00923     }
00924 
00925     if(nProcessedChildren == nChildren)
00926     {
00927         /*Codes_SRS_COMMAND_DECODER_02_010: [ If the complete MULTITREE has been parsed then CommandDecoder_IngestDesiredProperties shall succeed and return EXECUTE_COMMAND_SUCCESS. ]*/
00928         result = true;
00929     }
00930     else
00931     {
00932         /*Codes_SRS_COMMAND_DECODER_02_011: [ Otherwise CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_FAILED. ]*/
00933         LogError("not all constituents of the JSON have been ingested");
00934         result = false;
00935     }
00936     return result;
00937 }
00938 
00939 static EXECUTE_COMMAND_RESULT DecodeDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE_DATA* handle, MULTITREE_HANDLE desiredPropertiesTree)
00940 {
00941     /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
00942     return validateModel_vs_Multitree(startAddress, handle->ModelHandle, desiredPropertiesTree, 0 )?EXECUTE_COMMAND_SUCCESS:EXECUTE_COMMAND_FAILED;
00943 }
00944 
00945 /* Raw JSON has properties we don't need; potentially nodes other than "desired" for full TWIN as well as a $version we don't pass to callees */
00946 static bool RemoveUnneededTwinProperties(MULTITREE_HANDLE initialParsedTree, bool parseDesiredNode, MULTITREE_HANDLE *desiredPropertiesTree)
00947 {
00948     MULTITREE_HANDLE updateTree;
00949     bool result;
00950 
00951     if (parseDesiredNode)
00952     {
00953         /*Codes_SRS_COMMAND_DECODER_02_014: [ If parseDesiredNode is TRUE, parse only the `desired` part of JSON tree ]*/
00954         if (MultiTree_GetChildByName(initialParsedTree, "desired", &updateTree) != MULTITREE_OK)
00955         {
00956             LogError("Unable to find 'desired' in tree");
00957             return false;
00958         }
00959     }
00960     else
00961     {
00962         // Tree already starts on node we want so just use it.
00963         updateTree = initialParsedTree;
00964     }
00965 
00966     /*Codes_COMMAND_DECODER_02_015: [ Remove '$version' string from node, if it is present.  It not being present is not an error ]*/
00967     MULTITREE_RESULT deleteChildResult = MultiTree_DeleteChild(updateTree, "$version");
00968     if ((deleteChildResult == MULTITREE_OK) || (deleteChildResult == MULTITREE_CHILD_NOT_FOUND))
00969     {
00970         *desiredPropertiesTree = updateTree;
00971         result = true;
00972     }
00973     else
00974     {
00975         *desiredPropertiesTree = NULL;
00976         result = false;
00977     }
00978 
00979     return result;
00980 }
00981 
00982 EXECUTE_COMMAND_RESULT CommandDecoder_IngestDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE handle, const char* jsonPayload, bool parseDesiredNode)
00983 {
00984     EXECUTE_COMMAND_RESULT result;
00985 
00986     /*Codes_SRS_COMMAND_DECODER_02_001: [ If startAddress is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
00987     /*Codes_SRS_COMMAND_DECODER_02_002: [ If handle is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
00988     /*Codes_SRS_COMMAND_DECODER_02_003: [ If jsonPayload is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
00989     if(
00990         (startAddress == NULL) ||
00991         (handle == NULL) ||
00992         (jsonPayload == NULL)
00993         )
00994     {
00995         LogError("invalid argument COMMAND_DECODER_HANDLE handle=%p, const char* jsonPayload=%p", handle, jsonPayload);
00996         result = EXECUTE_COMMAND_ERROR;
00997     }
00998     else
00999     {
01000         /*Codes_SRS_COMMAND_DECODER_02_004: [ CommandDecoder_IngestDesiredProperties shall clone desiredProperties. ]*/
01001         char* copy;
01002         if (mallocAndStrcpy_s(&copy, jsonPayload) != 0)
01003         {
01004             LogError("failure in mallocAndStrcpy_s");
01005             result = EXECUTE_COMMAND_FAILED;
01006         }
01007         else
01008         {
01009             /*Codes_SRS_COMMAND_DECODER_02_005: [ CommandDecoder_IngestDesiredProperties shall create a MULTITREE_HANDLE ouf of the clone of desiredProperties. ]*/
01010             MULTITREE_HANDLE initialParsedTree;
01011             MULTITREE_HANDLE desiredPropertiesTree;
01012 
01013             if (JSONDecoder_JSON_To_MultiTree(copy, &initialParsedTree) != JSON_DECODER_OK)
01014             {
01015                 LogError("Decoding JSON to a multi tree failed");
01016                 result = EXECUTE_COMMAND_ERROR;
01017             }
01018             else
01019             {
01020                 if (RemoveUnneededTwinProperties(initialParsedTree, parseDesiredNode, &desiredPropertiesTree) == false)
01021                 {
01022                     LogError("Removing unneeded twin properties failed");
01023                     result = EXECUTE_COMMAND_ERROR;
01024                 }
01025                 else
01026                 {
01027                     COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
01028 
01029                     /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
01030                     result = DecodeDesiredProperties(startAddress, commandDecoderInstance, desiredPropertiesTree);
01031 
01032                     // Do NOT free desiredPropertiesTree.  It is only a pointer into initialParsedTree.
01033                     MultiTree_Destroy(initialParsedTree);
01034                 }
01035             }
01036             free(copy);
01037         }
01038     }
01039     return result;
01040 }