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