struct ast_endpoint_snapshot *ast_endpoint_latest_snapshot(const char *tech, const char *name) { RAII_VAR(char *, id, NULL, ast_free); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); struct ast_endpoint_snapshot *snapshot; if (ast_strlen_zero(name)) { ast_asprintf(&id, "%s", tech); } else { ast_asprintf(&id, "%s/%s", tech, name); } if (!id) { return NULL; } ast_tech_to_upper(id); msg = stasis_cache_get(ast_endpoint_cache(), ast_endpoint_snapshot_type(), id); if (!msg) { return NULL; } snapshot = stasis_message_data(msg); ast_assert(snapshot != NULL); ao2_ref(snapshot, +1); return snapshot; }
struct stasis_cp_all *stasis_cp_all_create(const char *name, snapshot_get_id id_fn) { RAII_VAR(char *, cached_name, NULL, ast_free); RAII_VAR(struct stasis_cp_all *, all, NULL, ao2_cleanup); all = ao2_alloc(sizeof(*all), all_dtor); if (!all) { return NULL; } ast_asprintf(&cached_name, "%s-cached", name); if (!cached_name) { return NULL; } all->topic = stasis_topic_create(name); all->topic_cached = stasis_topic_create(cached_name); all->cache = stasis_cache_create(id_fn); all->forward_all_to_cached = stasis_forward_all(all->topic, all->topic_cached); if (!all->topic || !all->topic_cached || !all->cache || !all->forward_all_to_cached) { return NULL; } ao2_ref(all, +1); return all; }
static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id) { struct ast_datastore *datastore = NULL; struct mixmonitor_ds *mixmonitor_ds; if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) { return -1; } if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) { ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n"); } ast_mutex_init(&mixmonitor_ds->lock); ast_cond_init(&mixmonitor_ds->destruction_condition, NULL); if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) { ast_mutex_destroy(&mixmonitor_ds->lock); ast_cond_destroy(&mixmonitor_ds->destruction_condition); ast_free(mixmonitor_ds); return -1; } mixmonitor_ds->samp_rate = 8000; mixmonitor_ds->audiohook = &mixmonitor->audiohook; datastore->data = mixmonitor_ds; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); mixmonitor->mixmonitor_ds = mixmonitor_ds; return 0; }
struct stasis_cp_all *stasis_cp_all_create(const char *name, snapshot_get_id id_fn) { char *cached_name = NULL; struct stasis_cp_all *all; static int cache_id; all = ao2_t_alloc(sizeof(*all), all_dtor, name); if (!all) { return NULL; } ast_asprintf(&cached_name, "cache_pattern:%d/%s", ast_atomic_fetchadd_int(&cache_id, +1), name); if (!cached_name) { ao2_ref(all, -1); return NULL; } all->topic = stasis_topic_create(name); all->topic_cached = stasis_topic_create(cached_name); ast_free(cached_name); all->cache = stasis_cache_create(id_fn); all->forward_all_to_cached = stasis_forward_all(all->topic, all->topic_cached); if (!all->topic || !all->topic_cached || !all->cache || !all->forward_all_to_cached) { ao2_ref(all, -1); return NULL; } return all; }
/** * Get all plan info. * @return */ struct ast_json* get_plans_all(void) { char* sql; struct ast_json* j_res; struct ast_json* j_tmp; db_res_t* db_res; ast_asprintf(&sql, "select * from plan where in_use=%d;", E_DL_USE_OK); db_res = db_query(sql); ast_free(sql); if(db_res == NULL) { ast_log(LOG_ERROR, "Could not get plan all info.\n"); return NULL; } j_res = ast_json_array_create(); while(1) { j_tmp = db_get_record(db_res); if(j_tmp == NULL) { break; } ast_json_array_append(j_res, j_tmp); } db_free(db_res); return j_res; }
int delete_queue(const char* uuid) { struct ast_json* j_tmp; char* tmp; char* sql; int ret; if(uuid == NULL) { // invalid parameter. return false; } j_tmp = ast_json_object_create(); tmp = get_utc_timestamp(); ast_json_object_set(j_tmp, "tm_delete", ast_json_string_create(tmp)); ast_json_object_set(j_tmp, "in_use", ast_json_integer_create(E_DL_USE_NO)); ast_free(tmp); tmp = db_get_update_str(j_tmp); AST_JSON_UNREF(j_tmp); ast_asprintf(&sql, "update queue set %s where uuid=\"%s\";", tmp, uuid); ast_free(tmp); ret = db_exec(sql); ast_free(sql); if(ret == false) { ast_log(LOG_WARNING, "Could not delete queue. uuid[%s]\n", uuid); return false; } // send notification send_manager_evt_out_queue_delete(uuid); return true; }
static void manager_park_bridged(struct mansession *s, const struct message *m, struct ast_channel *chan, struct ast_channel *parker_chan, const char *parkinglot, int timeout_override) { struct ast_bridge_channel *bridge_channel; char *app_data; if (timeout_override != -1) { if (ast_asprintf(&app_data, "%s,t(%d)", parkinglot, timeout_override) == -1) { astman_send_error(s, m, "Park action failed\n"); return; } } else { if (ast_asprintf(&app_data, "%s", parkinglot) == -1) { astman_send_error(s, m, "Park action failed\n"); return; } } ast_channel_lock(parker_chan); bridge_channel = ast_channel_get_bridge_channel(parker_chan); ast_channel_unlock(parker_chan); if (!bridge_channel) { ast_free(app_data); astman_send_error(s, m, "Park action failed\n"); return; } /* Subscribe to park messages for the channel being parked */ if (create_parked_subscription(parker_chan, ast_channel_uniqueid(chan), 1)) { ast_free(app_data); astman_send_error(s, m, "Park action failed\n"); ao2_cleanup(bridge_channel); return; } ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(chan), ast_channel_uniqueid(parker_chan), app_data); ast_free(app_data); astman_send_ack(s, m, "Park successful\n"); ao2_cleanup(bridge_channel); }
static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf) { const struct ast_sip_contact_status *status = obj; if (ast_asprintf(buf, "%ld.%06ld", (long)status->rtt_start.tv_sec, (long)status->rtt_start.tv_usec) == -1) { return -1; } return 0; }
static int convert_bdb_to_sqlite3(void) { char *cmd; int res; ast_asprintf(&cmd, "%s/astdb2sqlite3 '%s'\n", ast_config_AST_SBIN_DIR, ast_config_AST_DB); res = ast_safe_system(cmd); ast_free(cmd); return res; }
/*! * \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; }
/*! \internal \brief convert multi object blob to ami string */ static struct ast_str *multi_object_blob_to_ami(void *obj) { struct ast_str *ami_str=ast_str_create(1024); struct ast_str *ami_snapshot; const struct ast_multi_object_blob *multi = obj; enum stasis_user_multi_object_snapshot_type type; int i; if (!ami_str) { return NULL; } if (!multi) { ast_free(ami_str); return NULL; } for (type = 0; type < STASIS_UMOS_MAX; ++type) { for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) { char *name = ""; void *snapshot = AST_VECTOR_GET(&multi->snapshots[type], i); ami_snapshot = NULL; if (i > 0) { ast_asprintf(&name, "%d", i + 1); } switch (type) { case STASIS_UMOS_CHANNEL: ami_snapshot = ast_manager_build_channel_state_string_prefix(snapshot, name); break; case STASIS_UMOS_BRIDGE: ami_snapshot = ast_manager_build_bridge_state_string_prefix(snapshot, name); break; case STASIS_UMOS_ENDPOINT: /* currently not sending endpoint snapshots to AMI */ break; } if (ami_snapshot) { ast_str_append(&ami_str, 0, "%s", ast_str_buffer(ami_snapshot)); ast_free(ami_snapshot); } } } return ami_str; }
int ast_sdp_crypto_build_offer(struct ast_sdp_crypto *p, int taglen) { /* Rebuild the crypto line */ if (p->a_crypto) { ast_free(p->a_crypto); } if (ast_asprintf(&p->a_crypto, "%d AES_CM_128_HMAC_SHA1_%i inline:%s", p->tag, taglen, p->local_key64) == -1) { ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); return -1; } ast_debug(1, "Crypto line: a=crypto:%s\n", p->a_crypto); return 0; }
struct stasis_caching_topic *stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache) { RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup); struct stasis_subscription *sub; RAII_VAR(char *, new_name, NULL, ast_free); int ret; ret = ast_asprintf(&new_name, "%s-cached", stasis_topic_name(original_topic)); if (ret < 0) { return NULL; } caching_topic = ao2_alloc_options(sizeof(*caching_topic), stasis_caching_topic_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (caching_topic == NULL) { return NULL; } caching_topic->topic = stasis_topic_create(new_name); if (caching_topic->topic == NULL) { return NULL; } ao2_ref(cache, +1); caching_topic->cache = cache; sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0); if (sub == NULL) { return NULL; } ao2_ref(original_topic, +1); caching_topic->original_topic = original_topic; /* This is for the reference contained in the subscription above */ ao2_ref(caching_topic, +1); caching_topic->sub = sub; /* The subscription holds the reference, so no additional ref bump. */ return caching_topic; }
int update_queue(struct ast_json* j_queue) { char* tmp; char* sql; struct ast_json* j_tmp; int ret; const char* uuid; uuid = ast_json_string_get(ast_json_object_get(j_queue, "uuid")); if(uuid == NULL) { ast_log(LOG_WARNING, "Could not get uuid.\n"); return false; } tmp = db_get_update_str(j_queue); if(tmp == NULL) { ast_log(LOG_WARNING, "Could not get update str.\n"); return false; } ast_asprintf(&sql, "update queue set %s where in_use=%d and uuid=\"%s\";", tmp, E_DL_USE_OK, uuid); ast_free(tmp); ret = db_exec(sql); ast_free(sql); if(ret == false) { ast_log(LOG_WARNING, "Could not update queue info. uuid[%s]\n", uuid); return false; } j_tmp = get_queue(uuid); if(j_tmp == NULL) { ast_log(LOG_WARNING, "Could not get update queue info. uuid[%s]\n", uuid); return false; } send_manager_evt_out_queue_update(j_tmp); AST_JSON_UNREF(j_tmp); return true; }
static void update_all_unqualified_endpoints(void) { struct ao2_container *aors; struct ao2_container *contacts; RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, var_contact, NULL, ast_variables_destroy); RAII_VAR(char *, time_now, NULL, ast_free); struct timeval tv = ast_tvnow(); if (!(var_aor = ast_variable_new("contact !=", "", ""))) { return; } if (!(var_aor->next = ast_variable_new("qualify_frequency <=", "0", ""))) { return; } if (ast_asprintf(&time_now, "%ld", tv.tv_sec) == -1) { return; } if (!(var_contact = ast_variable_new("expiration_time >", time_now, ""))) { return; } if (!(var_contact->next = ast_variable_new("qualify_frequency <=", "0", ""))) { return; } aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor); if (aors) { ao2_callback(aors, OBJ_NODATA, on_aor_update_endpoint_state, NULL); ao2_ref(aors, -1); } contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", AST_RETRIEVE_FLAG_MULTIPLE, var_contact); if (contacts) { ao2_callback(contacts, OBJ_NODATA, contact_update_endpoint_state, NULL); ao2_ref(contacts, -1); } }
/** * Get deleted plan info. * @param uuid * @return */ static struct ast_json* get_plan_deleted(const char* uuid) { char* sql; struct ast_json* j_res; db_res_t* db_res; if(uuid == NULL) { ast_log(LOG_WARNING, "Invalid input parameters.\n"); return NULL; } ast_asprintf(&sql, "select * from plan where in_use=%d and uuid=\"%s\";", E_DL_USE_NO, uuid); db_res = db_query(sql); ast_free(sql); if(db_res == NULL) { ast_log(LOG_ERROR, "Could not get plan info. uuid[%s]\n", uuid); return NULL; } j_res = db_get_record(db_res); db_free(db_res); return j_res; }
/*! \brief Custom handler for translating from an actual structure timeval to string */ static int expiration_struct2str(const void *obj, const intptr_t *args, char **buf) { const struct ast_sip_contact *contact = obj; return (ast_asprintf(buf, "%ld", contact->expiration_time.tv_sec) < 0) ? -1 : 0; }
struct stasis_app_recording *stasis_app_control_record( struct stasis_app_control *control, struct stasis_app_recording_options *options) { struct stasis_app_recording *recording; char *last_slash; errno = 0; if (options == NULL || ast_strlen_zero(options->name) || ast_strlen_zero(options->format) || options->max_silence_seconds < 0 || options->max_duration_seconds < 0) { errno = EINVAL; return NULL; } ast_debug(3, "%s: Sending record(%s.%s) command\n", stasis_app_control_get_channel_id(control), options->name, options->format); recording = ao2_alloc(sizeof(*recording), recording_dtor); if (!recording) { errno = ENOMEM; return NULL; } recording->duration.total = -1; recording->duration.energy_only = -1; ast_asprintf(&recording->absolute_name, "%s/%s", ast_config_AST_RECORDING_DIR, options->name); if (recording->absolute_name == NULL) { errno = ENOMEM; ao2_ref(recording, -1); return NULL; } if ((last_slash = strrchr(recording->absolute_name, '/'))) { *last_slash = '\0'; if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR, recording->absolute_name, 0777) != 0) { /* errno set by ast_mkdir */ ao2_ref(recording, -1); return NULL; } *last_slash = '/'; } ao2_ref(options, +1); recording->options = options; recording->control = control; recording->state = STASIS_APP_RECORDING_STATE_QUEUED; if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) && (ast_fileexists(recording->absolute_name, NULL, NULL))) { ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n", recording->absolute_name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } { RAII_VAR(struct stasis_app_recording *, old_recording, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, recordings); old_recording = ao2_find(recordings, options->name, OBJ_KEY | OBJ_NOLOCK); if (old_recording) { ast_log(LOG_WARNING, "Recording %s already in progress\n", recording->options->name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } ao2_link(recordings, recording); } stasis_app_control_register_add_rule(control, &rule_recording); stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup); return recording; }
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)); }
/** * Update plan * @param j_plan * @return */ bool update_plan(const struct ast_json* j_plan) { char* tmp; const char* tmp_const; char* sql; struct ast_json* j_tmp; int ret; char* uuid; if(j_plan == NULL) { return false; } j_tmp = ast_json_deep_copy(j_plan); if(j_tmp == NULL) { return false; } tmp_const = ast_json_string_get(ast_json_object_get(j_tmp, "uuid")); if(tmp_const == NULL) { ast_log(LOG_WARNING, "Could not get uuid.\n"); AST_JSON_UNREF(j_tmp); return false; } uuid = ast_strdup(tmp_const); tmp = get_utc_timestamp(); ast_json_object_set(j_tmp, "tm_update", ast_json_string_create(tmp)); ast_free(tmp); tmp = db_get_update_str(j_tmp); if(tmp == NULL) { ast_log(LOG_WARNING, "Could not get update str.\n"); AST_JSON_UNREF(j_tmp); ast_free(uuid); return false; } AST_JSON_UNREF(j_tmp); ast_asprintf(&sql, "update plan set %s where in_use=%d and uuid=\"%s\";", tmp, E_DL_USE_OK, uuid); ast_free(tmp); ret = db_exec(sql); ast_free(sql); if(ret == false) { ast_log(LOG_WARNING, "Could not update plan info. uuid[%s]\n", uuid); ast_free(uuid); return false; } j_tmp = get_plan(uuid); ast_free(uuid); if(j_tmp == NULL) { ast_log(LOG_WARNING, "Could not get updated plan info.\n"); return false; } send_manager_evt_out_plan_update(j_tmp); AST_JSON_UNREF(j_tmp); return true; }
static void mcd_set_operation_result(struct ast_channel *chan, int result) { char *numresult = NULL; ast_asprintf(&numresult, "%d", result); pbx_builtin_setvar_helper(chan, "MCDRESULT", numresult); }
struct stasis_app_recording *stasis_app_control_record( struct stasis_app_control *control, struct stasis_app_recording_options *options) { RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); char *last_slash; errno = 0; if (options == NULL || ast_strlen_zero(options->name) || ast_strlen_zero(options->format) || options->max_silence_seconds < 0 || options->max_duration_seconds < 0) { errno = EINVAL; return NULL; } ast_debug(3, "%s: Sending record(%s.%s) command\n", stasis_app_control_get_channel_id(control), options->name, options->format); recording = ao2_alloc(sizeof(*recording), recording_dtor); if (!recording) { errno = ENOMEM; return NULL; } ast_asprintf(&recording->absolute_name, "%s/%s", ast_config_AST_RECORDING_DIR, options->name); if (recording->absolute_name == NULL) { errno = ENOMEM; return NULL; } if ((last_slash = strrchr(recording->absolute_name, '/'))) { *last_slash = '\0'; if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR, recording->absolute_name, 0777) != 0) { /* errno set by ast_mkdir */ return NULL; } *last_slash = '/'; } ao2_ref(options, +1); recording->options = options; recording->control = control; recording->state = STASIS_APP_RECORDING_STATE_QUEUED; { RAII_VAR(struct stasis_app_recording *, old_recording, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, recordings); old_recording = ao2_find(recordings, options->name, OBJ_KEY | OBJ_NOLOCK); if (old_recording) { ast_log(LOG_WARNING, "Recording %s already in progress\n", recording->options->name); errno = EEXIST; return NULL; } ao2_link(recordings, recording); } /* A ref is kept in the recordings container; no need to bump */ stasis_app_send_command_async(control, record_file, recording); /* Although this should be bumped for the caller */ ao2_ref(recording, +1); return recording; }
void ast_ari_channels_record(struct ast_variable *headers, struct ast_ari_channels_record_args *args, struct ast_ari_response *response) { 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); struct ast_json *json; RAII_VAR(struct stasis_app_recording_options *, options, NULL, ao2_cleanup); RAII_VAR(char *, uri_encoded_name, NULL, ast_free); size_t uri_name_maxlen; ast_assert(response != NULL); if (args->max_duration_seconds < 0) { ast_ari_response_error( response, 400, "Bad Request", "max_duration_seconds cannot be negative"); return; } if (args->max_silence_seconds < 0) { ast_ari_response_error( response, 400, "Bad Request", "max_silence_seconds cannot be negative"); return; } control = find_control(response, args->channel_id); if (control == NULL) { /* Response filled in by find_control */ return; } options = stasis_app_recording_options_create(args->name, args->format); if (options == NULL) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); } ast_string_field_build(options, target, "channel:%s", args->channel_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 == -1) { 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_error( response, 500, "Internal Server Error", "Out of memory"); 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_error( response, 500, "Internal Server Error", "Out of memory"); 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_error( response, 500, "Internal Server Error", "Out of memory"); return; } json = stasis_app_recording_to_json(recording); if (!json) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } ast_ari_response_created(response, recording_url, json); }
static void *hook_launch_thread(void *data) { struct hook_thread_arg *arg = data; struct ast_variable hook_id = { .name = "HOOK_ID", .value = arg->hook_id, }; struct ast_variable chan_name_var = { .name = "HOOK_CHANNEL", .value = arg->chan_name, .next = &hook_id, }; ast_pbx_outgoing_exten("Local", NULL, full_exten_name, 60, arg->context, arg->exten, 1, NULL, AST_OUTGOING_NO_WAIT, NULL, NULL, &chan_name_var, NULL, NULL, 1, NULL); hook_thread_arg_destroy(arg); return NULL; } static struct hook_thread_arg *hook_thread_arg_alloc(struct ast_channel *chan, struct hook_state *state) { struct hook_thread_arg *arg; if (!(arg = ast_calloc(1, sizeof(*arg)))) { return NULL; } ast_channel_lock(chan); arg->chan_name = ast_strdup(ast_channel_name(chan)); ast_channel_unlock(chan); if (!arg->chan_name) { hook_thread_arg_destroy(arg); return NULL; } if (ast_asprintf(&arg->hook_id, "%u", state->hook_id) == -1) { hook_thread_arg_destroy(arg); return NULL; } if (!(arg->context = ast_strdup(state->context))) { hook_thread_arg_destroy(arg); return NULL; } if (!(arg->exten = ast_strdup(state->exten))) { hook_thread_arg_destroy(arg); return NULL; } return arg; } static int do_hook(struct ast_channel *chan, struct hook_state *state) { pthread_t t; struct hook_thread_arg *arg; int res; if (!(arg = hook_thread_arg_alloc(chan, state))) { return -1; } /* * We don't want to block normal frame processing *at all* while we kick * this off, so do it in a new thread. */ res = ast_pthread_create_detached_background(&t, NULL, hook_launch_thread, arg); if (res != 0) { hook_thread_arg_destroy(arg); } return res; } static int hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction) { struct hook_state *state = (struct hook_state *) audiohook; /* trust me. */ struct timeval now; int res = 0; if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || state->disabled) { return 0; } now = ast_tvnow(); if (ast_tvdiff_ms(now, state->last_hook) > state->interval * 1000) { if ((res = do_hook(chan, state))) { const char *name; ast_channel_lock(chan); name = ast_strdupa(ast_channel_name(chan)); ast_channel_unlock(chan); ast_log(LOG_WARNING, "Failed to run hook on '%s'\n", name); } state->last_hook = now; } return res; } static struct hook_state *hook_state_alloc(const char *context, const char *exten, unsigned int interval, unsigned int hook_id) { struct hook_state *state; if (!(state = ast_calloc(1, sizeof(*state)))) { return NULL; } state->context = ast_strdup(context); state->exten = ast_strdup(exten); state->interval = interval; state->hook_id = hook_id; ast_audiohook_init(&state->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, AST_MODULE, AST_AUDIOHOOK_MANIPULATE_ALL_RATES); state->audiohook.manipulate_callback = hook_callback; return state; } static int init_hook(struct ast_channel *chan, const char *context, const char *exten, unsigned int interval, unsigned int hook_id) { struct hook_state *state; struct ast_datastore *datastore; char uid[32]; snprintf(uid, sizeof(uid), "%u", hook_id); if (!(datastore = ast_datastore_alloc(&hook_datastore, uid))) { return -1; } ast_module_ref(ast_module_info->self); if (!(state = hook_state_alloc(context, exten, interval, hook_id))) { ast_datastore_free(datastore); return -1; } datastore->data = state; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_audiohook_attach(chan, &state->audiohook); ast_channel_unlock(chan); return 0; } static int hook_on(struct ast_channel *chan, const char *data, unsigned int hook_id) { char *parse = ast_strdupa(S_OR(data, "")); AST_DECLARE_APP_ARGS(args, AST_APP_ARG(context); AST_APP_ARG(exten); AST_APP_ARG(interval); );
static void ari_channels_handle_play( const char *args_channel_id, const char *args_media, const char *args_lang, int args_offsetms, int args_skipms, const char *args_playback_id, 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); RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); RAII_VAR(char *, playback_url, NULL, ast_free); struct ast_json *json; const char *language; ast_assert(response != NULL); control = find_control(response, args_channel_id); if (control == NULL) { /* Response filled in by find_control */ return; } snapshot = stasis_app_control_get_snapshot(control); if (!snapshot) { ast_ari_response_error( response, 404, "Not Found", "Channel not found"); return; } if (args_skipms < 0) { ast_ari_response_error( response, 400, "Bad Request", "skipms cannot be negative"); return; } if (args_offsetms < 0) { ast_ari_response_error( response, 400, "Bad Request", "offsetms cannot be negative"); return; } language = S_OR(args_lang, snapshot->language); playback = stasis_app_control_play_uri(control, args_media, language, args_channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args_skipms, args_offsetms, args_playback_id); if (!playback) { ast_ari_response_error( response, 500, "Internal Server Error", "Failed to queue media for playback"); return; } if (ast_asprintf(&playback_url, "/playback/%s", stasis_app_playback_get_id(playback)) == -1) { playback_url = NULL; ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } json = stasis_app_playback_to_json(playback); if (!json) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } ast_ari_response_created(response, playback_url, json); }