Merged revisions 258775 via svnmerge from
[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                 pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
513         }
514         pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
515
516         UNLOCK_IF_NEEDED(chan, need_lock);
517
518         return 0;
519 }
520
521
522 /*! \brief Pause monitoring of channel */
523 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
524 {
525         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
526 }
527
528 /*! \brief Unpause monitoring of channel */
529 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
530 {
531         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
532 }
533
534 /*! \brief Wrapper for ast_monitor_pause */
535 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
536 {
537         return ast_monitor_pause(chan);
538 }
539
540 /*! \brief Wrapper for ast_monitor_unpause */
541 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
542 {
543         return ast_monitor_unpause(chan);
544 }
545
546 /*! 
547  * \brief Change monitored filename of channel 
548  * \param chan
549  * \param fname_base new filename
550  * \param need_lock
551  * \retval 0 on success.
552  * \retval -1 on failure.
553 */
554 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
555 {
556         if (ast_strlen_zero(fname_base)) {
557                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
558                 return -1;
559         }
560
561         LOCK_IF_NEEDED(chan, need_lock);
562
563         if (chan->monitor) {
564                 int directory = strchr(fname_base, '/') ? 1 : 0;
565                 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
566                 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
567                 char tmpstring[sizeof(chan->monitor->filename_base)] = "";
568                 int i, fd[2] = { -1, -1 }, doexit = 0;
569
570                 /* before continuing, see if we're trying to rename the file to itself... */
571                 snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
572
573                 /* try creating the directory just in case it doesn't exist */
574                 if (directory) {
575                         char *name = ast_strdupa(tmpstring);
576                         ast_mkdir(dirname(name), 0777);
577                 }
578
579                 /*!\note We cannot just compare filenames, due to symlinks, relative
580                  * paths, and other possible filesystem issues.  We could use
581                  * realpath(3), but its use is discouraged.  However, if we try to
582                  * create the same file from two different paths, the second will
583                  * fail, and so we have our notification that the filenames point to
584                  * the same path.
585                  *
586                  * Remember, also, that we're using the basename of the file (i.e.
587                  * the file without the format suffix), so it does not already exist
588                  * and we aren't interfering with the recording itself.
589                  */
590                 ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
591                 
592                 if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
593                         (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
594                         if (fd[0] < 0) {
595                                 ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
596                         } else {
597                                 ast_debug(2, "No need to rename monitor filename to itself\n");
598                         }
599                         doexit = 1;
600                 }
601
602                 /* Cleanup temporary files */
603                 for (i = 0; i < 2; i++) {
604                         if (fd[i] >= 0) {
605                                 while (close(fd[i]) < 0 && errno == EINTR);
606                         }
607                 }
608                 unlink(tmpstring);
609                 /* if previous monitor file existed in a subdirectory, the directory will not be removed */
610                 unlink(chan->monitor->filename_base);
611
612                 if (doexit) {
613                         UNLOCK_IF_NEEDED(chan, need_lock);
614                         return 0;
615                 }
616
617                 ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
618                 chan->monitor->filename_changed = 1;
619         } else {
620                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
621         }
622
623         UNLOCK_IF_NEEDED(chan, need_lock);
624
625         return 0;
626 }
627
628  
629 /*!
630  * \brief Start monitor
631  * \param chan
632  * \param data arguments passed fname|options
633  * \retval 0 on success.
634  * \retval -1 on failure.
635 */
636 static int start_monitor_exec(struct ast_channel *chan, const char *data)
637 {
638         char *arg = NULL;
639         char *options = NULL;
640         char *delay = NULL;
641         char *urlprefix = NULL;
642         char tmp[256];
643         int stream_action = X_REC_IN | X_REC_OUT;
644         int joinfiles = 0;
645         int waitforbridge = 0;
646         int res = 0;
647         char *parse;
648         AST_DECLARE_APP_ARGS(args,
649                 AST_APP_ARG(format);
650                 AST_APP_ARG(fname_base);
651                 AST_APP_ARG(options);
652         );
653         
654         /* Parse arguments. */
655         if (ast_strlen_zero(data)) {
656                 ast_log(LOG_ERROR, "Monitor requires an argument\n");
657                 return 0;
658         }
659
660         parse = ast_strdupa(data);
661         AST_STANDARD_APP_ARGS(args, parse);
662
663         if (!ast_strlen_zero(args.options)) {
664                 if (strchr(args.options, 'm'))
665                         stream_action |= X_JOIN;
666                 if (strchr(args.options, 'b'))
667                         waitforbridge = 1;
668                 if (strchr(args.options, 'i'))
669                         stream_action &= ~X_REC_IN;
670                 if (strchr(args.options, 'o'))
671                         stream_action &= ~X_REC_OUT;
672         }
673
674         arg = strchr(args.format, ':');
675         if (arg) {
676                 *arg++ = 0;
677                 urlprefix = arg;
678         }
679
680         if (urlprefix) {
681                 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
682                         ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
683                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
684                         return -1;
685                 ast_cdr_setuserfield(chan, tmp);
686         }
687         if (waitforbridge) {
688                 /* We must remove the "b" option if listed.  In principle none of
689                    the following could give NULL results, but we check just to
690                    be pedantic. Reconstructing with checks for 'm' option does not
691                    work if we end up adding more options than 'm' in the future. */
692                 delay = ast_strdupa(data);
693                 options = strrchr(delay, ',');
694                 if (options) {
695                         arg = strchr(options, 'b');
696                         if (arg) {
697                                 *arg = 'X';
698                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
699                         }
700                 }
701                 return 0;
702         }
703
704         res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
705         if (res < 0)
706                 res = ast_monitor_change_fname(chan, args.fname_base, 1);
707
708         if (stream_action & X_JOIN) {
709                 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
710                         joinfiles = 1;
711                 else
712                         ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
713         }
714         ast_monitor_setjoinfiles(chan, joinfiles);
715
716         return res;
717 }
718
719 /*! \brief Wrapper function \see ast_monitor_stop */
720 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
721 {
722         return ast_monitor_stop(chan, 1);
723 }
724
725 /*! \brief Wrapper function \see ast_monitor_change_fname */
726 static int change_monitor_exec(struct ast_channel *chan, const char *data)
727 {
728         return ast_monitor_change_fname(chan, data, 1);
729 }
730
731 /*! \brief Start monitoring a channel by manager connection */
732 static int start_monitor_action(struct mansession *s, const struct message *m)
733 {
734         struct ast_channel *c = NULL;
735         const char *name = astman_get_header(m, "Channel");
736         const char *fname = astman_get_header(m, "File");
737         const char *format = astman_get_header(m, "Format");
738         const char *mix = astman_get_header(m, "Mix");
739         char *d;
740
741         if (ast_strlen_zero(name)) {
742                 astman_send_error(s, m, "No channel specified");
743                 return 0;
744         }
745
746         if (!(c = ast_channel_get_by_name(name))) {
747                 astman_send_error(s, m, "No such channel");
748                 return 0;
749         }
750
751         if (ast_strlen_zero(fname)) {
752                 /* No filename base specified, default to channel name as per CLI */
753                 ast_channel_lock(c);
754                 fname = ast_strdupa(c->name);
755                 ast_channel_unlock(c);
756                 /* Channels have the format technology/channel_name - have to replace that /  */
757                 if ((d = strchr(fname, '/'))) {
758                         *d = '-';
759                 }
760         }
761
762         if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
763                 if (ast_monitor_change_fname(c, fname, 1)) {
764                         astman_send_error(s, m, "Could not start monitoring channel");
765                         c = ast_channel_unref(c);
766                         return 0;
767                 }
768         }
769
770         if (ast_true(mix)) {
771                 ast_channel_lock(c);
772                 ast_monitor_setjoinfiles(c, 1);
773                 ast_channel_unlock(c);
774         }
775
776         c = ast_channel_unref(c);
777
778         astman_send_ack(s, m, "Started monitoring channel");
779
780         return 0;
781 }
782
783 /*! \brief Stop monitoring a channel by manager connection */
784 static int stop_monitor_action(struct mansession *s, const struct message *m)
785 {
786         struct ast_channel *c = NULL;
787         const char *name = astman_get_header(m, "Channel");
788         int res;
789
790         if (ast_strlen_zero(name)) {
791                 astman_send_error(s, m, "No channel specified");
792                 return 0;
793         }
794
795         if (!(c = ast_channel_get_by_name(name))) {
796                 astman_send_error(s, m, "No such channel");
797                 return 0;
798         }
799
800         res = ast_monitor_stop(c, 1);
801
802         c = ast_channel_unref(c);
803
804         if (res) {
805                 astman_send_error(s, m, "Could not stop monitoring channel");
806                 return 0;
807         }
808
809         astman_send_ack(s, m, "Stopped monitoring channel");
810
811         return 0;
812 }
813
814 /*! \brief Change filename of a monitored channel by manager connection */
815 static int change_monitor_action(struct mansession *s, const struct message *m)
816 {
817         struct ast_channel *c = NULL;
818         const char *name = astman_get_header(m, "Channel");
819         const char *fname = astman_get_header(m, "File");
820
821         if (ast_strlen_zero(name)) {
822                 astman_send_error(s, m, "No channel specified");
823                 return 0;
824         }
825
826         if (ast_strlen_zero(fname)) {
827                 astman_send_error(s, m, "No filename specified");
828                 return 0;
829         }
830
831         if (!(c = ast_channel_get_by_name(name))) {
832                 astman_send_error(s, m, "No such channel");
833                 return 0;
834         }
835
836         if (ast_monitor_change_fname(c, fname, 1)) {
837                 c = ast_channel_unref(c);
838                 astman_send_error(s, m, "Could not change monitored filename of channel");
839                 return 0;
840         }
841
842         c = ast_channel_unref(c);
843
844         astman_send_ack(s, m, "Changed monitor filename");
845
846         return 0;
847 }
848
849 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
850 {
851         if (chan->monitor)
852                 chan->monitor->joinfiles = turnon;
853 }
854
855 enum MONITOR_PAUSING_ACTION
856 {
857         MONITOR_ACTION_PAUSE,
858         MONITOR_ACTION_UNPAUSE
859 };
860  
861 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
862 {
863         struct ast_channel *c = NULL;
864         const char *name = astman_get_header(m, "Channel");
865
866         if (ast_strlen_zero(name)) {
867                 astman_send_error(s, m, "No channel specified");
868                 return -1;
869         }
870
871         if (!(c = ast_channel_get_by_name(name))) {
872                 astman_send_error(s, m, "No such channel");
873                 return -1;
874         }
875
876         if (action == MONITOR_ACTION_PAUSE) {
877                 ast_monitor_pause(c);
878         } else {
879                 ast_monitor_unpause(c);
880         }
881
882         c = ast_channel_unref(c);
883
884         astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
885
886         return 0;
887 }
888
889 static int pause_monitor_action(struct mansession *s, const struct message *m)
890 {
891         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
892 }
893
894 static int unpause_monitor_action(struct mansession *s, const struct message *m)
895 {
896         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
897 }
898         
899
900 static int load_module(void)
901 {
902         ast_register_application_xml("Monitor", start_monitor_exec);
903         ast_register_application_xml("StopMonitor", stop_monitor_exec);
904         ast_register_application_xml("ChangeMonitor", change_monitor_exec);
905         ast_register_application_xml("PauseMonitor", pause_monitor_exec);
906         ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
907         ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
908         ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
909         ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
910         ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
911         ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
912
913         return AST_MODULE_LOAD_SUCCESS;
914 }
915
916 static int unload_module(void)
917 {
918         ast_unregister_application("Monitor");
919         ast_unregister_application("StopMonitor");
920         ast_unregister_application("ChangeMonitor");
921         ast_unregister_application("PauseMonitor");
922         ast_unregister_application("UnpauseMonitor");
923         ast_manager_unregister("Monitor");
924         ast_manager_unregister("StopMonitor");
925         ast_manager_unregister("ChangeMonitor");
926         ast_manager_unregister("PauseMonitor");
927         ast_manager_unregister("UnpauseMonitor");
928
929         return 0;
930 }
931
932 /* usecount semantics need to be defined */
933 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
934                 .load = load_module,
935                 .unload = unload_module,
936                 );