diff --git a/include/asterisk/json.h b/include/asterisk/json.h index 8cb74a4596..28ebfbd51b 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -1010,13 +1010,24 @@ struct ast_party_id; */ struct ast_json *ast_json_party_id(struct ast_party_id *party); +enum ast_json_to_ast_vars_code { + /*! \brief Conversion successful */ + AST_JSON_TO_AST_VARS_CODE_SUCCESS, + /*! + * \brief Conversion failed because invalid value type supplied. + * \note Only string values allowed. + */ + AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE, + /*! \brief Conversion failed because of allocation failure. (Out Of Memory) */ + AST_JSON_TO_AST_VARS_CODE_OOM, +}; + /*! * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list * \since 12.5.0 * * \param json_variables The JSON blob containing the variable * \param variables An out reference to the variables to populate. - * The pointer to the variables should be NULL when calling this. * * \code * struct ast_json *json_variables = ast_json_pack("[ { s: s } ]", "foo", "bar"); @@ -1026,10 +1037,9 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party); * res = ast_json_to_ast_variables(json_variables, &variables); * \endcode * - * \retval 0 success - * \retval -1 error + * \return Conversion enum ast_json_to_ast_vars_code status */ -int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables); +enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables); /*!@}*/ diff --git a/main/json.c b/main/json.c index 88e8077729..35e6f16cef 100644 --- a/main/json.c +++ b/main/json.c @@ -882,32 +882,47 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party) return ast_json_ref(json_party_id); } -int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables) +enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables) { struct ast_json_iter *it_json_var; *variables = NULL; for (it_json_var = ast_json_object_iter(json_variables); it_json_var; - it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) { + it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) { struct ast_variable *new_var; const char *key = ast_json_object_iter_key(it_json_var); + const char *value; + struct ast_json *json_value; if (ast_strlen_zero(key)) { continue; } - new_var = ast_variable_new(key, - ast_json_string_get(ast_json_object_iter_value(it_json_var)), - ""); + json_value = ast_json_object_iter_value(it_json_var); + if (ast_json_typeof(json_value) != AST_JSON_STRING) { + /* Error: Only strings allowed */ + ast_variables_destroy(*variables); + *variables = NULL; + return AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE; + } + value = ast_json_string_get(json_value); + /* Should never be NULL. Otherwise, how could it be a string type? */ + ast_assert(value != NULL); + if (!value) { + /* To be safe. */ + continue; + } + new_var = ast_variable_new(key, value, ""); if (!new_var) { + /* Error: OOM */ ast_variables_destroy(*variables); *variables = NULL; - return -1; + return AST_JSON_TO_AST_VARS_CODE_OOM; } ast_variable_list_append(variables, new_var); } - return 0; + return AST_JSON_TO_AST_VARS_CODE_SUCCESS; } diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c index 693835129e..fb1aa039c6 100644 --- a/res/ari/resource_channels.c +++ b/res/ari/resource_channels.c @@ -1147,11 +1147,44 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, return; } +/*! + * \internal + * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list + * \since 13.3.0 + * + * \param[out] response HTTP response if error + * \param json_variables The JSON blob containing the variable + * \param[out] variables An out reference to the variables to populate. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables) +{ + enum ast_json_to_ast_vars_code res; + + res = ast_json_to_ast_variables(json_variables, variables); + switch (res) { + case AST_JSON_TO_AST_VARS_CODE_SUCCESS: + return 0; + case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE: + ast_ari_response_error(response, 400, "Bad Request", + "Only string values in the 'variables' object allowed"); + break; + case AST_JSON_TO_AST_VARS_CODE_OOM: + ast_ari_response_alloc_failed(response); + break; + } + ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n"); + + return -1; +} + void ast_ari_channels_originate_with_id(struct ast_variable *headers, struct ast_ari_channels_originate_with_id_args *args, struct ast_ari_response *response) { - RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy); + struct ast_variable *variables = NULL; /* Parse any query parameters out of the body parameter */ if (args->variables) { @@ -1159,12 +1192,9 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers, ast_ari_channels_originate_with_id_parse_body(args->variables, args); json_variables = ast_json_object_get(args->variables, "variables"); - if (json_variables) { - if (ast_json_to_ast_variables(json_variables, &variables)) { - ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n"); - ast_ari_response_alloc_failed(response); - return; - } + if (json_variables + && json_to_ast_variables(response, json_variables, &variables)) { + return; } } @@ -1183,13 +1213,14 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers, args->other_channel_id, args->originator, response); + ast_variables_destroy(variables); } void ast_ari_channels_originate(struct ast_variable *headers, struct ast_ari_channels_originate_args *args, struct ast_ari_response *response) { - RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy); + struct ast_variable *variables = NULL; /* Parse any query parameters out of the body parameter */ if (args->variables) { @@ -1197,12 +1228,9 @@ void ast_ari_channels_originate(struct ast_variable *headers, ast_ari_channels_originate_parse_body(args->variables, args); json_variables = ast_json_object_get(args->variables, "variables"); - if (json_variables) { - if (ast_json_to_ast_variables(json_variables, &variables)) { - ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n"); - ast_ari_response_alloc_failed(response); - return; - } + if (json_variables + && json_to_ast_variables(response, json_variables, &variables)) { + return; } } @@ -1221,6 +1249,7 @@ void ast_ari_channels_originate(struct ast_variable *headers, args->other_channel_id, args->originator, response); + ast_variables_destroy(variables); } void ast_ari_channels_get_channel_var(struct ast_variable *headers, diff --git a/res/ari/resource_endpoints.c b/res/ari/resource_endpoints.c index 4f91e781d4..f794969f29 100644 --- a/res/ari/resource_endpoints.c +++ b/res/ari/resource_endpoints.c @@ -220,35 +220,66 @@ static void send_message(const char *to, const char *from, const char *body, str response->response_text = "Accepted"; } +/*! + * \internal + * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list + * \since 13.3.0 + * + * \param[out] response HTTP response if error + * \param json_variables The JSON blob containing the variable + * \param[out] variables An out reference to the variables to populate. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables) +{ + enum ast_json_to_ast_vars_code res; + + res = ast_json_to_ast_variables(json_variables, variables); + switch (res) { + case AST_JSON_TO_AST_VARS_CODE_SUCCESS: + return 0; + case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE: + ast_ari_response_error(response, 400, "Bad Request", + "Only string values in the 'variables' object allowed"); + break; + case AST_JSON_TO_AST_VARS_CODE_OOM: + ast_ari_response_alloc_failed(response); + break; + } + ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n"); + + return -1; +} + void ast_ari_endpoints_send_message(struct ast_variable *headers, struct ast_ari_endpoints_send_message_args *args, struct ast_ari_response *response) { - RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy); + struct ast_variable *variables = NULL; if (args->variables) { struct ast_json *json_variables; ast_ari_endpoints_send_message_parse_body(args->variables, args); json_variables = ast_json_object_get(args->variables, "variables"); - if (json_variables) { - if (ast_json_to_ast_variables(json_variables, &variables)) { - ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n"); - ast_ari_response_alloc_failed(response); - return; - } + if (json_variables + && json_to_ast_variables(response, json_variables, &variables)) { + return; } } send_message(args->to, args->from, args->body, variables, response); + ast_variables_destroy(variables); } void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers, struct ast_ari_endpoints_send_message_to_endpoint_args *args, struct ast_ari_response *response) { - RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy); - RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup); + struct ast_variable *variables = NULL; + struct ast_endpoint_snapshot *snapshot; char msg_to[128]; char *tech = ast_strdupa(args->tech); @@ -259,23 +290,21 @@ void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers, "Endpoint not found"); return; } + ao2_ref(snapshot, -1); if (args->variables) { struct ast_json *json_variables; ast_ari_endpoints_send_message_to_endpoint_parse_body(args->variables, args); json_variables = ast_json_object_get(args->variables, "variables"); - - if (json_variables) { - if (ast_json_to_ast_variables(json_variables, &variables)) { - ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n"); - ast_ari_response_alloc_failed(response); - return; - } + if (json_variables + && json_to_ast_variables(response, json_variables, &variables)) { + return; } } snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource); send_message(msg_to, args->from, args->body, variables, response); + ast_variables_destroy(variables); } diff --git a/res/res_ari_endpoints.c b/res/res_ari_endpoints.c index 3ebe668585..8d8ed716c1 100644 --- a/res/res_ari_endpoints.c +++ b/res/res_ari_endpoints.c @@ -181,6 +181,7 @@ static void ast_ari_endpoints_send_message_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 400: /* Invalid parameters for sending a message. */ case 404: /* Endpoint not found */ is_valid = 1; break; diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json index e7b4ba73a1..17b884784a 100644 --- a/rest-api/api-docs/endpoints.json +++ b/rest-api/api-docs/endpoints.json @@ -63,6 +63,10 @@ } ], "errorResponses": [ + { + "code": 400, + "reason": "Invalid parameters for sending a message." + }, { "code": 404, "reason": "Endpoint not found"