Merged revisions 227944 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 == '/' ? "" : "/";
309                         /* try creating the directory just in case it doesn't exist */
310                         if (directory) {
311                                 char *name = ast_strdupa(fname_base);
312                                 ast_mkdir(dirname(name), 0777);
313                         }
314                         snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
315                                                 directory ? "" : ast_config_AST_MONITOR_DIR, absolute, fname_base);
316                         snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
317                                                 directory ? "" : ast_config_AST_MONITOR_DIR, absolute, fname_base);
318                         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%s",
319                                          ast_config_AST_MONITOR_DIR, fname_base);
320                 } else {
321                         ast_mutex_lock(&monitorlock);
322                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
323                                                 ast_config_AST_MONITOR_DIR, seq);
324                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
325                                                 ast_config_AST_MONITOR_DIR, seq);
326                         seq++;
327                         ast_mutex_unlock(&monitorlock);
328
329                         channel_name = ast_strdupa(chan->name);
330                         while ((p = strchr(channel_name, '/'))) {
331                                 *p = '-';
332                         }
333                         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
334                                          ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
335                         monitor->filename_changed = 1;
336                 }
337
338                 monitor->stop = ast_monitor_stop;
339
340                 /* Determine file format */
341                 if (!ast_strlen_zero(format_spec)) {
342                         monitor->format = ast_strdup(format_spec);
343                 } else {
344                         monitor->format = ast_strdup("wav");
345                 }
346                 
347                 /* open files */
348                 if (stream_action & X_REC_IN) {
349                         if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
350                                 ast_filedelete(monitor->read_filename, NULL);
351                         if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
352                                                         monitor->format, NULL,
353                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
354                                 ast_log(LOG_WARNING, "Could not create file %s\n",
355                                                         monitor->read_filename);
356                                 ast_free(monitor);
357                                 UNLOCK_IF_NEEDED(chan, need_lock);
358                                 return -1;
359                         }
360                 } else
361                         monitor->read_stream = NULL;
362
363                 if (stream_action & X_REC_OUT) {
364                         if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
365                                 ast_filedelete(monitor->write_filename, NULL);
366                         }
367                         if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
368                                                         monitor->format, NULL,
369                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
370                                 ast_log(LOG_WARNING, "Could not create file %s\n",
371                                                         monitor->write_filename);
372                                 ast_closestream(monitor->read_stream);
373                                 ast_free(monitor);
374                                 UNLOCK_IF_NEEDED(chan, need_lock);
375                                 return -1;
376                         }
377                 } else
378                         monitor->write_stream = NULL;
379
380                 chan->monitor = monitor;
381                 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
382                 /* so we know this call has been monitored in case we need to bill for it or something */
383                 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
384
385                 manager_event(EVENT_FLAG_CALL, "MonitorStart",
386                                 "Channel: %s\r\n"
387                                 "Uniqueid: %s\r\n",                        
388                                 chan->name,
389                                 chan->uniqueid                        
390                                 );
391         } else {
392                 ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
393                 res = -1;
394         }
395
396         UNLOCK_IF_NEEDED(chan, need_lock);
397
398         return res;
399 }
400
401 /*!
402  * \brief Get audio format.
403  * \param format recording format.
404  * The file format extensions that Asterisk uses are not all the same as that
405  * which soxmix expects.  This function ensures that the format used as the
406  * extension on the filename is something soxmix will understand.
407  */
408 static const char *get_soxmix_format(const char *format)
409 {
410         const char *res = format;
411
412         if (!strcasecmp(format,"ulaw"))
413                 res = "ul";
414         if (!strcasecmp(format,"alaw"))
415                 res = "al";
416         
417         return res;
418 }
419
420 /*! 
421  * \brief Stop monitoring channel 
422  * \param chan 
423  * \param need_lock
424  * Stop the recording, close any open streams, mix in/out channels if required
425  * \return Always 0
426 */
427 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
428 {
429         int delfiles = 0;
430
431         LOCK_IF_NEEDED(chan, need_lock);
432
433         if (chan->monitor) {
434                 char filename[ FILENAME_MAX ];
435
436                 if (chan->monitor->read_stream) {
437                         ast_closestream(chan->monitor->read_stream);
438                 }
439                 if (chan->monitor->write_stream) {
440                         ast_closestream(chan->monitor->write_stream);
441                 }
442
443                 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
444                         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
445                                 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
446                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
447                                         ast_filedelete(filename, NULL);
448                                 }
449                                 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
450                         } else {
451                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
452                         }
453
454                         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
455                                 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
456                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
457                                         ast_filedelete(filename, NULL);
458                                 }
459                                 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
460                         } else {
461                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
462                         }
463                 }
464
465                 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
466                         char tmp[1024];
467                         char tmp2[1024];
468                         const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
469                         char *name = chan->monitor->filename_base;
470                         int directory = strchr(name, '/') ? 1 : 0;
471                         const char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
472                         const char *execute, *execute_args;
473                         const char *absolute = *name == '/' ? "" : "/";
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%s%s-in.%s\" \"%s%s%s-out.%s\" \"%s%s%s.%s\" %s &", execute, dir, absolute, name, format, dir, absolute, name, format, dir, absolute, name, format,execute_args);
492                         if (delfiles) {
493                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s%s%s-\"* ) &",tmp, dir, absolute, name); /* remove legs when done mixing */
494                                 ast_copy_string(tmp, tmp2, sizeof(tmp));
495                         }
496                         ast_debug(1,"monitor executing %s\n",tmp);
497                         if (ast_safe_system(tmp) == -1)
498                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
499                 }
500                 
501                 ast_free(chan->monitor->format);
502                 ast_free(chan->monitor);
503                 chan->monitor = NULL;
504
505                 manager_event(EVENT_FLAG_CALL, "MonitorStop",
506                                 "Channel: %s\r\n"
507                                 "Uniqueid: %s\r\n",
508                                 chan->name,
509                                 chan->uniqueid
510                                 );
511         }
512
513         UNLOCK_IF_NEEDED(chan, need_lock);
514
515         return 0;
516 }
517
518
519 /*! \brief Pause monitoring of channel */
520 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
521 {
522         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
523 }
524
525 /*! \brief Unpause monitoring of channel */
526 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
527 {
528         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
529 }
530
531 /*! \brief Wrapper for ast_monitor_pause */
532 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
533 {
534         return ast_monitor_pause(chan);
535 }
536
537 /*! \brief Wrapper for ast_monitor_unpause */
538 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
539 {
540         return ast_monitor_unpause(chan);
541 }
542
543 /*! 
544  * \brief Change monitored filename of channel 
545  * \param chan
546  * \param fname_base new filename
547  * \param need_lock
548  * \retval 0 on success.
549  * \retval -1 on failure.
550 */
551 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
552 {
553         if (ast_strlen_zero(fname_base)) {
554                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
555                 return -1;
556         }
557
558         LOCK_IF_NEEDED(chan, need_lock);
559
560         if (chan->monitor) {
561                 int directory = strchr(fname_base, '/') ? 1 : 0;
562                 const char *absolute = *fname_base == '/' ? "" : "/";
563                 char tmpstring[sizeof(chan->monitor->filename_base)] = "";
564                 int i, fd[2] = { -1, -1 }, doexit = 0;
565
566                 /* before continuing, see if we're trying to rename the file to itself... */
567                 snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", directory ? "" : ast_config_AST_MONITOR_DIR, absolute, fname_base);
568
569                 /*!\note We cannot just compare filenames, due to symlinks, relative
570                  * paths, and other possible filesystem issues.  We could use
571                  * realpath(3), but its use is discouraged.  However, if we try to
572                  * create the same file from two different paths, the second will
573                  * fail, and so we have our notification that the filenames point to
574                  * the same path.
575                  *
576                  * Remember, also, that we're using the basename of the file (i.e.
577                  * the file without the format suffix), so it does not already exist
578                  * and we aren't interfering with the recording itself.
579                  */
580                 ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
581                 
582                 if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
583                         (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
584                         if (fd[0] < 0) {
585                                 ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
586                         } else {
587                                 ast_debug(2, "No need to rename monitor filename to itself\n");
588                         }
589                         doexit = 1;
590                 }
591
592                 /* Cleanup temporary files */
593                 for (i = 0; i < 2; i++) {
594                         if (fd[i] >= 0) {
595                                 while (close(fd[i]) < 0 && errno == EINTR);
596                         }
597                 }
598                 unlink(tmpstring);
599                 unlink(chan->monitor->filename_base);
600
601                 if (doexit) {
602                         UNLOCK_IF_NEEDED(chan, need_lock);
603                         return 0;
604                 }
605
606                 /* try creating the directory just in case it doesn't exist */
607                 if (directory) {
608                         char *name = ast_strdupa(fname_base);
609                         ast_mkdir(dirname(name), 0777);
610                 }
611
612                 ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
613                 chan->monitor->filename_changed = 1;
614         } else {
615                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
616         }
617
618         UNLOCK_IF_NEEDED(chan, need_lock);
619
620         return 0;
621 }
622
623  
624 /*!
625  * \brief Start monitor
626  * \param chan
627  * \param data arguments passed fname|options
628  * \retval 0 on success.
629  * \retval -1 on failure.
630 */
631 static int start_monitor_exec(struct ast_channel *chan, const char *data)
632 {
633         char *arg = NULL;
634         char *options = NULL;
635         char *delay = NULL;
636         char *urlprefix = NULL;
637         char tmp[256];
638         int stream_action = X_REC_IN | X_REC_OUT;
639         int joinfiles = 0;
640         int waitforbridge = 0;
641         int res = 0;
642         char *parse;
643         AST_DECLARE_APP_ARGS(args,
644                 AST_APP_ARG(format);
645                 AST_APP_ARG(fname_base);
646                 AST_APP_ARG(options);
647         );
648         
649         /* Parse arguments. */
650         if (ast_strlen_zero(data)) {
651                 ast_log(LOG_ERROR, "Monitor requires an argument\n");
652                 return 0;
653         }
654
655         parse = ast_strdupa(data);
656         AST_STANDARD_APP_ARGS(args, parse);
657
658         if (!ast_strlen_zero(args.options)) {
659                 if (strchr(args.options, 'm'))
660                         stream_action |= X_JOIN;
661                 if (strchr(args.options, 'b'))
662                         waitforbridge = 1;
663                 if (strchr(args.options, 'i'))
664                         stream_action &= ~X_REC_IN;
665                 if (strchr(args.options, 'o'))
666                         stream_action &= ~X_REC_OUT;
667         }
668
669         arg = strchr(args.format, ':');
670         if (arg) {
671                 *arg++ = 0;
672                 urlprefix = arg;
673         }
674
675         if (urlprefix) {
676                 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
677                         ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
678                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
679                         return -1;
680                 ast_cdr_setuserfield(chan, tmp);
681         }
682         if (waitforbridge) {
683                 /* We must remove the "b" option if listed.  In principle none of
684                    the following could give NULL results, but we check just to
685                    be pedantic. Reconstructing with checks for 'm' option does not
686                    work if we end up adding more options than 'm' in the future. */
687                 delay = ast_strdupa(data);
688                 options = strrchr(delay, ',');
689                 if (options) {
690                         arg = strchr(options, 'b');
691                         if (arg) {
692                                 *arg = 'X';
693                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
694                         }
695                 }
696                 return 0;
697         }
698
699         res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
700         if (res < 0)
701                 res = ast_monitor_change_fname(chan, args.fname_base, 1);
702
703         if (stream_action & X_JOIN) {
704                 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
705                         joinfiles = 1;
706                 else
707                         ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
708         }
709         ast_monitor_setjoinfiles(chan, joinfiles);
710
711         return res;
712 }
713
714 /*! \brief Wrapper function \see ast_monitor_stop */
715 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
716 {
717         return ast_monitor_stop(chan, 1);
718 }
719
720 /*! \brief Wrapper function \see ast_monitor_change_fname */
721 static int change_monitor_exec(struct ast_channel *chan, const char *data)
722 {
723         return ast_monitor_change_fname(chan, data, 1);
724 }
725
726 /*! \brief Start monitoring a channel by manager connection */
727 static int start_monitor_action(struct mansession *s, const struct message *m)
728 {
729         struct ast_channel *c = NULL;
730         const char *name = astman_get_header(m, "Channel");
731         const char *fname = astman_get_header(m, "File");
732         const char *format = astman_get_header(m, "Format");
733         const char *mix = astman_get_header(m, "Mix");
734         char *d;
735
736         if (ast_strlen_zero(name)) {
737                 astman_send_error(s, m, "No channel specified");
738                 return 0;
739         }
740
741         if (!(c = ast_channel_get_by_name(name))) {
742                 astman_send_error(s, m, "No such channel");
743                 return 0;
744         }
745
746         if (ast_strlen_zero(fname)) {
747                 /* No filename base specified, default to channel name as per CLI */
748                 ast_channel_lock(c);
749                 fname = ast_strdupa(c->name);
750                 ast_channel_unlock(c);
751                 /* Channels have the format technology/channel_name - have to replace that /  */
752                 if ((d = strchr(fname, '/'))) {
753                         *d = '-';
754                 }
755         }
756
757         if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
758                 if (ast_monitor_change_fname(c, fname, 1)) {
759                         astman_send_error(s, m, "Could not start monitoring channel");
760                         c = ast_channel_unref(c);
761                         return 0;
762                 }
763         }
764
765         if (ast_true(mix)) {
766                 ast_channel_lock(c);
767                 ast_monitor_setjoinfiles(c, 1);
768                 ast_channel_unlock(c);
769         }
770
771         c = ast_channel_unref(c);
772
773         astman_send_ack(s, m, "Started monitoring channel");
774
775         return 0;
776 }
777
778 /*! \brief Stop monitoring a channel by manager connection */
779 static int stop_monitor_action(struct mansession *s, const struct message *m)
780 {
781         struct ast_channel *c = NULL;
782         const char *name = astman_get_header(m, "Channel");
783         int res;
784
785         if (ast_strlen_zero(name)) {
786                 astman_send_error(s, m, "No channel specified");
787                 return 0;
788         }
789
790         if (!(c = ast_channel_get_by_name(name))) {
791                 astman_send_error(s, m, "No such channel");
792                 return 0;
793         }
794
795         res = ast_monitor_stop(c, 1);
796
797         c = ast_channel_unref(c);
798
799         if (res) {
800                 astman_send_error(s, m, "Could not stop monitoring channel");
801                 return 0;
802         }
803
804         astman_send_ack(s, m, "Stopped monitoring channel");
805
806         return 0;
807 }
808
809 /*! \brief Change filename of a monitored channel by manager connection */
810 static int change_monitor_action(struct mansession *s, const struct message *m)
811 {
812         struct ast_channel *c = NULL;
813         const char *name = astman_get_header(m, "Channel");
814         const char *fname = astman_get_header(m, "File");
815
816         if (ast_strlen_zero(name)) {
817                 astman_send_error(s, m, "No channel specified");
818                 return 0;
819         }
820
821         if (ast_strlen_zero(fname)) {
822                 astman_send_error(s, m, "No filename specified");
823                 return 0;
824         }
825
826         if (!(c = ast_channel_get_by_name(name))) {
827                 astman_send_error(s, m, "No such channel");
828                 return 0;
829         }
830
831         if (ast_monitor_change_fname(c, fname, 1)) {
832                 c = ast_channel_unref(c);
833                 astman_send_error(s, m, "Could not change monitored filename of channel");
834                 return 0;
835         }
836
837         c = ast_channel_unref(c);
838
839         astman_send_ack(s, m, "Changed monitor filename");
840
841         return 0;
842 }
843
844 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
845 {
846         if (chan->monitor)
847                 chan->monitor->joinfiles = turnon;
848 }
849
850 enum MONITOR_PAUSING_ACTION
851 {
852         MONITOR_ACTION_PAUSE,
853         MONITOR_ACTION_UNPAUSE
854 };
855  
856 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
857 {
858         struct ast_channel *c = NULL;
859         const char *name = astman_get_header(m, "Channel");
860
861         if (ast_strlen_zero(name)) {
862                 astman_send_error(s, m, "No channel specified");
863                 return -1;
864         }
865
866         if (!(c = ast_channel_get_by_name(name))) {
867                 astman_send_error(s, m, "No such channel");
868                 return -1;
869         }
870
871         if (action == MONITOR_ACTION_PAUSE) {
872                 ast_monitor_pause(c);
873         } else {
874                 ast_monitor_unpause(c);
875         }
876
877         c = ast_channel_unref(c);
878
879         astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
880
881         return 0;
882 }
883
884 static int pause_monitor_action(struct mansession *s, const struct message *m)
885 {
886         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
887 }
888
889 static int unpause_monitor_action(struct mansession *s, const struct message *m)
890 {
891         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
892 }
893         
894
895 static int load_module(void)
896 {
897         ast_register_application_xml("Monitor", start_monitor_exec);
898         ast_register_application_xml("StopMonitor", stop_monitor_exec);
899         ast_register_application_xml("ChangeMonitor", change_monitor_exec);
900         ast_register_application_xml("PauseMonitor", pause_monitor_exec);
901         ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
902         ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
903         ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
904         ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
905         ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
906         ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
907
908         return AST_MODULE_LOAD_SUCCESS;
909 }
910
911 static int unload_module(void)
912 {
913         ast_unregister_application("Monitor");
914         ast_unregister_application("StopMonitor");
915         ast_unregister_application("ChangeMonitor");
916         ast_unregister_application("PauseMonitor");
917         ast_unregister_application("UnpauseMonitor");
918         ast_manager_unregister("Monitor");
919         ast_manager_unregister("StopMonitor");
920         ast_manager_unregister("ChangeMonitor");
921         ast_manager_unregister("PauseMonitor");
922         ast_manager_unregister("UnpauseMonitor");
923
924         return 0;
925 }
926
927 /* usecount semantics need to be defined */
928 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
929                 .load = load_module,
930                 .unload = unload_module,
931                 );