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