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