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