void ast_ari_asterisk_get_global_var(struct ast_variable *headers, struct ast_ari_asterisk_get_global_var_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_str *, tmp, NULL, ast_free); const char *value; ast_assert(response != NULL); if (ast_strlen_zero(args->variable)) { ast_ari_response_error( response, 400, "Bad Request", "Variable name is required"); return; } tmp = ast_str_create(32); if (!tmp) { ast_ari_response_alloc_failed(response); return; } value = ast_str_retrieve_variable(&tmp, 0, NULL, NULL, args->variable); if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_ok(response, ast_json_ref(json)); }
void ast_ari_channels_list(struct ast_variable *headers, struct ast_ari_channels_list_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer(); cache = ast_channel_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } ao2_ref(cache, +1); snapshots = stasis_cache_dump(cache, ast_channel_snapshot_type()); if (!snapshots) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } for (i = ao2_iterator_init(snapshots, 0); (obj = ao2_iterator_next(&i)); ao2_cleanup(obj)) { RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); struct ast_channel_snapshot *snapshot = stasis_message_data(msg); int r; if (sanitize && sanitize->channel_snapshot && sanitize->channel_snapshot(snapshot)) { continue; } r = ast_json_array_append( json, ast_channel_snapshot_to_json(snapshot, NULL)); if (r != 0) { ast_ari_response_alloc_failed(response); ao2_cleanup(obj); ao2_iterator_destroy(&i); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
/*! * \brief Performs common setup for a bridge playback operation * with both new controls and when existing controls are found. * * \param args_media medias to play * \param args_media_count number of media items in \c media * \param args_lang language string split from arguments * \param args_offset_ms milliseconds offset split from arguments * \param args_playback_id string to use for playback split from * arguments (null valid) * \param response ARI response being built * \param bridge Bridge the playback is being peformed on * \param control Control being used for the playback channel * \param json contents of the response to ARI * \param playback_url stores playback URL for use with response * * \retval -1 operation failed * \retval operation was successful */ static int ari_bridges_play_helper(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge, struct stasis_app_control *control, struct ast_json **json, char **playback_url) { RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); const char *language; snapshot = stasis_app_control_get_snapshot(control); if (!snapshot) { ast_ari_response_error( response, 500, "Internal Error", "Failed to get control snapshot"); return -1; } language = S_OR(args_lang, snapshot->language); playback = stasis_app_control_play_uri(control, args_media, args_media_count, language, bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms, args_offset_ms, args_playback_id); if (!playback) { ast_ari_response_alloc_failed(response); return -1; } if (ast_asprintf(playback_url, "/playbacks/%s", stasis_app_playback_get_id(playback)) == -1) { playback_url = NULL; ast_ari_response_alloc_failed(response); return -1; } *json = stasis_app_playback_to_json(playback); if (!*json) { ast_ari_response_alloc_failed(response); return -1; } return 0; }
void ast_ari_asterisk_get_module(struct ast_variable *headers, struct ast_ari_asterisk_get_module_args *args, struct ast_ari_response *response) { struct ast_json *json; int module_retrieved = 0; ast_assert(response != NULL); if (!ast_module_check(args->module_name)) { ast_ari_response_error( response, 404, "Not Found", "Module could not be found in running modules"); return; } json = ast_json_object_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json, args->module_name); if (!module_retrieved) { ast_ari_response_error( response, 409, "Conflict", "Module information could not be retrieved"); return; } ast_ari_response_ok(response, json); }
static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj, struct ast_ari_response *response) { RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref); struct ast_variable *change_set; struct ast_variable *it_change_set; return_set = ast_json_array_create(); if (!return_set) { ast_ari_response_alloc_failed(response); return; } /* Note that we can't use the sorcery JSON change set directly, * as it will hand us back an Object (with fields), and we need * a more generic representation of whatever the API call asked * for, i.e., a list of tuples. */ change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj); if (!change_set) { ast_ari_response_alloc_failed(response); return; } for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) { struct ast_json *tuple; tuple = ast_json_pack("{s: s, s: s}", "attribute", it_change_set->name, "value", it_change_set->value); if (!tuple) { ast_variables_destroy(change_set); ast_ari_response_alloc_failed(response); return; } if (ast_json_array_append(return_set, tuple)) { ast_json_unref(tuple); ast_variables_destroy(change_set); ast_ari_response_alloc_failed(response); return; } } ast_variables_destroy(change_set); ast_ari_response_ok(response, ast_json_ref(return_set)); }
void ast_ari_bridges_list(struct ast_variable *headers, struct ast_ari_bridges_list_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; cache = ast_bridge_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } ao2_ref(cache, +1); snapshots = stasis_cache_dump(cache, ast_bridge_snapshot_type()); if (!snapshots) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } i = ao2_iterator_init(snapshots, 0); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); struct ast_bridge_snapshot *snapshot = stasis_message_data(msg); struct ast_json *json_bridge = ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); if (!json_bridge || ast_json_array_append(json, json_bridge)) { ao2_iterator_destroy(&i); ast_ari_response_alloc_failed(response); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
/*! * \brief Parameter parsing callback for /sounds. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_sounds_list_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response) { struct ast_ari_sounds_list_args args = {}; struct ast_variable *i; #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "lang") == 0) { args.lang = (i->value); } else if (strcmp(i->name, "format") == 0) { args.format = (i->value); } else {} } if (ast_ari_sounds_list_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_sounds_list(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_list(response->message, ast_ari_validate_sound_fn()); } else { ast_log(LOG_ERROR, "Invalid error response %d for /sounds\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /sounds\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) return; }
void ast_ari_recordings_list_stored(struct ast_variable *headers, struct ast_ari_recordings_list_stored_args *args, struct ast_ari_response *response) { RAII_VAR(struct ao2_container *, recordings, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; recordings = stasis_app_stored_recording_find_all(); if (!recordings) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } i = ao2_iterator_init(recordings, 0); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_app_stored_recording *, recording, obj, ao2_cleanup); int r = ast_json_array_append( json, stasis_app_stored_recording_to_json(recording)); if (r != 0) { ast_ari_response_alloc_failed(response); ao2_iterator_destroy(&i); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
void ast_ari_bridges_add_channel(struct ast_variable *headers, struct ast_ari_bridges_add_channel_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); RAII_VAR(struct control_list *, list, NULL, ao2_cleanup); size_t i; int has_error = 0; if (!bridge) { /* Response filled in by find_bridge() */ return; } list = control_list_create(response, args->channel_count, args->channel); if (!list) { /* Response filled in by control_list_create() */ return; } for (i = 0; i < list->count; ++i) { stasis_app_control_clear_roles(list->controls[i]); if (!ast_strlen_zero(args->role)) { if (stasis_app_control_add_role(list->controls[i], args->role)) { ast_ari_response_alloc_failed(response); return; } } } for (i = 0; i < list->count; ++i) { if ((has_error = check_add_remove_channel(response, list->controls[i], stasis_app_control_add_channel_to_bridge( list->controls[i], bridge)))) { break; } } if (!has_error) { ast_ari_response_no_content(response); } }
void ast_ari_channels_continue_in_dialplan( struct ast_variable *headers, struct ast_ari_channels_continue_in_dialplan_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); ast_assert(response != NULL); control = find_control(response, args->channel_id); if (control == NULL) { return; } if (stasis_app_control_continue(control, args->context, args->extension, args->priority)) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_no_content(response); }
void ast_ari_endpoints_get(struct ast_variable *headers, struct ast_ari_endpoints_get_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup); snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource); if (!snapshot) { ast_ari_response_error(response, 404, "Not Found", "Endpoint not found"); return; } json = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); if (!json) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_ok(response, ast_json_ref(json)); }
void ast_ari_bridges_start_moh(struct ast_variable *headers, struct ast_ari_bridges_start_moh_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); struct ast_channel *moh_channel; const char *moh_class = args->moh_class; if (!bridge) { /* The response is provided by find_bridge() */ return; } moh_channel = stasis_app_bridge_moh_channel(bridge); if (!moh_channel) { ast_ari_response_alloc_failed(response); return; } ast_moh_start(moh_channel, moh_class, NULL); ast_ari_response_no_content(response); }
static struct control_list *control_list_create(struct ast_ari_response *response, size_t count, const char **channels) { RAII_VAR(struct control_list *, list, NULL, ao2_cleanup); size_t i; if (count == 0 || !channels) { ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel"); return NULL; } list = ao2_alloc(sizeof(*list) + count * sizeof(list->controls[0]), control_list_dtor); if (!list) { ast_ari_response_alloc_failed(response); return NULL; } for (i = 0; i < count; ++i) { if (ast_strlen_zero(channels[i])) { continue; } list->controls[list->count] = find_channel_control(response, channels[i]); if (!list->controls[list->count]) { /* response filled in by find_channel_control() */ return NULL; } ++list->count; } if (list->count == 0) { ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel"); return NULL; } ao2_ref(list, +1); return list; }
void ast_ari_asterisk_get_info(struct ast_variable *headers, struct ast_ari_asterisk_get_info_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); int show_all = args->only_count == 0; int show_build = show_all; int show_system = show_all; int show_config = show_all; int show_status = show_all; size_t i; int res = 0; for (i = 0; i < args->only_count; ++i) { if (strcasecmp("build", args->only[i]) == 0) { show_build = 1; } else if (strcasecmp("system", args->only[i]) == 0) { show_system = 1; } else if (strcasecmp("config", args->only[i]) == 0) { show_config = 1; } else if (strcasecmp("status", args->only[i]) == 0) { show_status = 1; } else { ast_log(LOG_WARNING, "Unrecognized info section '%s'\n", args->only[i]); } } json = ast_json_object_create(); if (show_build) { res |= ast_json_object_set(json, "build", ast_json_pack( "{ s: s, s: s, s: s," " s: s, s: s, s: s }", "os", ast_build_os, "kernel", ast_build_kernel, "machine", ast_build_machine, "options", AST_BUILDOPTS, "date", ast_build_date, "user", ast_build_user)); } if (show_system) { char eid_str[128]; ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default); res |= ast_json_object_set(json, "system", ast_json_pack("{ s: s, s: s }", "version", ast_get_version(), "entity_id", eid_str)); } if (show_config) { struct ast_json *config = ast_json_pack( "{ s: s, s: s," " s: { s: s, s: s } }", "name", ast_config_AST_SYSTEM_NAME, "default_language", ast_defaultlanguage, "setid", "user", ast_config_AST_RUN_USER, "group", ast_config_AST_RUN_GROUP); res |= ast_json_object_set(json, "config", config); if (ast_option_maxcalls) { res |= ast_json_object_set(config, "max_channels", ast_json_integer_create(ast_option_maxcalls)); } if (ast_option_maxfiles) { res |= ast_json_object_set(config, "max_open_files", ast_json_integer_create(ast_option_maxfiles)); } if (ast_option_maxload) { res |= ast_json_object_set(config, "max_load", ast_json_real_create(ast_option_maxload)); } } if (show_status) { res |= ast_json_object_set(json, "status", ast_json_pack("{ s: o, s: o }", "startup_time", ast_json_timeval(ast_startuptime, NULL), "last_reload_time", ast_json_timeval(ast_lastreloadtime, NULL))); } if (res != 0) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_ok(response, ast_json_ref(json)); }
void ast_ari_bridges_record(struct ast_variable *headers, struct ast_ari_bridges_record_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); RAII_VAR(struct ast_channel *, record_channel, NULL, ast_hangup); RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); RAII_VAR(char *, recording_url, NULL, ast_free); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_app_recording_options *, options, NULL, ao2_cleanup); RAII_VAR(char *, uri_encoded_name, NULL, ast_free); RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel); struct stasis_topic *channel_topic; struct stasis_topic *bridge_topic; size_t uri_name_maxlen; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; ast_assert(response != NULL); if (bridge == NULL) { return; } if (!(record_channel = prepare_bridge_media_channel("Recorder"))) { ast_ari_response_error( response, 500, "Internal Server Error", "Failed to create recording channel"); return; } bridge_topic = ast_bridge_topic(bridge); channel_topic = ast_channel_topic(record_channel); /* Forward messages from the recording channel topic to the bridge topic so that anything listening for * messages on the bridge topic will receive the recording start/stop messages. Other messages that would * go to this channel will be suppressed since the channel is marked as internal. */ if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) { ast_ari_response_error( response, 500, "Internal Error", "Could not forward record channel stasis messages to bridge topic"); return; } if (ast_unreal_channel_push_to_bridge(record_channel, bridge, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) { ast_ari_response_error( response, 500, "Internal Error", "Failed to put recording channel into the bridge"); return; } control = stasis_app_control_create(record_channel); if (control == NULL) { ast_ari_response_alloc_failed(response); return; } options = stasis_app_recording_options_create(args->name, args->format); if (options == NULL) { ast_ari_response_alloc_failed(response); return; } ast_string_field_build(options, target, "bridge:%s", args->bridge_id); options->max_silence_seconds = args->max_silence_seconds; options->max_duration_seconds = args->max_duration_seconds; options->terminate_on = stasis_app_recording_termination_parse(args->terminate_on); options->if_exists = stasis_app_recording_if_exists_parse(args->if_exists); options->beep = args->beep; if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) { ast_ari_response_error( response, 400, "Bad Request", "terminateOn invalid"); return; } if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) { ast_ari_response_error( response, 400, "Bad Request", "ifExists invalid"); return; } if (!ast_get_format_for_file_ext(options->format)) { ast_ari_response_error( response, 422, "Unprocessable Entity", "specified format is unknown on this system"); return; } recording = stasis_app_control_record(control, options); if (recording == NULL) { switch(errno) { case EINVAL: /* While the arguments are invalid, we should have * caught them prior to calling record. */ ast_ari_response_error( response, 500, "Internal Server Error", "Error parsing request"); break; case EEXIST: ast_ari_response_error(response, 409, "Conflict", "Recording '%s' already exists and can not be overwritten", args->name); break; case ENOMEM: ast_ari_response_alloc_failed(response); break; case EPERM: ast_ari_response_error( response, 400, "Bad Request", "Recording name invalid"); break; default: ast_log(LOG_WARNING, "Unrecognized recording error: %s\n", strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Internal Server Error"); break; } return; } uri_name_maxlen = strlen(args->name) * 3; uri_encoded_name = ast_malloc(uri_name_maxlen); if (!uri_encoded_name) { ast_ari_response_alloc_failed(response); return; } ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http); if (ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name) == -1) { recording_url = NULL; ast_ari_response_alloc_failed(response); return; } json = stasis_app_recording_to_json(recording); if (!json) { ast_ari_response_alloc_failed(response); return; } thread_data = ast_calloc(1, sizeof(*thread_data)); if (!thread_data) { ast_ari_response_alloc_failed(response); return; } thread_data->bridge_channel = record_channel; thread_data->control = control; thread_data->forward = channel_forward; if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) { ast_ari_response_alloc_failed(response); ast_free(thread_data); return; } /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */ record_channel = NULL; control = NULL; channel_forward = NULL; ast_ari_response_created(response, recording_url, ast_json_ref(json)); }
static void ari_bridges_play_new(const char *args_media, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge) { RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup); RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel); RAII_VAR(char *, playback_url, NULL, ast_free); struct stasis_topic *channel_topic; struct stasis_topic *bridge_topic; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; if (!(play_channel = prepare_bridge_media_channel("Announcer"))) { ast_ari_response_error( response, 500, "Internal Error", "Could not create playback channel"); return; } ast_debug(1, "Created announcer channel '%s'\n", ast_channel_name(play_channel)); bridge_topic = ast_bridge_topic(bridge); channel_topic = ast_channel_topic(play_channel); /* Forward messages from the playback channel topic to the bridge topic so that anything listening for * messages on the bridge topic will receive the playback start/stop messages. Other messages that would * go to this channel will be suppressed since the channel is marked as internal. */ if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) { ast_ari_response_error( response, 500, "Internal Error", "Could not forward playback channel stasis messages to bridge topic"); return; } if (ast_unreal_channel_push_to_bridge(play_channel, bridge, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) { ast_ari_response_error( response, 500, "Internal Error", "Failed to put playback channel into the bridge"); return; } control = stasis_app_control_create(play_channel); if (control == NULL) { ast_ari_response_alloc_failed(response); return; } ao2_lock(control); if (ari_bridges_play_helper(args_media, args_lang, args_offset_ms, args_skipms, args_playback_id, response, bridge, control, &json, &playback_url)) { ao2_unlock(control); return; } ao2_unlock(control); if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) { ast_ari_response_alloc_failed(response); return; } /* Give play_channel and control reference to the thread data */ thread_data = ast_calloc(1, sizeof(*thread_data)); if (!thread_data) { ast_ari_response_alloc_failed(response); return; } thread_data->bridge_channel = play_channel; thread_data->control = control; thread_data->forward = channel_forward; if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) { ast_ari_response_alloc_failed(response); ast_free(thread_data); return; } /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */ play_channel = NULL; control = NULL; channel_forward = NULL; ast_ari_response_created(response, playback_url, ast_json_ref(json)); }
/*! * \brief Parameter parsing callback for /asterisk/variable. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_asterisk_set_global_var_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_ari_asterisk_set_global_var_args args = {}; struct ast_variable *i; RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "variable") == 0) { args.variable = (i->value); } else if (strcmp(i->name, "value") == 0) { args.value = (i->value); } else {} } /* Look for a JSON request entity */ body = ast_http_get_json(ser, headers); if (!body) { switch (errno) { case EFBIG: ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); goto fin; case ENOMEM: ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); goto fin; case EIO: ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); goto fin; } } if (ast_ari_asterisk_set_global_var_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_asterisk_set_global_var(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 400: /* Missing variable parameter. */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_void( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/variable\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /asterisk/variable\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) return; }
void ast_ari_get_docs(const char *uri, struct ast_variable *headers, struct ast_ari_response *response) { RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free); RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free); RAII_VAR(char *, absolute_filename, NULL, ast_std_free); struct ast_json *obj = NULL; struct ast_variable *host = NULL; struct ast_json_error error = {}; struct stat file_stat; ast_debug(3, "%s(%s)\n", __func__, uri); absolute_path_builder = ast_str_create(80); if (absolute_path_builder == NULL) { ast_ari_response_alloc_failed(response); return; } /* absolute path to the rest-api directory */ ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR); ast_str_append(&absolute_path_builder, 0, "/rest-api/"); absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL); if (absolute_api_dirname == NULL) { ast_log(LOG_ERROR, "Error determining real directory for rest-api\n"); ast_ari_response_error( response, 500, "Internal Server Error", "Cannot find rest-api directory"); return; } /* absolute path to the requested file */ ast_str_append(&absolute_path_builder, 0, "%s", uri); absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL); if (absolute_filename == NULL) { switch (errno) { case ENAMETOOLONG: case ENOENT: case ENOTDIR: ast_ari_response_error( response, 404, "Not Found", "Resource not found"); break; case EACCES: ast_ari_response_error( response, 403, "Forbidden", "Permission denied"); break; default: ast_log(LOG_ERROR, "Error determining real path for uri '%s': %s\n", uri, strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Cannot find file"); break; } return; } if (!ast_begins_with(absolute_filename, absolute_api_dirname)) { /* HACKERZ! */ ast_log(LOG_ERROR, "Invalid attempt to access '%s' (not in %s)\n", absolute_filename, absolute_api_dirname); ast_ari_response_error( response, 404, "Not Found", "Resource not found"); return; } if (stat(absolute_filename, &file_stat) == 0) { if (!(file_stat.st_mode & S_IFREG)) { /* Not a file */ ast_ari_response_error( response, 403, "Forbidden", "Invalid access"); return; } } else { /* Does not exist */ ast_ari_response_error( response, 404, "Not Found", "Resource not found"); return; } /* Load resource object from file */ obj = ast_json_load_new_file(absolute_filename, &error); if (obj == NULL) { ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n", error.source, error.line, error.column, error.text); ast_ari_response_error( response, 500, "Internal Server Error", "Yikes! Cannot parse resource"); return; } /* Update the basePath properly */ if (ast_json_object_get(obj, "basePath") != NULL) { for (host = headers; host; host = host->next) { if (strcasecmp(host->name, "Host") == 0) { break; } } if (host != NULL) { ast_json_object_set( obj, "basePath", ast_json_stringf("http://%s/ari", host->value)); } else { /* Without the host, we don't have the basePath */ ast_json_object_del(obj, "basePath"); } } ast_ari_response_ok(response, obj); }
void ast_ari_channels_continue_in_dialplan( struct ast_variable *headers, struct ast_ari_channels_continue_in_dialplan_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); int ipri; const char *context; const char *exten; ast_assert(response != NULL); control = find_control(response, args->channel_id); if (control == NULL) { return; } snapshot = stasis_app_control_get_snapshot(control); if (!snapshot) { return; } if (ast_strlen_zero(args->context)) { context = snapshot->context; exten = S_OR(args->extension, snapshot->exten); } else { context = args->context; exten = S_OR(args->extension, "s"); } if (!ast_strlen_zero(args->label)) { /* A label was provided in the request, use that */ if (sscanf(args->label, "%30d", &ipri) != 1) { ipri = ast_findlabel_extension(NULL, context, exten, args->label, NULL); if (ipri == -1) { ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args->label, context); ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found"); return; } } else { ast_debug(3, "Numeric value provided for label, jumping to that priority\n"); } if (ipri == 0) { ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n", args->label, exten, context); ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal"); return; } } else if (args->priority) { /* No label provided, use provided priority */ ipri = args->priority; } else if (ast_strlen_zero(args->context) && ast_strlen_zero(args->extension)) { /* Special case. No exten, context, or priority provided, then move on to the next priority */ ipri = snapshot->priority + 1; } else { ipri = 1; } if (stasis_app_control_continue(control, context, exten, ipri)) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_no_content(response); }
/*! * \brief Parameter parsing callback for /recordings/stored/{recordingName}/copy. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_recordings_copy_stored_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response) { struct ast_ari_recordings_copy_stored_args args = {}; struct ast_variable *i; #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "destinationRecordingName") == 0) { args.destination_recording_name = (i->value); } else {} } for (i = path_vars; i; i = i->next) { if (strcmp(i->name, "recordingName") == 0) { args.recording_name = (i->value); } else {} } if (ast_ari_recordings_copy_stored_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_recordings_copy_stored(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 404: /* Recording not found */ case 409: /* A recording with the same name already exists on the system */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_stored_recording( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /recordings/stored/{recordingName}/copy\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /recordings/stored/{recordingName}/copy\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) return; }
/*! * \brief Parameter parsing callback for /bridges/{bridgeId}/removeChannel. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_bridges_remove_channel_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_ari_bridges_remove_channel_args args = {}; struct ast_variable *i; RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "channel") == 0) { /* Parse comma separated list */ char *vals[MAX_VALS]; size_t j; args.channel_parse = ast_strdup(i->value); if (!args.channel_parse) { ast_ari_response_alloc_failed(response); goto fin; } if (strlen(args.channel_parse) == 0) { /* ast_app_separate_args can't handle "" */ args.channel_count = 1; vals[0] = args.channel_parse; } else { args.channel_count = ast_app_separate_args( args.channel_parse, ',', vals, ARRAY_LEN(vals)); } if (args.channel_count == 0) { ast_ari_response_alloc_failed(response); goto fin; } if (args.channel_count >= MAX_VALS) { ast_ari_response_error(response, 400, "Bad Request", "Too many values for channel"); goto fin; } args.channel = ast_malloc(sizeof(*args.channel) * args.channel_count); if (!args.channel) { ast_ari_response_alloc_failed(response); goto fin; } for (j = 0; j < args.channel_count; ++j) { args.channel[j] = (vals[j]); } } else {} } for (i = path_vars; i; i = i->next) { if (strcmp(i->name, "bridgeId") == 0) { args.bridge_id = (i->value); } else {} } /* Look for a JSON request entity */ body = ast_http_get_json(ser, headers); if (!body) { switch (errno) { case EFBIG: ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); goto fin; case ENOMEM: ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); goto fin; case EIO: ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); goto fin; } } if (ast_ari_bridges_remove_channel_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_bridges_remove_channel(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 400: /* Channel not found */ case 404: /* Bridge not found */ case 409: /* Bridge not in Stasis application */ case 422: /* Channel not in this bridge */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_void( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/removeChannel\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/removeChannel\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) ast_free(args.channel_parse); ast_free(args.channel); return; }
void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup); RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup); struct ast_json *fields; struct ast_variable *update_set = NULL; int created = 0; sorcery = ast_sorcery_retrieve_by_module_name(args->config_class); if (!sorcery) { ast_ari_response_error( response, 404, "Not Found", "configClass '%s' not found", args->config_class); return; } object_type = ast_sorcery_get_object_type(sorcery, args->object_type); if (!object_type) { ast_ari_response_error( response, 404, "Not Found", "objectType '%s' not found", args->object_type); return; } sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id); if (!sorcery_obj) { ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id); sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id); if (!sorcery_obj) { ast_ari_response_alloc_failed(response); return; } created = 1; } else { void *copy; copy = ast_sorcery_copy(sorcery, sorcery_obj); if (!copy) { ast_ari_response_alloc_failed(response); return; } ao2_ref(sorcery_obj, -1); sorcery_obj = copy; } fields = ast_json_object_get(args->fields, "fields"); if (!fields && !created) { /* Whoops. We need data. */ ast_ari_response_error( response, 400, "Bad request", "Fields must be provided to update object '%s'", args->id); return; } else if (fields) { size_t i; for (i = 0; i < ast_json_array_size(fields); i++) { struct ast_variable *new_var; struct ast_json *json_value = ast_json_array_get(fields, i); if (!json_value) { continue; } new_var = ast_variable_new( ast_json_string_get(ast_json_object_get(json_value, "attribute")), ast_json_string_get(ast_json_object_get(json_value, "value")), ""); if (!new_var) { ast_variables_destroy(update_set); ast_ari_response_alloc_failed(response); return; } ast_variable_list_append(&update_set, new_var); } } /* APPLY! Note that a NULL update_set is fine (and necessary), as it * will force validation on a newly created object. */ if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) { ast_variables_destroy(update_set); ast_ari_response_error( response, 400, "Bad request", "%s of object '%s' failed field value validation", created ? "Creation" : "Update", args->id); return; } ast_variables_destroy(update_set); if (created) { if (ast_sorcery_create(sorcery, sorcery_obj)) { ast_ari_response_error( response, 403, "Forbidden", "Cannot create sorcery objects of type '%s'", args->object_type); return; } } else { if (ast_sorcery_update(sorcery, sorcery_obj)) { ast_ari_response_error( response, 403, "Forbidden", "Cannot update sorcery objects of type '%s'", args->object_type); return; } } return_sorcery_object(sorcery, sorcery_obj, response); }
static void ari_channels_handle_originate_with_id(const char *args_endpoint, const char *args_extension, const char *args_context, long args_priority, const char *args_app, const char *args_app_args, const char *args_caller_id, int args_timeout, struct ast_variable *variables, const char *args_channel_id, const char *args_other_channel_id, struct ast_ari_response *response) { char *dialtech; char dialdevice[AST_CHANNEL_NAME]; char *caller_id = NULL; char *cid_num = NULL; char *cid_name = NULL; int timeout = 30000; RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy); struct ast_format tmp_fmt; char *stuff; struct ast_channel *chan; RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); struct ast_assigned_ids assignedids = { .uniqueid = args_channel_id, .uniqueid2 = args_other_channel_id, }; if (!cap) { ast_ari_response_alloc_failed(response); return; } ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { ast_ari_response_error(response, 400, "Bad Request", "Uniqueid length exceeds maximum of %d\n", AST_MAX_PUBLIC_UNIQUEID); return; } if (ast_strlen_zero(args_endpoint)) { ast_ari_response_error(response, 400, "Bad Request", "Endpoint must be specified"); return; } dialtech = ast_strdupa(args_endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; ast_copy_string(dialdevice, stuff, sizeof(dialdevice)); } if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) { ast_ari_response_error(response, 400, "Bad Request", "Invalid endpoint specified"); return; } if (args_timeout > 0) { timeout = args_timeout * 1000; } else if (args_timeout == -1) { timeout = -1; } if (!ast_strlen_zero(args_caller_id)) { caller_id = ast_strdupa(args_caller_id); ast_callerid_parse(caller_id, &cid_name, &cid_num); if (ast_is_shrinkable_phonenumber(cid_num)) { ast_shrink_phone_number(cid_num); } } if (!ast_strlen_zero(args_app)) { const char *app = "Stasis"; RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free); if (!appdata) { ast_ari_response_alloc_failed(response); return; } ast_str_set(&appdata, 0, "%s", args_app); if (!ast_strlen_zero(args_app_args)) { ast_str_append(&appdata, 0, ",%s", args_app_args); } /* originate a channel, putting it into an application */ if (ast_pbx_outgoing_app(dialtech, cap, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, variables, NULL, &chan, &assignedids)) { ast_ari_response_alloc_failed(response); return; } } else if (!ast_strlen_zero(args_extension)) {
/*! * \brief Parameter parsing callback for /applications/{applicationName}/subscription. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_applications_unsubscribe_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response) { struct ast_ari_applications_unsubscribe_args args = {}; struct ast_variable *i; #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "eventSource") == 0) { /* Parse comma separated list */ char *vals[MAX_VALS]; size_t j; args.event_source_parse = ast_strdup(i->value); if (!args.event_source_parse) { ast_ari_response_alloc_failed(response); goto fin; } if (strlen(args.event_source_parse) == 0) { /* ast_app_separate_args can't handle "" */ args.event_source_count = 1; vals[0] = args.event_source_parse; } else { args.event_source_count = ast_app_separate_args( args.event_source_parse, ',', vals, ARRAY_LEN(vals)); } if (args.event_source_count == 0) { ast_ari_response_alloc_failed(response); goto fin; } if (args.event_source_count >= MAX_VALS) { ast_ari_response_error(response, 400, "Bad Request", "Too many values for event_source"); goto fin; } args.event_source = ast_malloc(sizeof(*args.event_source) * args.event_source_count); if (!args.event_source) { ast_ari_response_alloc_failed(response); goto fin; } for (j = 0; j < args.event_source_count; ++j) { args.event_source[j] = (vals[j]); } } else {} } for (i = path_vars; i; i = i->next) { if (strcmp(i->name, "applicationName") == 0) { args.application_name = (i->value); } else {} } if (ast_ari_applications_unsubscribe_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_applications_unsubscribe(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 400: /* Missing parameter; event source scheme not recognized. */ case 404: /* Application does not exist. */ case 409: /* Application not subscribed to event source. */ case 422: /* Event source does not exist. */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_application( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /applications/{applicationName}/subscription\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /applications/{applicationName}/subscription\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) ast_free(args.event_source_parse); ast_free(args.event_source); return; }
static void ari_channels_handle_originate_with_id(const char *args_endpoint, const char *args_extension, const char *args_context, long args_priority, const char *args_app, const char *args_app_args, const char *args_caller_id, int args_timeout, struct ast_variable *variables, const char *args_channel_id, const char *args_other_channel_id, const char *args_originator, struct ast_ari_response *response) { char *dialtech; char dialdevice[AST_CHANNEL_NAME]; struct ast_dial *dial; char *caller_id = NULL; char *cid_num = NULL; char *cid_name = NULL; RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); char *stuff; struct ast_channel *other = NULL; struct ast_channel *chan; RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); struct ast_assigned_ids assignedids = { .uniqueid = args_channel_id, .uniqueid2 = args_other_channel_id, }; struct ari_origination *origination; pthread_t thread; if (!cap) { ast_ari_response_alloc_failed(response); return; } ast_format_cap_append(cap, ast_format_slin, 0); if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { ast_ari_response_error(response, 400, "Bad Request", "Uniqueid length exceeds maximum of %d", AST_MAX_PUBLIC_UNIQUEID); return; } if (ast_strlen_zero(args_endpoint)) { ast_ari_response_error(response, 400, "Bad Request", "Endpoint must be specified"); return; } dialtech = ast_strdupa(args_endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; ast_copy_string(dialdevice, stuff, sizeof(dialdevice)); } if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) { ast_ari_response_error(response, 400, "Bad Request", "Invalid endpoint specified"); return; } if (!ast_strlen_zero(args_app)) { RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free); if (!appdata) { ast_ari_response_alloc_failed(response); return; } ast_str_set(&appdata, 0, "%s", args_app); if (!ast_strlen_zero(args_app_args)) { ast_str_append(&appdata, 0, ",%s", args_app_args); } origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1); if (!origination) { ast_ari_response_alloc_failed(response); return; } strcpy(origination->appdata, ast_str_buffer(appdata)); } else if (!ast_strlen_zero(args_extension)) {
void ast_ari_channels_originate(struct ast_variable *headers, struct ast_ari_channels_originate_args *args, struct ast_ari_response *response) { char *dialtech; char dialdevice[AST_CHANNEL_NAME]; char *caller_id = NULL; char *cid_num = NULL; char *cid_name = NULL; int timeout = 30000; char *stuff; struct ast_channel *chan; RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); if (ast_strlen_zero(args->endpoint)) { ast_ari_response_error(response, 400, "Bad Request", "Endpoint must be specified"); return; } dialtech = ast_strdupa(args->endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; ast_copy_string(dialdevice, stuff, sizeof(dialdevice)); } if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) { ast_ari_response_error(response, 400, "Bad Request", "Invalid endpoint specified"); return; } if (args->timeout > 0) { timeout = args->timeout * 1000; } else if (args->timeout == -1) { timeout = -1; } if (!ast_strlen_zero(args->caller_id)) { caller_id = ast_strdupa(args->caller_id); ast_callerid_parse(caller_id, &cid_name, &cid_num); if (ast_is_shrinkable_phonenumber(cid_num)) { ast_shrink_phone_number(cid_num); } } if (!ast_strlen_zero(args->app)) { const char *app = "Stasis"; RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free); if (!appdata) { ast_ari_response_alloc_failed(response); return; } ast_str_set(&appdata, 0, "%s", args->app); if (!ast_strlen_zero(args->app_args)) { ast_str_append(&appdata, 0, ",%s", args->app_args); } /* originate a channel, putting it into an application */ if (ast_pbx_outgoing_app(dialtech, NULL, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, NULL, NULL, &chan)) { ast_ari_response_alloc_failed(response); return; } } else if (!ast_strlen_zero(args->extension)) {
/*! * \brief Parameter parsing callback for /bridges/{bridgeId}/record. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_bridges_record_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_ari_bridges_record_args args = {}; struct ast_variable *i; RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "name") == 0) { args.name = (i->value); } else if (strcmp(i->name, "format") == 0) { args.format = (i->value); } else if (strcmp(i->name, "maxDurationSeconds") == 0) { args.max_duration_seconds = atoi(i->value); } else if (strcmp(i->name, "maxSilenceSeconds") == 0) { args.max_silence_seconds = atoi(i->value); } else if (strcmp(i->name, "ifExists") == 0) { args.if_exists = (i->value); } else if (strcmp(i->name, "beep") == 0) { args.beep = ast_true(i->value); } else if (strcmp(i->name, "terminateOn") == 0) { args.terminate_on = (i->value); } else {} } for (i = path_vars; i; i = i->next) { if (strcmp(i->name, "bridgeId") == 0) { args.bridge_id = (i->value); } else {} } /* Look for a JSON request entity */ body = ast_http_get_json(ser, headers); if (!body) { switch (errno) { case EFBIG: ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); goto fin; case ENOMEM: ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); goto fin; case EIO: ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); goto fin; } } if (ast_ari_bridges_record_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_bridges_record(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 400: /* Invalid parameters */ case 404: /* Bridge not found */ case 409: /* Bridge is not in a Stasis application; A recording with the same name already exists on the system and can not be overwritten because it is in progress or ifExists=fail */ case 422: /* The format specified is unknown on this system */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_live_recording( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/record\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/record\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) return; }
void ast_ari_endpoints_list_by_tech(struct ast_variable *headers, struct ast_ari_endpoints_list_by_tech_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; if (!ast_get_channel_tech(args->tech)) { ast_ari_response_error(response, 404, "Not Found", "No Endpoints found - invalid tech %s", args->tech); return; } cache = ast_endpoint_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } ao2_ref(cache, +1); snapshots = stasis_cache_dump(cache, ast_endpoint_snapshot_type()); if (!snapshots) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } i = ao2_iterator_init(snapshots, 0); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg); struct ast_json *json_endpoint; int r; if (strcasecmp(args->tech, snapshot->tech) != 0) { continue; } json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); if (!json_endpoint) { continue; } r = ast_json_array_append( json, json_endpoint); if (r != 0) { ast_ari_response_alloc_failed(response); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
/*! * \brief Parameter parsing callback for /bridges/{bridgeId}/play. * \param get_params GET parameters in the HTTP request. * \param path_vars Path variables extracted from the request. * \param headers HTTP headers. * \param[out] response Response to the HTTP request. */ static void ast_ari_bridges_play_cb( struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_ari_bridges_play_args args = {}; struct ast_variable *i; RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); #if defined(AST_DEVMODE) int is_valid; int code; #endif /* AST_DEVMODE */ for (i = get_params; i; i = i->next) { if (strcmp(i->name, "media") == 0) { args.media = (i->value); } else if (strcmp(i->name, "lang") == 0) { args.lang = (i->value); } else if (strcmp(i->name, "offsetms") == 0) { args.offsetms = atoi(i->value); } else if (strcmp(i->name, "skipms") == 0) { args.skipms = atoi(i->value); } else if (strcmp(i->name, "playbackId") == 0) { args.playback_id = (i->value); } else {} } for (i = path_vars; i; i = i->next) { if (strcmp(i->name, "bridgeId") == 0) { args.bridge_id = (i->value); } else {} } /* Look for a JSON request entity */ body = ast_http_get_json(ser, headers); if (!body) { switch (errno) { case EFBIG: ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); goto fin; case ENOMEM: ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); goto fin; case EIO: ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); goto fin; } } if (ast_ari_bridges_play_parse_body(body, &args)) { ast_ari_response_alloc_failed(response); goto fin; } ast_ari_bridges_play(headers, &args, response); #if defined(AST_DEVMODE) code = response->response_code; switch (code) { case 0: /* Implementation is still a stub, or the code wasn't set */ is_valid = response->message == NULL; break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ case 404: /* Bridge not found */ case 409: /* Bridge not in a Stasis application */ is_valid = 1; break; default: if (200 <= code && code <= 299) { is_valid = ast_ari_validate_playback( response->message); } else { ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/play\n", code); is_valid = 0; } } if (!is_valid) { ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/play\n"); ast_ari_response_error(response, 500, "Internal Server Error", "Response validation failed"); } #endif /* AST_DEVMODE */ fin: __attribute__((unused)) return; }
/*! * \brief Handle OPTIONS request, mainly for CORS preflight requests. * * Some browsers will send this prior to non-simple methods (i.e. DELETE). * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2. */ static void handle_options(struct stasis_rest_handlers *handler, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_variable *header; char const *acr_method = NULL; char const *acr_headers = NULL; char const *origin = NULL; RAII_VAR(struct ast_str *, allow, NULL, ast_free); enum ast_http_method m; int allowed = 0; /* Regular OPTIONS response */ add_allow_header(handler, response); ast_ari_response_no_content(response); /* Parse CORS headers */ for (header = headers; header != NULL; header = header->next) { if (strcmp(ACR_METHOD, header->name) == 0) { acr_method = header->value; } else if (strcmp(ACR_HEADERS, header->name) == 0) { acr_headers = header->value; } else if (strcmp("Origin", header->name) == 0) { origin = header->value; } } /* CORS 6.2, #1 - "If the Origin header is not present terminate this * set of steps." */ if (origin == NULL) { return; } /* CORS 6.2, #2 - "If the value of the Origin header is not a * case-sensitive match for any of the values in list of origins do not * set any additional headers and terminate this set of steps. * * Always matching is acceptable since the list of origins can be * unbounded. * * The Origin header can only contain a single origin as the user agent * will not follow redirects." */ if (!origin_allowed(origin)) { ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin); return; } /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header * or if parsing failed, do not set any additional headers and terminate * this set of steps." */ if (acr_method == NULL) { return; } /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers * headers let header field-names be the empty list." */ if (acr_headers == NULL) { acr_headers = ""; } /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of * the values in list of methods do not set any additional headers and * terminate this set of steps." */ allow = ast_str_create(20); if (!allow) { ast_ari_response_alloc_failed(response); return; } /* Go ahead and build the ACA_METHODS header at the same time */ for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) { if (handler->callbacks[m] != NULL) { char const *m_str = ast_get_http_method(m); if (strcmp(m_str, acr_method) == 0) { allowed = 1; } ast_str_append(&allow, 0, ",%s", m_str); } } if (!allowed) { return; } /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII * case-insensitive match for any of the values in list of headers do * not set any additional headers and terminate this set of steps. * * Note: Always matching is acceptable since the list of headers can be * unbounded." */ /* CORS 6.2 #7 - "If the resource supports credentials add a single * Access-Control-Allow-Origin header, with the value of the Origin * header as value, and add a single Access-Control-Allow-Credentials * header with the case-sensitive string "true" as value." * * Added by process_cors_request() earlier in the request. */ /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age * header..." */ /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers * consisting of (a subset of) the list of methods." */ ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n", ACA_METHODS, ast_str_buffer(allow)); /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers * consisting of (a subset of) the list of headers. * * Since the list of headers can be unbounded simply returning headers * can be enough." */ if (!ast_strlen_zero(acr_headers)) { ast_str_append(&response->headers, 0, "%s: %s\r\n", ACA_HEADERS, acr_headers); } }