Merged revisions 77191 via svnmerge from
[asterisk/asterisk.git] / res / res_monitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@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 PBX channel monitoring
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25  
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <libgen.h>
37
38 #include "asterisk/lock.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/file.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/monitor.h"
47 #include "asterisk/app.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/config.h"
50 #include "asterisk/options.h"
51
52 AST_MUTEX_DEFINE_STATIC(monitorlock);
53
54 #define LOCK_IF_NEEDED(lock, needed) do { \
55         if (needed) \
56                 ast_channel_lock(lock); \
57         } while(0)
58
59 #define UNLOCK_IF_NEEDED(lock, needed) do { \
60         if (needed) \
61                 ast_channel_unlock(lock); \
62         } while (0)
63
64 static unsigned long seq = 0;
65
66 static char *monitor_synopsis = "Monitor a channel";
67
68 static char *monitor_descrip = "Monitor([file_format[:urlbase]|[fname_base]|[options]]):\n"
69 "Used to start monitoring a channel. The channel's input and output\n"
70 "voice packets are logged to files until the channel hangs up or\n"
71 "monitoring is stopped by the StopMonitor application.\n"
72 "  file_format          optional, if not set, defaults to \"wav\"\n"
73 "  fname_base           if set, changes the filename used to the one specified.\n"
74 "  options:\n"
75 "    m   - when the recording ends mix the two leg files into one and\n"
76 "          delete the two leg files.  If the variable MONITOR_EXEC is set, the\n"
77 "          application referenced in it will be executed instead of\n"
78 "          soxmix and the raw leg files will NOT be deleted automatically.\n"
79 "          soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
80 "          and a target mixed file name which is the same as the leg file names\n"
81 "          only without the in/out designator.\n"
82 "          If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
83 "          additional arguements to MONITOR_EXEC\n"
84 "          Both MONITOR_EXEC and the Mix flag can be set from the\n"
85 "          administrator interface\n"
86 "\n"
87 "    b   - Don't begin recording unless a call is bridged to another channel\n"
88 "\n"
89 "    i   - Skip recording of input stream (disables m option)\n"
90 "\n"
91 "    o   - Skip recording of output stream (disables m option)\n"
92 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
93 "monitored, otherwise 0.\n"
94 ;
95
96 static char *stopmonitor_synopsis = "Stop monitoring a channel";
97
98 static char *stopmonitor_descrip = "StopMonitor\n"
99         "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
100
101 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
102
103 static char *changemonitor_descrip = "ChangeMonitor(filename_base)\n"
104         "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
105         "The argument is the new filename base to use for monitoring this channel.\n";
106
107 static char *pausemonitor_synopsis = "Pause monitoring of a channel";
108
109 static char *pausemonitor_descrip = "PauseMonitor\n"
110         "Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.\n";
111
112 static char *unpausemonitor_synopsis = "Unpause monitoring of a channel";
113
114 static char *unpausemonitor_descrip = "UnpauseMonitor\n"
115         "Unpauses monitoring of a channel on which monitoring had\n"
116         "previously been paused with PauseMonitor.\n";
117
118 /*! 
119  * \brief Change state of monitored channel 
120  * \param chan 
121  * \param state monitor state
122  * \retval 0 on success.
123  * \retval -1 on failure.
124 */
125 static int ast_monitor_set_state(struct ast_channel *chan, int state)
126 {
127         LOCK_IF_NEEDED(chan, 1);
128         if (!chan->monitor) {
129                 UNLOCK_IF_NEEDED(chan, 1);
130                 return -1;
131         }
132         chan->monitor->state = state;
133         UNLOCK_IF_NEEDED(chan, 1);
134         return 0;
135 }
136
137 /*! \brief Start monitoring a channel
138  * \param chan ast_channel struct to record
139  * \param format_spec file format to use for recording
140  * \param fname_base filename base to record to
141  * \param need_lock whether to lock the channel mutex
142  * \param stream_action whether to record the input and/or output streams.  X_REC_IN | X_REC_OUT is most often used
143  * Creates the file to record, if no format is specified it assumes WAV
144  * It also sets channel variable __MONITORED=yes
145  * \retval 0 on success
146  * \retval -1 on failure
147  */
148 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
149                 const char *fname_base, int need_lock, int stream_action)
150 {
151         int res = 0;
152
153         LOCK_IF_NEEDED(chan, need_lock);
154
155         if (!(chan->monitor)) {
156                 struct ast_channel_monitor *monitor;
157                 char *channel_name, *p;
158
159                 /* Create monitoring directory if needed */
160                 ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
161
162                 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
163                         UNLOCK_IF_NEEDED(chan, need_lock);
164                         return -1;
165                 }
166
167                 /* Determine file names */
168                 if (!ast_strlen_zero(fname_base)) {
169                         int directory = strchr(fname_base, '/') ? 1 : 0;
170                         /* try creating the directory just in case it doesn't exist */
171                         if (directory) {
172                                 char *name = ast_strdupa(fname_base);
173                                 ast_mkdir(dirname(name), 0777);
174                         }
175                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
176                                                 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
177                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
178                                                 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
179                         ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
180                 } else {
181                         ast_mutex_lock(&monitorlock);
182                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
183                                                 ast_config_AST_MONITOR_DIR, seq);
184                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
185                                                 ast_config_AST_MONITOR_DIR, seq);
186                         seq++;
187                         ast_mutex_unlock(&monitorlock);
188
189                         channel_name = ast_strdupa(chan->name);
190                         while ((p = strchr(channel_name, '/'))) {
191                                 *p = '-';
192                         }
193                         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
194                                          ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
195                         monitor->filename_changed = 1;
196                 }
197
198                 monitor->stop = ast_monitor_stop;
199
200                 /* Determine file format */
201                 if (!ast_strlen_zero(format_spec)) {
202                         monitor->format = ast_strdup(format_spec);
203                 } else {
204                         monitor->format = ast_strdup("wav");
205                 }
206                 
207                 /* open files */
208                 if (stream_action & X_REC_IN) {
209                         if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
210                                 ast_filedelete(monitor->read_filename, NULL);
211                         if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
212                                                         monitor->format, NULL,
213                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
214                                 ast_log(LOG_WARNING, "Could not create file %s\n",
215                                                         monitor->read_filename);
216                                 ast_free(monitor);
217                                 UNLOCK_IF_NEEDED(chan, need_lock);
218                                 return -1;
219                         }
220                 } else
221                         monitor->read_stream = NULL;
222
223                 if (stream_action & X_REC_OUT) {
224                         if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
225                                 ast_filedelete(monitor->write_filename, NULL);
226                         }
227                         if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
228                                                         monitor->format, NULL,
229                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
230                                 ast_log(LOG_WARNING, "Could not create file %s\n",
231                                                         monitor->write_filename);
232                                 ast_closestream(monitor->read_stream);
233                                 ast_free(monitor);
234                                 UNLOCK_IF_NEEDED(chan, need_lock);
235                                 return -1;
236                         }
237                 } else
238                         monitor->write_stream = NULL;
239
240                 chan->monitor = monitor;
241                 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
242                 /* so we know this call has been monitored in case we need to bill for it or something */
243                 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
244         } else {
245                 ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
246                 res = -1;
247         }
248
249         UNLOCK_IF_NEEDED(chan, need_lock);
250
251         return res;
252 }
253
254 /*!
255  * \brief Get audio format.
256  * \param format recording format.
257  * The file format extensions that Asterisk uses are not all the same as that
258  * which soxmix expects.  This function ensures that the format used as the
259  * extension on the filename is something soxmix will understand.
260  */
261 static const char *get_soxmix_format(const char *format)
262 {
263         const char *res = format;
264
265         if (!strcasecmp(format,"ulaw"))
266                 res = "ul";
267         if (!strcasecmp(format,"alaw"))
268                 res = "al";
269         
270         return res;
271 }
272
273 /*! 
274  * \brief Stop monitoring channel 
275  * \param chan 
276  * \param need_lock
277  * Stop the recording, close any open streams, mix in/out channels if required
278  * \return Always 0
279 */
280 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
281 {
282         int delfiles = 0;
283
284         LOCK_IF_NEEDED(chan, need_lock);
285
286         if (chan->monitor) {
287                 char filename[ FILENAME_MAX ];
288
289                 if (chan->monitor->read_stream) {
290                         ast_closestream(chan->monitor->read_stream);
291                 }
292                 if (chan->monitor->write_stream) {
293                         ast_closestream(chan->monitor->write_stream);
294                 }
295
296                 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
297                         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
298                                 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
299                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
300                                         ast_filedelete(filename, NULL);
301                                 }
302                                 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
303                         } else {
304                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
305                         }
306
307                         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
308                                 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
309                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
310                                         ast_filedelete(filename, NULL);
311                                 }
312                                 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
313                         } else {
314                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
315                         }
316                 }
317
318                 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
319                         char tmp[1024];
320                         char tmp2[1024];
321                         const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
322                         char *name = chan->monitor->filename_base;
323                         int directory = strchr(name, '/') ? 1 : 0;
324                         char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
325                         const char *execute, *execute_args;
326
327                         /* Set the execute application */
328                         execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
329                         if (ast_strlen_zero(execute)) { 
330                                 execute = "nice -n 19 soxmix";
331                                 format = get_soxmix_format(format);
332                                 delfiles = 1;
333                         } 
334                         execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
335                         if (ast_strlen_zero(execute_args)) {
336                                 execute_args = "";
337                         }
338                         
339                         snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
340                         if (delfiles) {
341                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
342                                 ast_copy_string(tmp, tmp2, sizeof(tmp));
343                         }
344                         ast_debug(1,"monitor executing %s\n",tmp);
345                         if (ast_safe_system(tmp) == -1)
346                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
347                 }
348                 
349                 ast_free(chan->monitor->format);
350                 ast_free(chan->monitor);
351                 chan->monitor = NULL;
352         }
353
354         UNLOCK_IF_NEEDED(chan, need_lock);
355
356         return 0;
357 }
358
359
360 /*! \brief Pause monitoring of channel */
361 int ast_monitor_pause(struct ast_channel *chan)
362 {
363         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
364 }
365
366 /*! \brief Unpause monitoring of channel */
367 int ast_monitor_unpause(struct ast_channel *chan)
368 {
369         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
370 }
371
372 /*! \brief Wrapper for ast_monitor_pause */
373 static int pause_monitor_exec(struct ast_channel *chan, void *data)
374 {
375         return ast_monitor_pause(chan);
376 }
377
378 /*! \brief Wrapper for ast_monitor_unpause */
379 static int unpause_monitor_exec(struct ast_channel *chan, void *data)
380 {
381         return ast_monitor_unpause(chan);
382 }
383
384 /*! 
385  * \brief Change monitored filename of channel 
386  * \param chan
387  * \param fname_base new filename
388  * \param need_lock
389  * \retval 0 on success.
390  * \retval -1 on failure.
391 */
392 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
393 {
394         if (ast_strlen_zero(fname_base)) {
395                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
396                 return -1;
397         }
398
399         LOCK_IF_NEEDED(chan, need_lock);
400
401         if (chan->monitor) {
402                 int directory = strchr(fname_base, '/') ? 1 : 0;
403                 /* try creating the directory just in case it doesn't exist */
404                 if (directory) {
405                         char *name = ast_strdupa(fname_base);
406                         ast_mkdir(dirname(name), 0777);
407                 }
408
409                 snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
410                 chan->monitor->filename_changed = 1;
411         } else {
412                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
413         }
414
415         UNLOCK_IF_NEEDED(chan, need_lock);
416
417         return 0;
418 }
419
420  
421 /*!
422  * \brief Start monitor
423  * \param chan
424  * \param data arguements passed  fname|options
425  * \retval 0 on success.
426  * \retval -1 on failure.
427 */
428 static int start_monitor_exec(struct ast_channel *chan, void *data)
429 {
430         char *arg = NULL;
431         char *format = NULL;
432         char *fname_base = NULL;
433         char *options = NULL;
434         char *delay = NULL;
435         char *urlprefix = NULL;
436         char tmp[256];
437         int stream_action = X_REC_IN | X_REC_OUT;
438         int joinfiles = 0;
439         int waitforbridge = 0;
440         int res = 0;
441         
442         /* Parse arguments. */
443         if (!ast_strlen_zero((char*)data)) {
444                 arg = ast_strdupa((char*)data);
445                 format = arg;
446                 fname_base = strchr(arg, '|');
447                 if (fname_base) {
448                         *fname_base = 0;
449                         fname_base++;
450                         if ((options = strchr(fname_base, '|'))) {
451                                 *options = 0;
452                                 options++;
453                                 if (strchr(options, 'm'))
454                                         stream_action |= X_JOIN;
455                                 if (strchr(options, 'b'))
456                                         waitforbridge = 1;
457                                 if (strchr(options, 'i'))
458                                         stream_action &= ~X_REC_IN;
459                                 if (strchr(options, 'o'))
460                                         stream_action &= ~X_REC_OUT;
461                         }
462                 }
463                 arg = strchr(format,':');
464                 if (arg) {
465                         *arg++ = 0;
466                         urlprefix = arg;
467                 }
468         }
469         if (urlprefix) {
470                 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
471                         ((strcmp(format,"gsm")) ? "wav" : "gsm"));
472                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
473                         return -1;
474                 ast_cdr_setuserfield(chan, tmp);
475         }
476         if (waitforbridge) {
477                 /* We must remove the "b" option if listed.  In principle none of
478                    the following could give NULL results, but we check just to
479                    be pedantic. Reconstructing with checks for 'm' option does not
480                    work if we end up adding more options than 'm' in the future. */
481                 delay = ast_strdupa(data);
482                 options = strrchr(delay, '|');
483                 if (options) {
484                         arg = strchr(options, 'b');
485                         if (arg) {
486                                 *arg = 'X';
487                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
488                         }
489                 }
490                 return 0;
491         }
492
493         res = ast_monitor_start(chan, format, fname_base, 1, stream_action);
494         if (res < 0)
495                 res = ast_monitor_change_fname(chan, fname_base, 1);
496
497         if (stream_action & X_JOIN) {
498                 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
499                         joinfiles = 1;
500                 else
501                         ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
502         }
503         ast_monitor_setjoinfiles(chan, joinfiles);
504
505         return res;
506 }
507
508 /*! \brief Wrapper function \see ast_monitor_stop */
509 static int stop_monitor_exec(struct ast_channel *chan, void *data)
510 {
511         return ast_monitor_stop(chan, 1);
512 }
513
514 /*! \brief Wrapper function \see ast_monitor_change_fname */
515 static int change_monitor_exec(struct ast_channel *chan, void *data)
516 {
517         return ast_monitor_change_fname(chan, (const char*)data, 1);
518 }
519
520 static char start_monitor_action_help[] =
521 "Description: The 'Monitor' action may be used to record the audio on a\n"
522 "  specified channel.  The following parameters may be used to control\n"
523 "  this:\n"
524 "  Channel     - Required.  Used to specify the channel to record.\n"
525 "  File        - Optional.  Is the name of the file created in the\n"
526 "                monitor spool directory.  Defaults to the same name\n"
527 "                as the channel (with slashes replaced with dashes).\n"
528 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
529 "                to \"wav\".\n"
530 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
531 "                the input and output channels together after the\n"
532 "                recording is finished.\n";
533
534 /*! \brief Start monitoring a channel by manager connection */
535 static int start_monitor_action(struct mansession *s, const struct message *m)
536 {
537         struct ast_channel *c = NULL;
538         const char *name = astman_get_header(m, "Channel");
539         const char *fname = astman_get_header(m, "File");
540         const char *format = astman_get_header(m, "Format");
541         const char *mix = astman_get_header(m, "Mix");
542         char *d;
543
544         if (ast_strlen_zero(name)) {
545                 astman_send_error(s, m, "No channel specified");
546                 return 0;
547         }
548         c = ast_get_channel_by_name_locked(name);
549         if (!c) {
550                 astman_send_error(s, m, "No such channel");
551                 return 0;
552         }
553
554         if (ast_strlen_zero(fname)) {
555                 /* No filename base specified, default to channel name as per CLI */            
556                 if (!(fname = ast_strdup(c->name))) {
557                         astman_send_error(s, m, "Could not start monitoring channel");
558                         ast_channel_unlock(c);
559                         return 0;
560                 }
561                 /* Channels have the format technology/channel_name - have to replace that /  */
562                 if ((d = strchr(fname, '/'))) 
563                         *d = '-';
564         }
565
566         if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
567                 if (ast_monitor_change_fname(c, fname, 1)) {
568                         astman_send_error(s, m, "Could not start monitoring channel");
569                         ast_channel_unlock(c);
570                         return 0;
571                 }
572         }
573
574         if (ast_true(mix)) {
575                 ast_monitor_setjoinfiles(c, 1);
576         }
577
578         ast_channel_unlock(c);
579         astman_send_ack(s, m, "Started monitoring channel");
580         return 0;
581 }
582
583 static char stop_monitor_action_help[] =
584 "Description: The 'StopMonitor' action may be used to end a previously\n"
585 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
586 "  of the channel monitored.\n";
587
588 /*! \brief Stop monitoring a channel by manager connection */
589 static int stop_monitor_action(struct mansession *s, const struct message *m)
590 {
591         struct ast_channel *c = NULL;
592         const char *name = astman_get_header(m, "Channel");
593         int res;
594         if (ast_strlen_zero(name)) {
595                 astman_send_error(s, m, "No channel specified");
596                 return 0;
597         }
598         c = ast_get_channel_by_name_locked(name);
599         if (!c) {
600                 astman_send_error(s, m, "No such channel");
601                 return 0;
602         }
603         res = ast_monitor_stop(c, 1);
604         ast_channel_unlock(c);
605         if (res) {
606                 astman_send_error(s, m, "Could not stop monitoring channel");
607                 return 0;
608         }
609         astman_send_ack(s, m, "Stopped monitoring channel");
610         return 0;
611 }
612
613 static char change_monitor_action_help[] =
614 "Description: The 'ChangeMonitor' action may be used to change the file\n"
615 "  started by a previous 'Monitor' action.  The following parameters may\n"
616 "  be used to control this:\n"
617 "  Channel     - Required.  Used to specify the channel to record.\n"
618 "  File        - Required.  Is the new name of the file created in the\n"
619 "                monitor spool directory.\n";
620
621 /*! \brief Change filename of a monitored channel by manager connection */
622 static int change_monitor_action(struct mansession *s, const struct message *m)
623 {
624         struct ast_channel *c = NULL;
625         const char *name = astman_get_header(m, "Channel");
626         const char *fname = astman_get_header(m, "File");
627         if (ast_strlen_zero(name)) {
628                 astman_send_error(s, m, "No channel specified");
629                 return 0;
630         }
631         if (ast_strlen_zero(fname)) {
632                 astman_send_error(s, m, "No filename specified");
633                 return 0;
634         }
635         c = ast_get_channel_by_name_locked(name);
636         if (!c) {
637                 astman_send_error(s, m, "No such channel");
638                 return 0;
639         }
640         if (ast_monitor_change_fname(c, fname, 1)) {
641                 astman_send_error(s, m, "Could not change monitored filename of channel");
642                 ast_channel_unlock(c);
643                 return 0;
644         }
645         ast_channel_unlock(c);
646         astman_send_ack(s, m, "Changed monitor filename");
647         return 0;
648 }
649
650 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
651 {
652         if (chan->monitor)
653                 chan->monitor->joinfiles = turnon;
654 }
655
656 enum MONITOR_PAUSING_ACTION
657 {
658         MONITOR_ACTION_PAUSE,
659         MONITOR_ACTION_UNPAUSE
660 };
661           
662 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
663 {
664         struct ast_channel *c = NULL;
665         const char *name = astman_get_header(m, "Channel");
666         
667         if (ast_strlen_zero(name)) {
668                 astman_send_error(s, m, "No channel specified");
669                 return -1;
670         }
671         
672         c = ast_get_channel_by_name_locked(name);
673         if (!c) {
674                 astman_send_error(s, m, "No such channel");
675                 return -1;
676         }
677
678         if (action == MONITOR_ACTION_PAUSE)
679                 ast_monitor_pause(c);
680         else
681                 ast_monitor_unpause(c);
682         
683         ast_channel_unlock(c);
684         astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
685         return 0;       
686 }
687
688 static char pause_monitor_action_help[] =
689         "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
690         " recording of a channel.  The following parameters may\n"
691         " be used to control this:\n"
692         "  Channel     - Required.  Used to specify the channel to record.\n";
693
694 static int pause_monitor_action(struct mansession *s, const struct message *m)
695 {
696         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
697 }
698
699 static char unpause_monitor_action_help[] =
700         "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
701         "  of a channel after calling PauseMonitor.  The following parameters may\n"
702         "  be used to control this:\n"
703         "  Channel     - Required.  Used to specify the channel to record.\n";
704
705 static int unpause_monitor_action(struct mansession *s, const struct message *m)
706 {
707         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
708 }
709         
710
711 static int load_module(void)
712 {
713         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
714         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
715         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
716         ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
717         ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
718         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
719         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
720         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
721         ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
722         ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
723
724         return 0;
725 }
726
727 static int unload_module(void)
728 {
729         ast_unregister_application("Monitor");
730         ast_unregister_application("StopMonitor");
731         ast_unregister_application("ChangeMonitor");
732         ast_unregister_application("PauseMonitor");
733         ast_unregister_application("UnpauseMonitor");
734         ast_manager_unregister("Monitor");
735         ast_manager_unregister("StopMonitor");
736         ast_manager_unregister("ChangeMonitor");
737         ast_manager_unregister("PauseMonitor");
738         ast_manager_unregister("UnpauseMonitor");
739
740         return 0;
741 }
742
743 /* usecount semantics need to be defined */
744 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
745                 .load = load_module,
746                 .unload = unload_module,
747                 );