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