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