res_stasis_recording: Add a "target_uri" field to recording events.
[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                         return;
233                 }
234         }
235
236         message = ast_channel_blob_create_from_cache(
237                 stasis_app_control_get_channel_id(recording->control),
238                 stasis_app_recording_snapshot_type(), json);
239         if (message == NULL) {
240                 return;
241         }
242
243         stasis_app_control_publish(recording->control, message);
244 }
245
246
247 static void recording_set_state(struct stasis_app_recording *recording,
248                                 enum stasis_app_recording_state state,
249                                 const char *cause)
250 {
251         SCOPED_AO2LOCK(lock, recording);
252         recording->state = state;
253         recording_publish(recording, cause);
254 }
255
256 static enum stasis_app_control_channel_result check_rule_recording(
257         const struct stasis_app_control *control)
258 {
259         return STASIS_APP_CHANNEL_RECORDING;
260 }
261
262 struct stasis_app_control_rule rule_recording = {
263         .check_rule = check_rule_recording
264 };
265
266 static void recording_fail(struct stasis_app_control *control,
267                            struct stasis_app_recording *recording,
268                            const char *cause)
269 {
270         stasis_app_control_unregister_add_rule(control, &rule_recording);
271
272         recording_set_state(
273                 recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
274 }
275
276 static void recording_cleanup(struct stasis_app_recording *recording)
277 {
278         ao2_unlink_flags(recordings, recording,
279                 OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
280 }
281
282 static int record_file(struct stasis_app_control *control,
283         struct ast_channel *chan, void *data)
284 {
285         RAII_VAR(struct stasis_app_recording *, recording,
286                 NULL, recording_cleanup);
287         char *acceptdtmf;
288         int res;
289         int duration = 0;
290
291         recording = data;
292         ast_assert(recording != NULL);
293
294         if (stasis_app_get_bridge(control)) {
295                 ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
296                 recording_fail(control, recording, "Cannot record channel while in bridge");
297                 return -1;
298         }
299
300         switch (recording->options->terminate_on) {
301         case STASIS_APP_RECORDING_TERMINATE_NONE:
302         case STASIS_APP_RECORDING_TERMINATE_INVALID:
303                 acceptdtmf = "";
304                 break;
305         case STASIS_APP_RECORDING_TERMINATE_ANY:
306                 acceptdtmf = "#*0123456789abcd";
307                 break;
308         default:
309                 acceptdtmf = ast_alloca(2);
310                 acceptdtmf[0] = recording->options->terminate_on;
311                 acceptdtmf[1] = '\0';
312         }
313
314         res = ast_auto_answer(chan);
315         if (res != 0) {
316                 ast_debug(3, "%s: Failed to answer\n",
317                         ast_channel_uniqueid(chan));
318                 recording_fail(control, recording, "Failed to answer channel");
319                 return -1;
320         }
321
322         recording_set_state(
323                 recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL);
324         ast_play_and_record_full(chan,
325                 NULL, /* playfile */
326                 recording->absolute_name,
327                 recording->options->max_duration_seconds,
328                 recording->options->format,
329                 &duration,
330                 NULL, /* sound_duration */
331                 recording->options->beep,
332                 -1, /* silencethreshold */
333                 recording->options->max_silence_seconds * 1000,
334                 NULL, /* path */
335                 acceptdtmf,
336                 NULL, /* canceldtmf */
337                 1, /* skip_confirmation_sound */
338                 recording->options->if_exists);
339
340         ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
341
342         recording_set_state(
343                 recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL);
344
345         stasis_app_control_unregister_add_rule(control, &rule_recording);
346
347         return 0;
348 }
349
350 static void recording_dtor(void *obj)
351 {
352         struct stasis_app_recording *recording = obj;
353
354         ast_free(recording->absolute_name);
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, s: s}",
480                 "name", recording->options->name,
481                 "format", recording->options->format,
482                 "state", state_to_string(recording->state),
483                 "target_uri", recording->options->target);
484
485         return ast_json_ref(json);
486 }
487
488 typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
489
490 static int recording_noop(struct stasis_app_recording *recording)
491 {
492         return 0;
493 }
494
495 static int recording_disregard(struct stasis_app_recording *recording)
496 {
497         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
498         return 0;
499 }
500
501 static int recording_cancel(struct stasis_app_recording *recording)
502 {
503         int res = 0;
504         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
505         res |= stasis_app_control_queue_control(recording->control,
506                 AST_CONTROL_RECORD_CANCEL);
507         res |= ast_filedelete(recording->absolute_name, NULL);
508         return res;
509 }
510
511 static int recording_stop(struct stasis_app_recording *recording)
512 {
513         recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
514         return stasis_app_control_queue_control(recording->control,
515                 AST_CONTROL_RECORD_STOP);
516 }
517
518 static int recording_pause(struct stasis_app_recording *recording)
519 {
520         recording->state = STASIS_APP_RECORDING_STATE_PAUSED;
521         return stasis_app_control_queue_control(recording->control,
522                 AST_CONTROL_RECORD_SUSPEND);
523 }
524
525 static int recording_unpause(struct stasis_app_recording *recording)
526 {
527         recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
528         return stasis_app_control_queue_control(recording->control,
529                 AST_CONTROL_RECORD_SUSPEND);
530 }
531
532 static int recording_mute(struct stasis_app_recording *recording)
533 {
534         if (recording->muted) {
535                 /* already muted */
536                 return 0;
537         }
538
539         recording->muted = 1;
540         return stasis_app_control_queue_control(recording->control,
541                 AST_CONTROL_RECORD_MUTE);
542 }
543
544 static int recording_unmute(struct stasis_app_recording *recording)
545 {
546         if (!recording->muted) {
547                 /* already unmuted */
548                 return 0;
549         }
550
551         return stasis_app_control_queue_control(recording->control,
552                 AST_CONTROL_RECORD_MUTE);
553 }
554
555 recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX] = {
556         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_CANCEL] = recording_disregard,
557         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_STOP] = recording_disregard,
558         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
559         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_STOP] = recording_stop,
560         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_PAUSE] = recording_pause,
561         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNPAUSE] = recording_noop,
562         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_MUTE] = recording_mute,
563         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
564         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
565         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_STOP] = recording_stop,
566         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_PAUSE] = recording_noop,
567         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNPAUSE] = recording_unpause,
568         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_MUTE] = recording_mute,
569         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
570 };
571
572 enum stasis_app_recording_oper_results stasis_app_recording_operation(
573         struct stasis_app_recording *recording,
574         enum stasis_app_recording_media_operation operation)
575 {
576         recording_operation_cb cb;
577         SCOPED_AO2LOCK(lock, recording);
578
579         if (recording->state < 0 || recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
580                 ast_log(LOG_WARNING, "Invalid recording state %d\n",
581                         recording->state);
582                 return -1;
583         }
584
585         if (operation < 0 || operation >= STASIS_APP_RECORDING_OPER_MAX) {
586                 ast_log(LOG_WARNING, "Invalid recording operation %d\n",
587                         operation);
588                 return -1;
589         }
590
591         cb = operations[recording->state][operation];
592
593         if (!cb) {
594                 if (recording->state != STASIS_APP_RECORDING_STATE_RECORDING) {
595                         /* So we can be specific in our error message. */
596                         return STASIS_APP_RECORDING_OPER_NOT_RECORDING;
597                 } else {
598                         /* And, really, all operations should be valid during
599                          * recording */
600                         ast_log(LOG_ERROR,
601                                 "Unhandled operation during recording: %d\n",
602                                 operation);
603                         return STASIS_APP_RECORDING_OPER_FAILED;
604                 }
605         }
606
607         return cb(recording) ?
608                 STASIS_APP_RECORDING_OPER_FAILED : STASIS_APP_RECORDING_OPER_OK;
609 }
610
611 static int load_module(void)
612 {
613         int r;
614
615         r = STASIS_MESSAGE_TYPE_INIT(stasis_app_recording_snapshot_type);
616         if (r != 0) {
617                 return AST_MODULE_LOAD_FAILURE;
618         }
619
620         recordings = ao2_container_alloc(RECORDING_BUCKETS, recording_hash,
621                 recording_cmp);
622         if (!recordings) {
623                 return AST_MODULE_LOAD_FAILURE;
624         }
625         return AST_MODULE_LOAD_SUCCESS;
626 }
627
628 static int unload_module(void)
629 {
630         ao2_cleanup(recordings);
631         recordings = NULL;
632         STASIS_MESSAGE_TYPE_CLEANUP(stasis_app_recording_snapshot_type);
633         return 0;
634 }
635
636 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis application recording support",
637         .load = load_module,
638         .unload = unload_module,
639         .nonoptreq = "res_stasis",
640         .load_pri = AST_MODPRI_APP_DEPEND);