Merged revisions 80166 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 *options = NULL;
432         char *delay = NULL;
433         char *urlprefix = NULL;
434         char tmp[256];
435         int stream_action = X_REC_IN | X_REC_OUT;
436         int joinfiles = 0;
437         int waitforbridge = 0;
438         int res = 0;
439         char *parse;
440         AST_DECLARE_APP_ARGS(args,
441                 AST_APP_ARG(format);
442                 AST_APP_ARG(fname_base);
443                 AST_APP_ARG(options);
444         );
445         
446         /* Parse arguments. */
447         if (ast_strlen_zero((char*)data)) {
448                 ast_log(LOG_ERROR, "Monitor requires an argument\n");
449                 return 0;
450         }
451
452         parse = ast_strdupa((char*)data);
453         AST_STANDARD_APP_ARGS(args, parse);
454
455         if (strchr(args.options, 'm'))
456                 stream_action |= X_JOIN;
457         if (strchr(args.options, 'b'))
458                 waitforbridge = 1;
459         if (strchr(args.options, 'i'))
460                 stream_action &= ~X_REC_IN;
461         if (strchr(args.options, 'o'))
462                 stream_action &= ~X_REC_OUT;
463
464         arg = strchr(args.format, ':');
465         if (arg) {
466                 *arg++ = 0;
467                 urlprefix = arg;
468         }
469
470         if (urlprefix) {
471                 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
472                         ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
473                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
474                         return -1;
475                 ast_cdr_setuserfield(chan, tmp);
476         }
477         if (waitforbridge) {
478                 /* We must remove the "b" option if listed.  In principle none of
479                    the following could give NULL results, but we check just to
480                    be pedantic. Reconstructing with checks for 'm' option does not
481                    work if we end up adding more options than 'm' in the future. */
482                 delay = ast_strdupa(data);
483                 options = strrchr(delay, '|');
484                 if (options) {
485                         arg = strchr(options, 'b');
486                         if (arg) {
487                                 *arg = 'X';
488                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
489                         }
490                 }
491                 return 0;
492         }
493
494         res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
495         if (res < 0)
496                 res = ast_monitor_change_fname(chan, args.fname_base, 1);
497
498         if (stream_action & X_JOIN) {
499                 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
500                         joinfiles = 1;
501                 else
502                         ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
503         }
504         ast_monitor_setjoinfiles(chan, joinfiles);
505
506         return res;
507 }
508
509 /*! \brief Wrapper function \see ast_monitor_stop */
510 static int stop_monitor_exec(struct ast_channel *chan, void *data)
511 {
512         return ast_monitor_stop(chan, 1);
513 }
514
515 /*! \brief Wrapper function \see ast_monitor_change_fname */
516 static int change_monitor_exec(struct ast_channel *chan, void *data)
517 {
518         return ast_monitor_change_fname(chan, (const char*)data, 1);
519 }
520
521 static char start_monitor_action_help[] =
522 "Description: The 'Monitor' action may be used to record the audio on a\n"
523 "  specified channel.  The following parameters may be used to control\n"
524 "  this:\n"
525 "  Channel     - Required.  Used to specify the channel to record.\n"
526 "  File        - Optional.  Is the name of the file created in the\n"
527 "                monitor spool directory.  Defaults to the same name\n"
528 "                as the channel (with slashes replaced with dashes).\n"
529 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
530 "                to \"wav\".\n"
531 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
532 "                the input and output channels together after the\n"
533 "                recording is finished.\n";
534
535 /*! \brief Start monitoring a channel by manager connection */
536 static int start_monitor_action(struct mansession *s, const struct message *m)
537 {
538         struct ast_channel *c = NULL;
539         const char *name = astman_get_header(m, "Channel");
540         const char *fname = astman_get_header(m, "File");
541         const char *format = astman_get_header(m, "Format");
542         const char *mix = astman_get_header(m, "Mix");
543         char *d;
544
545         if (ast_strlen_zero(name)) {
546                 astman_send_error(s, m, "No channel specified");
547                 return 0;
548         }
549         c = ast_get_channel_by_name_locked(name);
550         if (!c) {
551                 astman_send_error(s, m, "No such channel");
552                 return 0;
553         }
554
555         if (ast_strlen_zero(fname)) {
556                 /* No filename base specified, default to channel name as per CLI */            
557                 if (!(fname = ast_strdup(c->name))) {
558                         astman_send_error(s, m, "Could not start monitoring channel");
559                         ast_channel_unlock(c);
560                         return 0;
561                 }
562                 /* Channels have the format technology/channel_name - have to replace that /  */
563                 if ((d = strchr(fname, '/'))) 
564                         *d = '-';
565         }
566
567         if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
568                 if (ast_monitor_change_fname(c, fname, 1)) {
569                         astman_send_error(s, m, "Could not start monitoring channel");
570                         ast_channel_unlock(c);
571                         return 0;
572                 }
573         }
574
575         if (ast_true(mix)) {
576                 ast_monitor_setjoinfiles(c, 1);
577         }
578
579         ast_channel_unlock(c);
580         astman_send_ack(s, m, "Started monitoring channel");
581         return 0;
582 }
583
584 static char stop_monitor_action_help[] =
585 "Description: The 'StopMonitor' action may be used to end a previously\n"
586 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
587 "  of the channel monitored.\n";
588
589 /*! \brief Stop monitoring a channel by manager connection */
590 static int stop_monitor_action(struct mansession *s, const struct message *m)
591 {
592         struct ast_channel *c = NULL;
593         const char *name = astman_get_header(m, "Channel");
594         int res;
595         if (ast_strlen_zero(name)) {
596                 astman_send_error(s, m, "No channel specified");
597                 return 0;
598         }
599         c = ast_get_channel_by_name_locked(name);
600         if (!c) {
601                 astman_send_error(s, m, "No such channel");
602                 return 0;
603         }
604         res = ast_monitor_stop(c, 1);
605         ast_channel_unlock(c);
606         if (res) {
607                 astman_send_error(s, m, "Could not stop monitoring channel");
608                 return 0;
609         }
610         astman_send_ack(s, m, "Stopped monitoring channel");
611         return 0;
612 }
613
614 static char change_monitor_action_help[] =
615 "Description: The 'ChangeMonitor' action may be used to change the file\n"
616 "  started by a previous 'Monitor' action.  The following parameters may\n"
617 "  be used to control this:\n"
618 "  Channel     - Required.  Used to specify the channel to record.\n"
619 "  File        - Required.  Is the new name of the file created in the\n"
620 "                monitor spool directory.\n";
621
622 /*! \brief Change filename of a monitored channel by manager connection */
623 static int change_monitor_action(struct mansession *s, const struct message *m)
624 {
625         struct ast_channel *c = NULL;
626         const char *name = astman_get_header(m, "Channel");
627         const char *fname = astman_get_header(m, "File");
628         if (ast_strlen_zero(name)) {
629                 astman_send_error(s, m, "No channel specified");
630                 return 0;
631         }
632         if (ast_strlen_zero(fname)) {
633                 astman_send_error(s, m, "No filename specified");
634                 return 0;
635         }
636         c = ast_get_channel_by_name_locked(name);
637         if (!c) {
638                 astman_send_error(s, m, "No such channel");
639                 return 0;
640         }
641         if (ast_monitor_change_fname(c, fname, 1)) {
642                 astman_send_error(s, m, "Could not change monitored filename of channel");
643                 ast_channel_unlock(c);
644                 return 0;
645         }
646         ast_channel_unlock(c);
647         astman_send_ack(s, m, "Changed monitor filename");
648         return 0;
649 }
650
651 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
652 {
653         if (chan->monitor)
654                 chan->monitor->joinfiles = turnon;
655 }
656
657 enum MONITOR_PAUSING_ACTION
658 {
659         MONITOR_ACTION_PAUSE,
660         MONITOR_ACTION_UNPAUSE
661 };
662           
663 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
664 {
665         struct ast_channel *c = NULL;
666         const char *name = astman_get_header(m, "Channel");
667         
668         if (ast_strlen_zero(name)) {
669                 astman_send_error(s, m, "No channel specified");
670                 return -1;
671         }
672         
673         c = ast_get_channel_by_name_locked(name);
674         if (!c) {
675                 astman_send_error(s, m, "No such channel");
676                 return -1;
677         }
678
679         if (action == MONITOR_ACTION_PAUSE)
680                 ast_monitor_pause(c);
681         else
682                 ast_monitor_unpause(c);
683         
684         ast_channel_unlock(c);
685         astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
686         return 0;       
687 }
688
689 static char pause_monitor_action_help[] =
690         "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
691         " recording of a channel.  The following parameters may\n"
692         " be used to control this:\n"
693         "  Channel     - Required.  Used to specify the channel to record.\n";
694
695 static int pause_monitor_action(struct mansession *s, const struct message *m)
696 {
697         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
698 }
699
700 static char unpause_monitor_action_help[] =
701         "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
702         "  of a channel after calling PauseMonitor.  The following parameters may\n"
703         "  be used to control this:\n"
704         "  Channel     - Required.  Used to specify the channel to record.\n";
705
706 static int unpause_monitor_action(struct mansession *s, const struct message *m)
707 {
708         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
709 }
710         
711
712 static int load_module(void)
713 {
714         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
715         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
716         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
717         ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
718         ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
719         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
720         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
721         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
722         ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
723         ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
724
725         return 0;
726 }
727
728 static int unload_module(void)
729 {
730         ast_unregister_application("Monitor");
731         ast_unregister_application("StopMonitor");
732         ast_unregister_application("ChangeMonitor");
733         ast_unregister_application("PauseMonitor");
734         ast_unregister_application("UnpauseMonitor");
735         ast_manager_unregister("Monitor");
736         ast_manager_unregister("StopMonitor");
737         ast_manager_unregister("ChangeMonitor");
738         ast_manager_unregister("PauseMonitor");
739         ast_manager_unregister("UnpauseMonitor");
740
741         return 0;
742 }
743
744 /* usecount semantics need to be defined */
745 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
746                 .load = load_module,
747                 .unload = unload_module,
748                 );