security_events: Fix error caused by DTD validation error
[asterisk/asterisk.git] / res / res_stasis_recording.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 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 res_stasis recording support.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend type="module">res_stasis</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/dsp.h"
36 #include "asterisk/file.h"
37 #include "asterisk/module.h"
38 #include "asterisk/paths.h"
39 #include "asterisk/stasis_app_impl.h"
40 #include "asterisk/stasis_app_recording.h"
41 #include "asterisk/stasis_channels.h"
42
43 /*! Number of hash buckets for recording container. Keep it prime! */
44 #define RECORDING_BUCKETS 127
45
46 /*! Comment is ignored by most formats, so we will ignore it, too. */
47 #define RECORDING_COMMENT NULL
48
49 /*! Recording check is unimplemented. le sigh */
50 #define RECORDING_CHECK 0
51
52 /*! Container of all current recordings */
53 static struct ao2_container *recordings;
54
55 struct stasis_app_recording {
56         /*! Recording options. */
57         struct stasis_app_recording_options *options;
58         /*! Absolute path (minus extension) of the recording */
59         char *absolute_name;
60         /*! Control object for the channel we're recording */
61         struct stasis_app_control *control;
62         /*! Current state of the recording. */
63         enum stasis_app_recording_state state;
64         /*! Indicates whether the recording is currently muted */
65         int muted:1;
66 };
67
68 static struct ast_json *recording_to_json(struct stasis_message *message,
69         const struct stasis_message_sanitizer *sanitize)
70 {
71         struct ast_channel_blob *channel_blob = stasis_message_data(message);
72         struct ast_json *blob = channel_blob->blob;
73         const char *state =
74                 ast_json_string_get(ast_json_object_get(blob, "state"));
75         const char *type;
76
77         if (!strcmp(state, "recording")) {
78                 type = "RecordingStarted";
79         } else if (!strcmp(state, "done") || !strcasecmp(state, "canceled")) {
80                 type = "RecordingFinished";
81         } else if (!strcmp(state, "failed")) {
82                 type = "RecordingFailed";
83         } else {
84                 return NULL;
85         }
86
87         return ast_json_pack("{s: s, s: O}",
88                 "type", type,
89                 "recording", blob);
90 }
91
92 STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,
93         .to_json = recording_to_json,
94 );
95
96 static int recording_hash(const void *obj, int flags)
97 {
98         const struct stasis_app_recording *recording = obj;
99         const char *id = flags & OBJ_KEY ? obj : recording->options->name;
100         return ast_str_hash(id);
101 }
102
103 static int recording_cmp(void *obj, void *arg, int flags)
104 {
105         struct stasis_app_recording *lhs = obj;
106         struct stasis_app_recording *rhs = arg;
107         const char *rhs_id = flags & OBJ_KEY ? arg : rhs->options->name;
108
109         if (strcmp(lhs->options->name, rhs_id) == 0) {
110                 return CMP_MATCH | CMP_STOP;
111         } else {
112                 return 0;
113         }
114 }
115
116 static const char *state_to_string(enum stasis_app_recording_state state)
117 {
118         switch (state) {
119         case STASIS_APP_RECORDING_STATE_QUEUED:
120                 return "queued";
121         case STASIS_APP_RECORDING_STATE_RECORDING:
122                 return "recording";
123         case STASIS_APP_RECORDING_STATE_PAUSED:
124                 return "paused";
125         case STASIS_APP_RECORDING_STATE_COMPLETE:
126                 return "done";
127         case STASIS_APP_RECORDING_STATE_FAILED:
128                 return "failed";
129         case STASIS_APP_RECORDING_STATE_CANCELED:
130                 return "canceled";
131         case STASIS_APP_RECORDING_STATE_MAX:
132                 return "?";
133         }
134
135         return "?";
136 }
137
138 static void recording_options_dtor(void *obj)
139 {
140         struct stasis_app_recording_options *options = obj;
141
142         ast_string_field_free_memory(options);
143 }
144
145 struct stasis_app_recording_options *stasis_app_recording_options_create(
146         const char *name, const char *format)
147 {
148         RAII_VAR(struct stasis_app_recording_options *, options, NULL,
149                 ao2_cleanup);
150
151         options = ao2_alloc(sizeof(*options), recording_options_dtor);
152
153         if (!options || ast_string_field_init(options, 128)) {
154                 return NULL;
155         }
156         ast_string_field_set(options, name, name);
157         ast_string_field_set(options, format, format);
158
159         ao2_ref(options, +1);
160         return options;
161 }
162
163 char stasis_app_recording_termination_parse(const char *str)
164 {
165         if (ast_strlen_zero(str)) {
166                 return STASIS_APP_RECORDING_TERMINATE_NONE;
167         }
168
169         if (strcasecmp(str, "none") == 0) {
170                 return STASIS_APP_RECORDING_TERMINATE_NONE;
171         }
172
173         if (strcasecmp(str, "any") == 0) {
174                 return STASIS_APP_RECORDING_TERMINATE_ANY;
175         }
176
177         if (strcasecmp(str, "#") == 0) {
178                 return '#';
179         }
180
181         if (strcasecmp(str, "*") == 0) {
182                 return '*';
183         }
184
185         return STASIS_APP_RECORDING_TERMINATE_INVALID;
186 }
187
188 enum ast_record_if_exists stasis_app_recording_if_exists_parse(
189         const char *str)
190 {
191         if (ast_strlen_zero(str)) {
192                 /* Default value */
193                 return AST_RECORD_IF_EXISTS_FAIL;
194         }
195
196         if (strcasecmp(str, "fail") == 0) {
197                 return AST_RECORD_IF_EXISTS_FAIL;
198         }
199
200         if (strcasecmp(str, "overwrite") == 0) {
201                 return AST_RECORD_IF_EXISTS_OVERWRITE;
202         }
203
204         if (strcasecmp(str, "append") == 0) {
205                 return AST_RECORD_IF_EXISTS_APPEND;
206         }
207
208         return -1;
209 }
210
211 static void recording_publish(struct stasis_app_recording *recording, const char *cause)
212 {
213         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
214         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
215         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
216
217         ast_assert(recording != NULL);
218
219         json = stasis_app_recording_to_json(recording);
220         if (json == NULL) {
221                 return;
222         }
223
224         if (!ast_strlen_zero(cause)) {
225                 struct ast_json *failure_cause = ast_json_string_create(cause);
226
227                 if (!failure_cause) {
228                         return;
229                 }
230
231                 if (ast_json_object_set(json, "cause", failure_cause)) {
232                         ast_json_unref(failure_cause);
233                         return;
234                 }
235         }
236
237         message = ast_channel_blob_create_from_cache(
238                 stasis_app_control_get_channel_id(recording->control),
239                 stasis_app_recording_snapshot_type(), json);
240         if (message == NULL) {
241                 return;
242         }
243
244         stasis_app_control_publish(recording->control, message);
245 }
246
247
248 static void recording_set_state(struct stasis_app_recording *recording,
249                                 enum stasis_app_recording_state state,
250                                 const char *cause)
251 {
252         SCOPED_AO2LOCK(lock, recording);
253         recording->state = state;
254         recording_publish(recording, cause);
255 }
256
257 static enum stasis_app_control_channel_result check_rule_recording(
258         const struct stasis_app_control *control)
259 {
260         return STASIS_APP_CHANNEL_RECORDING;
261 }
262
263 struct stasis_app_control_rule rule_recording = {
264         .check_rule = check_rule_recording
265 };
266
267 static void recording_fail(struct stasis_app_control *control,
268                            struct stasis_app_recording *recording,
269                            const char *cause)
270 {
271         stasis_app_control_unregister_add_rule(control, &rule_recording);
272
273         recording_set_state(
274                 recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
275 }
276
277 static void recording_cleanup(struct stasis_app_recording *recording)
278 {
279         ao2_unlink_flags(recordings, recording,
280                 OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
281 }
282
283 static int record_file(struct stasis_app_control *control,
284         struct ast_channel *chan, void *data)
285 {
286         RAII_VAR(struct stasis_app_recording *, recording,
287                 NULL, recording_cleanup);
288         char *acceptdtmf;
289         int res;
290         int duration = 0;
291
292         recording = data;
293         ast_assert(recording != NULL);
294
295         if (stasis_app_get_bridge(control)) {
296                 ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
297                 recording_fail(control, recording, "Cannot record channel while in bridge");
298                 return -1;
299         }
300
301         switch (recording->options->terminate_on) {
302         case STASIS_APP_RECORDING_TERMINATE_NONE:
303         case STASIS_APP_RECORDING_TERMINATE_INVALID:
304                 acceptdtmf = "";
305                 break;
306         case STASIS_APP_RECORDING_TERMINATE_ANY:
307                 acceptdtmf = "#*0123456789abcd";
308                 break;
309         default:
310                 acceptdtmf = ast_alloca(2);
311                 acceptdtmf[0] = recording->options->terminate_on;
312                 acceptdtmf[1] = '\0';
313         }
314
315         res = ast_auto_answer(chan);
316         if (res != 0) {
317                 ast_debug(3, "%s: Failed to answer\n",
318                         ast_channel_uniqueid(chan));
319                 recording_fail(control, recording, "Failed to answer channel");
320                 return -1;
321         }
322
323         recording_set_state(
324                 recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL);
325         ast_play_and_record_full(chan,
326                 NULL, /* playfile */
327                 recording->absolute_name,
328                 recording->options->max_duration_seconds,
329                 recording->options->format,
330                 &duration,
331                 NULL, /* sound_duration */
332                 recording->options->beep,
333                 -1, /* silencethreshold */
334                 recording->options->max_silence_seconds * 1000,
335                 NULL, /* path */
336                 acceptdtmf,
337                 NULL, /* canceldtmf */
338                 1, /* skip_confirmation_sound */
339                 recording->options->if_exists);
340
341         ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
342
343         recording_set_state(
344                 recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL);
345
346         stasis_app_control_unregister_add_rule(control, &rule_recording);
347
348         return 0;
349 }
350
351 static void recording_dtor(void *obj)
352 {
353         struct stasis_app_recording *recording = obj;
354
355         ao2_cleanup(recording->options);
356 }
357
358 struct stasis_app_recording *stasis_app_control_record(
359         struct stasis_app_control *control,
360         struct stasis_app_recording_options *options)
361 {
362         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
363         char *last_slash;
364
365         errno = 0;
366
367         if (options == NULL ||
368                 ast_strlen_zero(options->name) ||
369                 ast_strlen_zero(options->format) ||
370                 options->max_silence_seconds < 0 ||
371                 options->max_duration_seconds < 0) {
372                 errno = EINVAL;
373                 return NULL;
374         }
375
376         ast_debug(3, "%s: Sending record(%s.%s) command\n",
377                 stasis_app_control_get_channel_id(control), options->name,
378                 options->format);
379
380         recording = ao2_alloc(sizeof(*recording), recording_dtor);
381         if (!recording) {
382                 errno = ENOMEM;
383                 return NULL;
384         }
385
386         ast_asprintf(&recording->absolute_name, "%s/%s",
387                 ast_config_AST_RECORDING_DIR, options->name);
388
389         if (recording->absolute_name == NULL) {
390                 errno = ENOMEM;
391                 return NULL;
392         }
393
394         if ((last_slash = strrchr(recording->absolute_name, '/'))) {
395                 *last_slash = '\0';
396                 if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
397                                 recording->absolute_name, 0777) != 0) {
398                         /* errno set by ast_mkdir */
399                         return NULL;
400                 }
401                 *last_slash = '/';
402         }
403
404         ao2_ref(options, +1);
405         recording->options = options;
406         recording->control = control;
407         recording->state = STASIS_APP_RECORDING_STATE_QUEUED;
408
409         if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) &&
410                         (ast_fileexists(recording->absolute_name, NULL, NULL))) {
411                 ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n",
412                         recording->absolute_name);
413                 errno = EEXIST;
414                 return NULL;
415         }
416
417         {
418                 RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
419                         ao2_cleanup);
420
421                 SCOPED_AO2LOCK(lock, recordings);
422
423                 old_recording = ao2_find(recordings, options->name,
424                         OBJ_KEY | OBJ_NOLOCK);
425                 if (old_recording) {
426                         ast_log(LOG_WARNING,
427                                 "Recording %s already in progress\n",
428                                 recording->options->name);
429                         errno = EEXIST;
430                         return NULL;
431                 }
432                 ao2_link(recordings, recording);
433         }
434
435         stasis_app_control_register_add_rule(control, &rule_recording);
436
437         /* A ref is kept in the recordings container; no need to bump */
438         stasis_app_send_command_async(control, record_file, recording);
439
440         /* Although this should be bumped for the caller */
441         ao2_ref(recording, +1);
442         return recording;
443 }
444
445 enum stasis_app_recording_state stasis_app_recording_get_state(
446         struct stasis_app_recording *recording)
447 {
448         return recording->state;
449 }
450
451 const char *stasis_app_recording_get_name(
452         struct stasis_app_recording *recording)
453 {
454         return recording->options->name;
455 }
456
457 struct stasis_app_recording *stasis_app_recording_find_by_name(const char *name)
458 {
459         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
460
461         recording = ao2_find(recordings, name, OBJ_KEY);
462         if (recording == NULL) {
463                 return NULL;
464         }
465
466         ao2_ref(recording, +1);
467         return recording;
468 }
469
470 struct ast_json *stasis_app_recording_to_json(
471         const struct stasis_app_recording *recording)
472 {
473         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
474
475         if (recording == NULL) {
476                 return NULL;
477         }
478
479         json = ast_json_pack("{s: s, s: s, s: s}",
480                 "name", recording->options->name,
481                 "format", recording->options->format,
482                 "state", state_to_string(recording->state));
483
484         return ast_json_ref(json);
485 }
486
487 typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
488
489 static int recording_noop(struct stasis_app_recording *recording)
490 {
491         return 0;
492 }
493
494 static int recording_disregard(struct stasis_app_recording *recording)
495 {
496         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
497         return 0;
498 }
499
500 static int recording_cancel(struct stasis_app_recording *recording)
501 {
502         int res = 0;
503         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
504         res |= stasis_app_control_queue_control(recording->control,
505                 AST_CONTROL_RECORD_CANCEL);
506         res |= ast_filedelete(recording->absolute_name, NULL);
507         return res;
508 }
509
510 static int recording_stop(struct stasis_app_recording *recording)
511 {
512         recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
513         return stasis_app_control_queue_control(recording->control,
514                 AST_CONTROL_RECORD_STOP);
515 }
516
517 static int recording_pause(struct stasis_app_recording *recording)
518 {
519         recording->state = STASIS_APP_RECORDING_STATE_PAUSED;
520         return stasis_app_control_queue_control(recording->control,
521                 AST_CONTROL_RECORD_SUSPEND);
522 }
523
524 static int recording_unpause(struct stasis_app_recording *recording)
525 {
526         recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
527         return stasis_app_control_queue_control(recording->control,
528                 AST_CONTROL_RECORD_SUSPEND);
529 }
530
531 static int recording_mute(struct stasis_app_recording *recording)
532 {
533         if (recording->muted) {
534                 /* already muted */
535                 return 0;
536         }
537
538         recording->muted = 1;
539         return stasis_app_control_queue_control(recording->control,
540                 AST_CONTROL_RECORD_MUTE);
541 }
542
543 static int recording_unmute(struct stasis_app_recording *recording)
544 {
545         if (!recording->muted) {
546                 /* already unmuted */
547                 return 0;
548         }
549
550         return stasis_app_control_queue_control(recording->control,
551                 AST_CONTROL_RECORD_MUTE);
552 }
553
554 recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX] = {
555         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_CANCEL] = recording_disregard,
556         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_STOP] = recording_disregard,
557         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
558         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_STOP] = recording_stop,
559         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_PAUSE] = recording_pause,
560         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNPAUSE] = recording_noop,
561         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_MUTE] = recording_mute,
562         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
563         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
564         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_STOP] = recording_stop,
565         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_PAUSE] = recording_noop,
566         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNPAUSE] = recording_unpause,
567         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_MUTE] = recording_mute,
568         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
569 };
570
571 enum stasis_app_recording_oper_results stasis_app_recording_operation(
572         struct stasis_app_recording *recording,
573         enum stasis_app_recording_media_operation operation)
574 {
575         recording_operation_cb cb;
576         SCOPED_AO2LOCK(lock, recording);
577
578         if (recording->state < 0 || recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
579                 ast_log(LOG_WARNING, "Invalid recording state %d\n",
580                         recording->state);
581                 return -1;
582         }
583
584         if (operation < 0 || operation >= STASIS_APP_RECORDING_OPER_MAX) {
585                 ast_log(LOG_WARNING, "Invalid recording operation %d\n",
586                         operation);
587                 return -1;
588         }
589
590         cb = operations[recording->state][operation];
591
592         if (!cb) {
593                 if (recording->state != STASIS_APP_RECORDING_STATE_RECORDING) {
594                         /* So we can be specific in our error message. */
595                         return STASIS_APP_RECORDING_OPER_NOT_RECORDING;
596                 } else {
597                         /* And, really, all operations should be valid during
598                          * recording */
599                         ast_log(LOG_ERROR,
600                                 "Unhandled operation during recording: %d\n",
601                                 operation);
602                         return STASIS_APP_RECORDING_OPER_FAILED;
603                 }
604         }
605
606         return cb(recording) ?
607                 STASIS_APP_RECORDING_OPER_FAILED : STASIS_APP_RECORDING_OPER_OK;
608 }
609
610 static int load_module(void)
611 {
612         int r;
613
614         r = STASIS_MESSAGE_TYPE_INIT(stasis_app_recording_snapshot_type);
615         if (r != 0) {
616                 return AST_MODULE_LOAD_FAILURE;
617         }
618
619         recordings = ao2_container_alloc(RECORDING_BUCKETS, recording_hash,
620                 recording_cmp);
621         if (!recordings) {
622                 return AST_MODULE_LOAD_FAILURE;
623         }
624         return AST_MODULE_LOAD_SUCCESS;
625 }
626
627 static int unload_module(void)
628 {
629         ao2_cleanup(recordings);
630         recordings = NULL;
631         STASIS_MESSAGE_TYPE_CLEANUP(stasis_app_recording_snapshot_type);
632         return 0;
633 }
634
635 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis application recording support",
636         .load = load_module,
637         .unload = unload_module,
638         .nonoptreq = "res_stasis",
639         .load_pri = AST_MODPRI_APP_DEPEND);