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