ARI: Add the ability to download the media associated with a stored recording
[asterisk/asterisk.git] / res / ari / resource_recordings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief /api-docs/recordings.{format} implementation- Recording resources
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend type="module">res_stasis_recording</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_REGISTER_FILE()
34
35 #include "asterisk/stasis_app_recording.h"
36 #include "resource_recordings.h"
37
38 void ast_ari_recordings_list_stored(struct ast_variable *headers,
39         struct ast_ari_recordings_list_stored_args *args,
40         struct ast_ari_response *response)
41 {
42         RAII_VAR(struct ao2_container *, recordings, NULL, ao2_cleanup);
43         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
44         struct ao2_iterator i;
45         void *obj;
46
47         recordings = stasis_app_stored_recording_find_all();
48
49         if (!recordings) {
50                 ast_ari_response_alloc_failed(response);
51                 return;
52         }
53
54         json = ast_json_array_create();
55         if (!json) {
56                 ast_ari_response_alloc_failed(response);
57                 return;
58         }
59
60         i = ao2_iterator_init(recordings, 0);
61         while ((obj = ao2_iterator_next(&i))) {
62                 RAII_VAR(struct stasis_app_stored_recording *, recording, obj,
63                         ao2_cleanup);
64
65                 int r = ast_json_array_append(
66                         json, stasis_app_stored_recording_to_json(recording));
67                 if (r != 0) {
68                         ast_ari_response_alloc_failed(response);
69                         ao2_iterator_destroy(&i);
70                         return;
71                 }
72         }
73         ao2_iterator_destroy(&i);
74
75         ast_ari_response_ok(response, ast_json_ref(json));
76 }
77
78 void ast_ari_recordings_get_stored(struct ast_variable *headers,
79         struct ast_ari_recordings_get_stored_args *args,
80         struct ast_ari_response *response)
81 {
82         RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
83                 ao2_cleanup);
84         struct ast_json *json;
85
86         recording = stasis_app_stored_recording_find_by_name(
87                 args->recording_name);
88         if (recording == NULL) {
89                 ast_ari_response_error(response, 404, "Not Found",
90                         "Recording not found");
91                 return;
92         }
93
94         json = stasis_app_stored_recording_to_json(recording);
95         if (json == NULL) {
96                 ast_ari_response_error(response, 500,
97                         "Internal Server Error", "Error building response");
98                 return;
99         }
100
101         ast_ari_response_ok(response, json);
102 }
103
104 void ast_ari_recordings_get_stored_file(struct ast_tcptls_session_instance *ser,
105         struct ast_variable *headers, struct ast_ari_recordings_get_stored_file_args *args,
106         struct ast_ari_response *response)
107 {
108         RAII_VAR(struct stasis_app_stored_recording *, recording,
109                 stasis_app_stored_recording_find_by_name(args->recording_name),
110                 ao2_cleanup);
111         static const char *format_type_names[AST_MEDIA_TYPE_TEXT + 1] = {
112                 [AST_MEDIA_TYPE_UNKNOWN] = "binary",
113                 [AST_MEDIA_TYPE_AUDIO] = "audio",
114                 [AST_MEDIA_TYPE_VIDEO] = "video",
115                 [AST_MEDIA_TYPE_IMAGE] = "image",
116                 [AST_MEDIA_TYPE_TEXT] = "text",
117         };
118         struct ast_format *format;
119
120         response->message = ast_json_null();
121
122         if (!recording) {
123                 ast_ari_response_error(response, 404, "Not Found",
124                         "Recording not found");
125                 return;
126         }
127
128         format = ast_get_format_for_file_ext(stasis_app_stored_recording_get_extension(recording));
129         if (!format) {
130                 ast_ari_response_error(response, 500, "Internal Server Error",
131                         "Format specified by recording not available or loaded");
132                 return;
133         }
134
135         response->fd = open(stasis_app_stored_recording_get_filename(recording), O_RDONLY);
136         if (response->fd < 0) {
137                 ast_ari_response_error(response, 403, "Forbidden",
138                         "Recording could not be opened");
139                 return;
140         }
141
142         ast_str_append(&response->headers, 0, "Content-Type: %s/%s\r\n",
143                 format_type_names[ast_format_get_type(format)],
144                 stasis_app_stored_recording_get_extension(recording));
145         ast_ari_response_ok(response, ast_json_null());
146 }
147
148 void ast_ari_recordings_copy_stored(struct ast_variable *headers,
149         struct ast_ari_recordings_copy_stored_args *args,
150         struct ast_ari_response *response)
151 {
152         RAII_VAR(struct stasis_app_stored_recording *, src_recording, NULL,
153                 ao2_cleanup);
154         RAII_VAR(struct stasis_app_stored_recording *, dst_recording, NULL,
155                 ao2_cleanup);
156         struct ast_json *json;
157         int res;
158
159         src_recording = stasis_app_stored_recording_find_by_name(
160                 args->recording_name);
161         if (src_recording == NULL) {
162                 ast_ari_response_error(response, 404, "Not Found",
163                         "Recording not found");
164                 return;
165         }
166
167         dst_recording = stasis_app_stored_recording_find_by_name(
168                 args->destination_recording_name);
169         if (dst_recording) {
170                 ast_ari_response_error(response, 409, "Conflict",
171                         "A recording with the same name already exists on the system");
172                 return;
173         }
174
175         /* See if we got our name rejected */
176         switch (errno) {
177         case EINVAL:
178                 ast_ari_response_error(response, 400, "Bad request",
179                         "Invalid destination recording name");
180                 return;
181         case EACCES:
182                 ast_ari_response_error(response, 403, "Forbidden",
183                         "Destination file path is forbidden");
184                 return;
185         default:
186                 break;
187         }
188
189         res = stasis_app_stored_recording_copy(src_recording,
190                 args->destination_recording_name, &dst_recording);
191         if (res) {
192                 switch (errno) {
193                 case EACCES:
194                 case EPERM:
195                         ast_ari_response_error(response, 500,
196                                 "Internal Server Error",
197                                 "Copy failed");
198                         break;
199                 default:
200                         ast_log(LOG_WARNING,
201                                 "Unexpected error copying recording %s to %s: %s\n",
202                                 args->recording_name, args->destination_recording_name, strerror(errno));
203                         ast_ari_response_error(response, 500,
204                                 "Internal Server Error",
205                                 "Copy failed");
206                         break;
207                 }
208                 return;
209         }
210
211         json = stasis_app_stored_recording_to_json(dst_recording);
212         if (json == NULL) {
213                 ast_ari_response_error(response, 500,
214                         "Internal Server Error", "Error building response");
215                 return;
216         }
217
218         ast_ari_response_ok(response, json);
219 }
220
221 void ast_ari_recordings_delete_stored(struct ast_variable *headers,
222         struct ast_ari_recordings_delete_stored_args *args,
223         struct ast_ari_response *response)
224 {
225         RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
226                 ao2_cleanup);
227         int res;
228
229         recording = stasis_app_stored_recording_find_by_name(
230                 args->recording_name);
231         if (recording == NULL) {
232                 ast_ari_response_error(response, 404, "Not Found",
233                         "Recording not found");
234                 return;
235         }
236
237         res = stasis_app_stored_recording_delete(recording);
238
239         if (res != 0) {
240                 switch (errno) {
241                 case EACCES:
242                 case EPERM:
243                         ast_ari_response_error(response, 500,
244                                 "Internal Server Error",
245                                 "Delete failed");
246                         break;
247                 default:
248                         ast_log(LOG_WARNING,
249                                 "Unexpected error deleting recording %s: %s\n",
250                                 args->recording_name, strerror(errno));
251                         ast_ari_response_error(response, 500,
252                                 "Internal Server Error",
253                                 "Delete failed");
254                         break;
255                 }
256                 return;
257         }
258
259         ast_ari_response_no_content(response);
260 }
261
262 void ast_ari_recordings_get_live(struct ast_variable *headers,
263         struct ast_ari_recordings_get_live_args *args,
264         struct ast_ari_response *response)
265 {
266         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
267         struct ast_json *json;
268
269         recording = stasis_app_recording_find_by_name(args->recording_name);
270         if (recording == NULL) {
271                 ast_ari_response_error(response, 404, "Not Found",
272                         "Recording not found");
273                 return;
274         }
275
276         json = stasis_app_recording_to_json(recording);
277         if (json == NULL) {
278                 ast_ari_response_error(response, 500,
279                         "Internal Server Error", "Error building response");
280                 return;
281         }
282
283         ast_ari_response_ok(response, json);
284 }
285
286 static void control_recording(const char *name,
287         enum stasis_app_recording_media_operation operation,
288         struct ast_ari_response *response)
289 {
290         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
291         enum stasis_app_recording_oper_results res;
292
293         recording = stasis_app_recording_find_by_name(name);
294         if (recording == NULL) {
295                 ast_ari_response_error(response, 404, "Not Found",
296                         "Recording not found");
297                 return;
298         }
299
300         res = stasis_app_recording_operation(recording, operation);
301
302         switch (res) {
303         case STASIS_APP_RECORDING_OPER_OK:
304                 ast_ari_response_no_content(response);
305                 return;
306         case STASIS_APP_RECORDING_OPER_FAILED:
307                 ast_ari_response_error(response, 500,
308                         "Internal Server Error", "Recording operation failed");
309                 return;
310         case STASIS_APP_RECORDING_OPER_NOT_RECORDING:
311                 ast_ari_response_error(response, 409,
312                         "Conflict", "Recording not in session");
313         }
314 }
315
316 void ast_ari_recordings_cancel(struct ast_variable *headers,
317         struct ast_ari_recordings_cancel_args *args,
318         struct ast_ari_response *response)
319 {
320         control_recording(args->recording_name, STASIS_APP_RECORDING_CANCEL,
321                 response);
322 }
323
324 void ast_ari_recordings_stop(struct ast_variable *headers,
325         struct ast_ari_recordings_stop_args *args,
326         struct ast_ari_response *response)
327 {
328         control_recording(args->recording_name, STASIS_APP_RECORDING_STOP,
329                 response);
330 }
331
332 void ast_ari_recordings_pause(struct ast_variable *headers,
333         struct ast_ari_recordings_pause_args *args,
334         struct ast_ari_response *response)
335 {
336         control_recording(args->recording_name, STASIS_APP_RECORDING_PAUSE,
337                 response);
338 }
339
340 void ast_ari_recordings_unpause(struct ast_variable *headers,
341         struct ast_ari_recordings_unpause_args *args,
342         struct ast_ari_response *response)
343 {
344         control_recording(args->recording_name, STASIS_APP_RECORDING_UNPAUSE,
345                 response);
346 }
347
348 void ast_ari_recordings_mute(struct ast_variable *headers,
349         struct ast_ari_recordings_mute_args *args,
350         struct ast_ari_response *response)
351 {
352         control_recording(args->recording_name, STASIS_APP_RECORDING_MUTE,
353                 response);
354 }
355
356 void ast_ari_recordings_unmute(struct ast_variable *headers,
357         struct ast_ari_recordings_unmute_args *args,
358         struct ast_ari_response *response)
359 {
360         control_recording(args->recording_name, STASIS_APP_RECORDING_UNMUTE,
361                 response);
362 }