From a75fd32212c35b41143442bd757387fad636177a Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Wed, 3 Jul 2013 17:58:45 +0000 Subject: ARI - channel recording support This patch is the first step in adding recording support to the Asterisk REST Interface. Recordings are stored in /var/spool/recording. Since recordings may be destructive (overwriting existing files), the API rejects attempts to escape the recording directory (avoiding issues if someone attempts to record to ../../lib/sounds/greeting, for example). (closes issue ASTERISK-21594) (closes issue ASTERISK-21581) Review: https://reviewboard.asterisk.org/r/2612/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393550 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- res/stasis_http/resource_channels.c | 136 +++++++++++++++++++++++++++++++++- res/stasis_http/resource_channels.h | 4 +- res/stasis_http/resource_recordings.c | 26 ++++++- res/stasis_http/resource_recordings.h | 40 +++++----- 4 files changed, 181 insertions(+), 25 deletions(-) (limited to 'res/stasis_http') diff --git a/res/stasis_http/resource_channels.c b/res/stasis_http/resource_channels.c index 0fbb75487..8db3b697c 100644 --- a/res/stasis_http/resource_channels.c +++ b/res/stasis_http/resource_channels.c @@ -1,4 +1,4 @@ -/* -*- C -*- +/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2012 - 2013, Digium, Inc. @@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/callerid.h" #include "asterisk/stasis_app.h" #include "asterisk/stasis_app_playback.h" +#include "asterisk/stasis_app_recording.h" #include "asterisk/stasis_channels.h" #include "resource_channels.h" @@ -249,10 +250,139 @@ void stasis_http_play_on_channel(struct ast_variable *headers, stasis_http_response_created(response, playback_url, json); } -void stasis_http_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct stasis_http_response *response) + +void stasis_http_record_channel(struct ast_variable *headers, + struct ast_record_channel_args *args, + struct stasis_http_response *response) { - ast_log(LOG_ERROR, "TODO: stasis_http_record_channel\n"); + 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_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); + size_t uri_name_maxlen; + + ast_assert(response != NULL); + + if (args->max_duration_seconds < 0) { + stasis_http_response_error( + response, 400, "Bad Request", + "max_duration_seconds cannot be negative"); + return; + } + + if (args->max_silence_seconds < 0) { + stasis_http_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) { + stasis_http_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + } + 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) { + stasis_http_response_error( + response, 400, "Bad Request", + "terminateOn invalid"); + return; + } + + if (options->if_exists == -1) { + stasis_http_response_error( + response, 400, "Bad Request", + "ifExists invalid"); + 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. + */ + stasis_http_response_error( + response, 500, "Internal Server Error", + "Error parsing request"); + break; + case EEXIST: + stasis_http_response_error(response, 409, "Conflict", + "Recording '%s' already in progress", + args->name); + break; + case ENOMEM: + stasis_http_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + break; + case EPERM: + stasis_http_response_error( + response, 400, "Bad Request", + "Recording name invalid"); + break; + default: + ast_log(LOG_WARNING, + "Unrecognized recording error: %s\n", + strerror(errno)); + stasis_http_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) { + stasis_http_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); + + ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name); + if (!recording_url) { + stasis_http_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + json = stasis_app_recording_to_json(recording); + if (!json) { + stasis_http_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + stasis_http_response_created(response, recording_url, json); } + void stasis_http_get_channel(struct ast_variable *headers, struct ast_get_channel_args *args, struct stasis_http_response *response) diff --git a/res/stasis_http/resource_channels.h b/res/stasis_http/resource_channels.h index 57f2a63d2..7e8dc5dbe 100644 --- a/res/stasis_http/resource_channels.h +++ b/res/stasis_http/resource_channels.h @@ -247,8 +247,8 @@ struct ast_record_channel_args { int max_duration_seconds; /*! \brief Maximum duration of silence, in seconds. 0 for no limit */ int max_silence_seconds; - /*! \brief If true, and recording already exists, append to recording */ - int append; + /*! \brief Action to take if a recording with the same name already exists. */ + const char *if_exists; /*! \brief Play beep when recording begins */ int beep; /*! \brief DTMF input to terminate recording */ diff --git a/res/stasis_http/resource_recordings.c b/res/stasis_http/resource_recordings.c index 7d31c42aa..d93d59017 100644 --- a/res/stasis_http/resource_recordings.c +++ b/res/stasis_http/resource_recordings.c @@ -27,6 +27,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include "asterisk/stasis_app_recording.h" #include "resource_recordings.h" void stasis_http_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct stasis_http_response *response) @@ -45,10 +46,31 @@ void stasis_http_get_live_recordings(struct ast_variable *headers, struct ast_ge { ast_log(LOG_ERROR, "TODO: stasis_http_get_live_recordings\n"); } -void stasis_http_get_live_recording(struct ast_variable *headers, struct ast_get_live_recording_args *args, struct stasis_http_response *response) + +void stasis_http_get_live_recording(struct ast_variable *headers, + struct ast_get_live_recording_args *args, + struct stasis_http_response *response) { - ast_log(LOG_ERROR, "TODO: stasis_http_get_live_recording\n"); + RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + + recording = stasis_app_recording_find_by_name(args->recording_name); + if (recording == NULL) { + stasis_http_response_error(response, 404, "Not Found", + "Recording not found"); + return; + } + + json = stasis_app_recording_to_json(recording); + if (json == NULL) { + stasis_http_response_error(response, 500, + "Internal Server Error", "Error building response"); + return; + } + + stasis_http_response_ok(response, ast_json_ref(json)); } + void stasis_http_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct stasis_http_response *response) { ast_log(LOG_ERROR, "TODO: stasis_http_cancel_recording\n"); diff --git a/res/stasis_http/resource_recordings.h b/res/stasis_http/resource_recordings.h index acccc124b..18a5bfe68 100644 --- a/res/stasis_http/resource_recordings.h +++ b/res/stasis_http/resource_recordings.h @@ -52,8 +52,8 @@ struct ast_get_stored_recordings_args { void stasis_http_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_get_stored_recording() */ struct ast_get_stored_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Get a stored recording's details. @@ -65,8 +65,8 @@ struct ast_get_stored_recording_args { void stasis_http_get_stored_recording(struct ast_variable *headers, struct ast_get_stored_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_delete_stored_recording() */ struct ast_delete_stored_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Delete a stored recording. @@ -89,8 +89,8 @@ struct ast_get_live_recordings_args { void stasis_http_get_live_recordings(struct ast_variable *headers, struct ast_get_live_recordings_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_get_live_recording() */ struct ast_get_live_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief List live recordings. @@ -102,8 +102,8 @@ struct ast_get_live_recording_args { void stasis_http_get_live_recording(struct ast_variable *headers, struct ast_get_live_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_cancel_recording() */ struct ast_cancel_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Stop a live recording and discard it. @@ -115,8 +115,8 @@ struct ast_cancel_recording_args { void stasis_http_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_stop_recording() */ struct ast_stop_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Stop a live recording and store it. @@ -128,12 +128,14 @@ struct ast_stop_recording_args { void stasis_http_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_pause_recording() */ struct ast_pause_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Pause a live recording. * + * Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused. + * * \param headers HTTP headers * \param args Swagger parameters * \param[out] response HTTP response @@ -141,8 +143,8 @@ struct ast_pause_recording_args { void stasis_http_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_unpause_recording() */ struct ast_unpause_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Unpause a live recording. @@ -154,12 +156,14 @@ struct ast_unpause_recording_args { void stasis_http_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_mute_recording() */ struct ast_mute_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Mute a live recording. * + * Muting a recording suspends silence detection, which will be restarted when the recording is unmuted. + * * \param headers HTTP headers * \param args Swagger parameters * \param[out] response HTTP response @@ -167,8 +171,8 @@ struct ast_mute_recording_args { void stasis_http_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_unmute_recording() */ struct ast_unmute_recording_args { - /*! \brief Recording's id */ - const char *recording_id; + /*! \brief The name of the recording */ + const char *recording_name; }; /*! * \brief Unmute a live recording. -- cgit v1.2.3