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