Fix dialplan function NULL channel safety issues
[asterisk/asterisk.git] / res / res_mutestream.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Olle E. Johansson
5  *
6  * Olle E. Johansson <oej@edvina.net>
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 MUTESTREAM audiohooks
22  *
23  * \author Olle E. Johansson <oej@edvina.net>
24  *
25  *  \ingroup functions
26  *
27  * \note This module only handles audio streams today, but can easily be appended to also
28  * zero out text streams if there's an application for it.
29  * When we know and understands what happens if we zero out video, we can do that too.
30  */
31
32 /*** MODULEINFO
33         <support_level>core</support_level>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include "asterisk/options.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/module.h"
44 #include "asterisk/config.h"
45 #include "asterisk/file.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/frame.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/audiohook.h"
50 #include "asterisk/manager.h"
51
52 /*** DOCUMENTATION
53         <function name="MUTEAUDIO" language="en_US">
54                 <synopsis>
55                         Muting audio streams in the channel
56                 </synopsis>
57                 <syntax>
58                         <parameter name="direction" required="true">
59                                 <para>Must be one of </para>
60                                 <enumlist>
61                                         <enum name="in">
62                                                 <para>Inbound stream (to the PBX)</para>
63                                         </enum>
64                                         <enum name="out">
65                                                 <para>Outbound stream (from the PBX)</para>
66                                         </enum>
67                                         <enum name="all">
68                                                 <para>Both streams</para>
69                                         </enum>
70                                 </enumlist>
71                         </parameter>
72                 </syntax>
73                 <description>
74                         <para>The MUTEAUDIO function can be used to mute inbound (to the PBX) or outbound audio in a call.
75                         </para>
76                         <para>Examples:
77                         </para>
78                         <para>
79                         MUTEAUDIO(in)=on
80                         </para>
81                         <para>
82                         MUTEAUDIO(in)=off
83                         </para>
84                 </description>
85         </function>
86         <manager name="MuteAudio" language="en_US">
87                 <synopsis>
88                         Mute an audio stream.
89                 </synopsis>
90                 <syntax>
91                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
92                         <parameter name="Channel" required="true">
93                                 <para>The channel you want to mute.</para>
94                         </parameter>
95                         <parameter name="Direction" required="true">
96                                 <enumlist>
97                                         <enum name="in">
98                                                 <para>Set muting on inbound audio stream. (to the PBX)</para>
99                                         </enum>
100                                         <enum name="out">
101                                                 <para>Set muting on outbound audio stream. (from the PBX)</para>
102                                         </enum>
103                                         <enum name="all">
104                                                 <para>Set muting on inbound and outbound audio streams.</para>
105                                         </enum>
106                                 </enumlist>
107                         </parameter>
108                         <parameter name="State" required="true">
109                                 <enumlist>
110                                         <enum name="on">
111                                                 <para>Turn muting on.</para>
112                                         </enum>
113                                         <enum name="off">
114                                                 <para>Turn muting off.</para>
115                                         </enum>
116                                 </enumlist>
117                         </parameter>
118                 </syntax>
119                 <description>
120                         <para>Mute an incoming or outgoing audio stream on a channel.</para>
121                 </description>
122         </manager>
123  ***/
124
125
126 static int mute_channel(struct ast_channel *chan, const char *direction, int mute)
127 {
128         unsigned int mute_direction = 0;
129         enum ast_frame_type frametype = AST_FRAME_VOICE;
130         int ret = 0;
131
132         if (!strcmp(direction, "in")) {
133                 mute_direction = AST_MUTE_DIRECTION_READ;
134         } else if (!strcmp(direction, "out")) {
135                 mute_direction = AST_MUTE_DIRECTION_WRITE;
136         } else if (!strcmp(direction, "all")) {
137                 mute_direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
138         } else {
139                 return -1;
140         }
141
142         ast_channel_lock(chan);
143
144         if (mute) {
145                 ret = ast_channel_suppress(chan, mute_direction, frametype);
146         } else {
147                 ret = ast_channel_unsuppress(chan, mute_direction, frametype);
148         }
149
150         ast_channel_unlock(chan);
151
152         return ret;
153 }
154
155 /*! \brief Mute dialplan function */
156 static int func_mute_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
157 {
158         if (!chan) {
159                 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
160                 return -1;
161         }
162
163         return mute_channel(chan, data, ast_true(value));
164 }
165
166 /* Function for debugging - might be useful */
167 static struct ast_custom_function mute_function = {
168         .name = "MUTEAUDIO",
169         .write = func_mute_write,
170 };
171
172 static int manager_mutestream(struct mansession *s, const struct message *m)
173 {
174         const char *channel = astman_get_header(m, "Channel");
175         const char *id = astman_get_header(m,"ActionID");
176         const char *state = astman_get_header(m,"State");
177         const char *direction = astman_get_header(m,"Direction");
178         char id_text[256];
179         struct ast_channel *c = NULL;
180
181         if (ast_strlen_zero(channel)) {
182                 astman_send_error(s, m, "Channel not specified");
183                 return 0;
184         }
185         if (ast_strlen_zero(state)) {
186                 astman_send_error(s, m, "State not specified");
187                 return 0;
188         }
189         if (ast_strlen_zero(direction)) {
190                 astman_send_error(s, m, "Direction not specified");
191                 return 0;
192         }
193         /* Ok, we have everything */
194
195         c = ast_channel_get_by_name(channel);
196         if (!c) {
197                 astman_send_error(s, m, "No such channel");
198                 return 0;
199         }
200
201         if (mute_channel(c, direction, ast_true(state))) {
202                 astman_send_error(s, m, "Failed to mute/unmute stream");
203                 ast_channel_unref(c);
204                 return 0;
205         }
206
207         ast_channel_unref(c);
208
209         if (!ast_strlen_zero(id)) {
210                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
211         } else {
212                 id_text[0] = '\0';
213         }
214         astman_append(s, "Response: Success\r\n"
215                 "%s"
216                 "\r\n", id_text);
217         return 0;
218 }
219
220
221 static int load_module(void)
222 {
223         int res;
224
225         res = ast_custom_function_register(&mute_function);
226         res |= ast_manager_register_xml("MuteAudio", EVENT_FLAG_SYSTEM, manager_mutestream);
227
228         return (res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS);
229 }
230
231 static int unload_module(void)
232 {
233         ast_custom_function_unregister(&mute_function);
234         /* Unregister AMI actions */
235         ast_manager_unregister("MuteAudio");
236
237         return 0;
238 }
239
240 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mute audio stream resources");