diff options
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/Voice')
3 files changed, 994 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDialplan.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDialplan.cs new file mode 100644 index 0000000..2a2b4a3 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDialplan.cs | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using log4net; | ||
29 | using System.Reflection; | ||
30 | using System.Collections; | ||
31 | |||
32 | namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice | ||
33 | { | ||
34 | public class FreeSwitchDialplan | ||
35 | { | ||
36 | private static readonly ILog m_log = | ||
37 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
38 | |||
39 | |||
40 | public Hashtable HandleDialplanRequest(Hashtable request) | ||
41 | { | ||
42 | m_log.DebugFormat("[FreeSwitchVoice] HandleDialplanRequest called with {0}",request.ToString()); | ||
43 | |||
44 | Hashtable response = new Hashtable(); | ||
45 | |||
46 | foreach(DictionaryEntry item in request) | ||
47 | { | ||
48 | m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}",item.Key, item.Value); | ||
49 | } | ||
50 | |||
51 | response["content_type"] = "text/xml"; | ||
52 | response["keepalive"] = false; | ||
53 | response["int_response_code"]=200; | ||
54 | response["str_response_string"] = @"<?xml version=""1.0"" encoding=""utf-8""?> | ||
55 | <document type=""freeswitch/xml""> | ||
56 | <section name=""dialplan""> | ||
57 | <context name=""default""> | ||
58 | |||
59 | <!-- dial via SIP uri --> | ||
60 | <extension name=""sip_uri""> | ||
61 | <condition field=""destination_number"" expression=""^sip:(.*)$""> | ||
62 | <action application=""bridge"" data=""sofia/${use_profile}/$1""/> | ||
63 | <!--<action application=""bridge"" data=""$1""/>--> | ||
64 | </condition> | ||
65 | </extension> | ||
66 | |||
67 | <extension name=""opensim_conferences""> | ||
68 | <condition field=""destination_number"" expression=""^confctl-(.*)$""> | ||
69 | <action application=""answer""/> | ||
70 | <action application=""conference"" data=""$1-${domain_name}@default""/> | ||
71 | </condition> | ||
72 | </extension> | ||
73 | |||
74 | <extension name=""avatar""> | ||
75 | <condition field=""destination_number"" expression=""^(x.*)$""> | ||
76 | <action application=""bridge"" data=""user/$1""/> | ||
77 | </condition> | ||
78 | </extension> | ||
79 | |||
80 | </context> | ||
81 | </section> | ||
82 | </document>"; | ||
83 | |||
84 | return response; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDirectory.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDirectory.cs new file mode 100644 index 0000000..9959d11 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchDirectory.cs | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using log4net; | ||
29 | using System; | ||
30 | using System.Reflection; | ||
31 | using System.Text; | ||
32 | using System.Collections; | ||
33 | |||
34 | namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice | ||
35 | { | ||
36 | public class FreeSwitchDirectory | ||
37 | { | ||
38 | private static readonly ILog m_log = | ||
39 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
40 | |||
41 | public Hashtable HandleDirectoryRequest(Hashtable request) | ||
42 | { | ||
43 | m_log.DebugFormat("[FreeSwitchDirectory] HandleDirectoryRequest called with {0}",request.ToString()); | ||
44 | |||
45 | Hashtable response = new Hashtable(); | ||
46 | |||
47 | // information in the request we might be interested in | ||
48 | |||
49 | // Request 1 sip_auth for users account | ||
50 | |||
51 | //Event-Calling-Function=sofia_reg_parse_auth | ||
52 | //Event-Calling-Line-Number=1494 | ||
53 | //action=sip_auth | ||
54 | //sip_user_agent=Vivox-SDK-2.1.3010.6151-Mac%20(Feb-11-2009/16%3A42%3A41) | ||
55 | //sip_auth_username=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) | ||
56 | //sip_auth_realm=9.20.151.43 | ||
57 | //sip_contact_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) | ||
58 | //sip_contact_host=192.168.0.3 // this shouldnt really be a local IP, investigate STUN servers | ||
59 | //sip_to_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D | ||
60 | //sip_to_host=9.20.151.43 | ||
61 | //sip_auth_method=REGISTER | ||
62 | //user=xhZuXKmRpECyr2AARJYyGgg%3D%3D | ||
63 | //domain=9.20.151.43 | ||
64 | //ip=9.167.220.137 // this is the correct IP rather than sip_contact_host above when through a vpn or NAT setup | ||
65 | |||
66 | foreach(DictionaryEntry item in request) | ||
67 | { | ||
68 | m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}",item.Key, item.Value); | ||
69 | } | ||
70 | |||
71 | string eventCallingFunction = (string) request["Event-Calling-Function"]; | ||
72 | |||
73 | |||
74 | if(eventCallingFunction=="sofia_reg_parse_auth") | ||
75 | { | ||
76 | string sipAuthMethod = (string)request["sip_auth_method"]; | ||
77 | |||
78 | if(sipAuthMethod=="REGISTER") | ||
79 | { | ||
80 | response = HandleRegister(request); | ||
81 | } | ||
82 | else if(sipAuthMethod=="INVITE") | ||
83 | { | ||
84 | response = HandleInvite(request); | ||
85 | } | ||
86 | else | ||
87 | { | ||
88 | m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); | ||
89 | response["int_response_code"]=404; | ||
90 | } | ||
91 | } | ||
92 | else if(eventCallingFunction=="switch_xml_locate_user") | ||
93 | { | ||
94 | response = HandleLocateUser(request); | ||
95 | } | ||
96 | else if(eventCallingFunction=="user_data_function") // gets called when an avatar to avatar call is made | ||
97 | { | ||
98 | response = HandleLocateUser(request); | ||
99 | } | ||
100 | else if(eventCallingFunction=="user_outgoing_channel") | ||
101 | { | ||
102 | response = HandleRegister(request); | ||
103 | } | ||
104 | else if(eventCallingFunction=="config_sofia") // happens once on freeswitch startup | ||
105 | { | ||
106 | response = HandleConfigSofia(request); | ||
107 | } | ||
108 | else if(eventCallingFunction=="switch_load_network_lists") | ||
109 | { | ||
110 | //response = HandleLoadNetworkLists(request); | ||
111 | response["int_response_code"]=404; | ||
112 | response["keepalive"] = false; | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); | ||
117 | response["int_response_code"]=404; | ||
118 | response["keepalive"] = false; | ||
119 | } | ||
120 | |||
121 | |||
122 | |||
123 | return response; | ||
124 | } | ||
125 | |||
126 | private Hashtable HandleRegister(Hashtable request) | ||
127 | { | ||
128 | m_log.Info("[FreeSwitchDirectory] HandleRegister called"); | ||
129 | |||
130 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
131 | string password = "1234"; | ||
132 | string domain = (string) request["domain"]; | ||
133 | string user = (string) request["user"]; | ||
134 | |||
135 | Hashtable response = new Hashtable(); | ||
136 | response["content_type"] = "text/xml"; | ||
137 | response["keepalive"] = false; | ||
138 | response["int_response_code"]=200; | ||
139 | response["str_response_string"] = String.Format( | ||
140 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
141 | "<document type=\"freeswitch/xml\">\r\n" + | ||
142 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
143 | "<domain name=\"{0}\">\r\n" + | ||
144 | "<user id=\"{1}\">\r\n" + | ||
145 | "<params>\r\n" + | ||
146 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
147 | "<param name=\"dial-string\" value=\"{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
148 | "</params>\r\n" + | ||
149 | "<variables>\r\n" + | ||
150 | "<variable name=\"user_context\" value=\"default\" />\r\n" + | ||
151 | "<variable name=\"presence_id\" value=\"{1}@{0}\"/>"+ | ||
152 | "</variables>\r\n" + | ||
153 | "</user>\r\n" + | ||
154 | "</domain>\r\n" + | ||
155 | "</section>\r\n" + | ||
156 | "</document>\r\n" | ||
157 | , domain , user, password); | ||
158 | |||
159 | return response; | ||
160 | |||
161 | } | ||
162 | |||
163 | private Hashtable HandleInvite(Hashtable request) | ||
164 | { | ||
165 | m_log.Info("[FreeSwitchDirectory] HandleInvite called"); | ||
166 | |||
167 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
168 | string password = "1234"; | ||
169 | string domain = (string) request["domain"]; | ||
170 | string user = (string) request["user"]; | ||
171 | string sipRequestUser = (string) request["sip_request_user"]; | ||
172 | |||
173 | Hashtable response = new Hashtable(); | ||
174 | response["content_type"] = "text/xml"; | ||
175 | response["keepalive"] = false; | ||
176 | response["int_response_code"]=200; | ||
177 | response["str_response_string"] = String.Format( | ||
178 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
179 | "<document type=\"freeswitch/xml\">\r\n" + | ||
180 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
181 | "<domain name=\"{0}\">\r\n" + | ||
182 | "<user id=\"{1}\">\r\n" + | ||
183 | "<params>\r\n" + | ||
184 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
185 | "<param name=\"dial-string\" value=\"{{presence_id=${1}@${{dialed_domain}}}}${{sofia_contact(${1}@${{dialed_domain}})}}\"/>\r\n" + | ||
186 | "</params>\r\n" + | ||
187 | "<variables>\r\n" + | ||
188 | "<variable name=\"user_context\" value=\"default\" />\r\n" + | ||
189 | "<variable name=\"presence_id\" value=\"{1}@$${{domain}}\"/>"+ | ||
190 | "</variables>\r\n" + | ||
191 | "</user>\r\n" + | ||
192 | "<user id=\"{3}\">\r\n" + | ||
193 | "<params>\r\n" + | ||
194 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
195 | "<param name=\"dial-string\" value=\"{{presence_id=${3}@${{dialed_domain}}}}${{sofia_contact(${3}@${{dialed_domain}})}}\"/>\r\n" + | ||
196 | "</params>\r\n" + | ||
197 | "<variables>\r\n" + | ||
198 | "<variable name=\"user_context\" value=\"default\" />\r\n" + | ||
199 | "<variable name=\"presence_id\" value=\"{3}@$${{domain}}\"/>"+ | ||
200 | "</variables>\r\n" + | ||
201 | "</user>\r\n" + | ||
202 | "</domain>\r\n" + | ||
203 | "</section>\r\n" + | ||
204 | "</document>\r\n" | ||
205 | , domain , user, password,sipRequestUser); | ||
206 | |||
207 | return response; | ||
208 | } | ||
209 | |||
210 | |||
211 | private Hashtable HandleLocateUser(Hashtable request) | ||
212 | { | ||
213 | m_log.Info("[FreeSwitchDirectory] HandleLocateUser called"); | ||
214 | |||
215 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
216 | string domain = (string) request["domain"]; | ||
217 | string user = (string) request["user"]; | ||
218 | |||
219 | Hashtable response = new Hashtable(); | ||
220 | response["content_type"] = "text/xml"; | ||
221 | response["keepalive"] = false; | ||
222 | response["int_response_code"]=200; | ||
223 | response["str_response_string"] = String.Format( | ||
224 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
225 | "<document type=\"freeswitch/xml\">\r\n" + | ||
226 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
227 | "<domain name=\"{0}\">\r\n" + | ||
228 | "<params>\r\n" + | ||
229 | "<param name=\"dial-string\" value=\"{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
230 | "</params>\r\n" + | ||
231 | "<user id=\"{1}\">\r\n" + | ||
232 | "<variables>\r\n"+ | ||
233 | "<variable name=\"default_gateway\" value=\"$${{default_provider}}\"/>\r\n"+ | ||
234 | "<variable name=\"presence_id\" value=\"{1}@$${{domain}}\"/>"+ | ||
235 | "</variables>\r\n"+ | ||
236 | "</user>\r\n" + | ||
237 | "</domain>\r\n" + | ||
238 | "</section>\r\n" + | ||
239 | "</document>\r\n" | ||
240 | , domain , user); | ||
241 | |||
242 | |||
243 | |||
244 | return response; | ||
245 | } | ||
246 | |||
247 | private Hashtable HandleConfigSofia(Hashtable request) | ||
248 | { | ||
249 | m_log.Info("[FreeSwitchDirectory] HandleConfigSofia called"); | ||
250 | |||
251 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
252 | string domain = (string) request["domain"]; | ||
253 | |||
254 | Hashtable response = new Hashtable(); | ||
255 | response["content_type"] = "text/xml"; | ||
256 | response["keepalive"] = false; | ||
257 | response["int_response_code"]=200; | ||
258 | response["str_response_string"] = String.Format( | ||
259 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
260 | "<document type=\"freeswitch/xml\">\r\n" + | ||
261 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
262 | "<domain name=\"{0}\">\r\n" + | ||
263 | "<params>\r\n" + | ||
264 | "<param name=\"dial-string\" value=\"{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
265 | "</params>\r\n" + | ||
266 | "<groups name=\"default\">\r\n"+ | ||
267 | "<users>\r\n"+ | ||
268 | "<user id=\"$${{default_provider}}\">\r\n"+ | ||
269 | "<gateways>\r\n"+ | ||
270 | "<gateway name=\"$${{default_provider}}\">\r\n"+ | ||
271 | "<param name=\"username\" value=\"$${{default_provider_username}}\"/>\r\n"+ | ||
272 | "<param name=\"password\" value=\"$${{default_provider_password}}\"/>\r\n"+ | ||
273 | "<param name=\"from-user\" value=\"$${{default_provider_username}}\"/>\r\n"+ | ||
274 | "<param name=\"from-domain\" value=\"$${{default_provider_from_domain}}\"/>\r\n"+ | ||
275 | "<param name=\"expire-seconds\" value=\"600\"/>\r\n"+ | ||
276 | "<param name=\"register\" value=\"$${{default_provider_register}}\"/>\r\n"+ | ||
277 | "<param name=\"retry-seconds\" value=\"30\"/>\r\n"+ | ||
278 | "<param name=\"extension\" value=\"$${{default_provider_contact}}\"/>\r\n"+ | ||
279 | "<param name=\"contact-params\" value=\"domain_name=$${{domain}}\"/>\r\n"+ | ||
280 | "<param name=\"context\" value=\"public\"/>\r\n"+ | ||
281 | "</gateway>\r\n"+ | ||
282 | "</gateways>\r\n"+ | ||
283 | "<params>\r\n"+ | ||
284 | "<param name=\"password\" value=\"$${{default_provider_password}}\"/>\r\n"+ | ||
285 | "</params>\r\n"+ | ||
286 | "</user>\r\n"+ | ||
287 | "</users>"+ | ||
288 | "</groups>\r\n" + | ||
289 | "<variables>\r\n"+ | ||
290 | "<variable name=\"default_gateway\" value=\"$${{default_provider}}\"/>\r\n"+ | ||
291 | "</variables>\r\n"+ | ||
292 | "</domain>\r\n" + | ||
293 | "</section>\r\n" + | ||
294 | "</document>\r\n" | ||
295 | , domain); | ||
296 | |||
297 | |||
298 | return response; | ||
299 | } | ||
300 | |||
301 | private Hashtable HandleLoadNetworkLists(Hashtable request) | ||
302 | { | ||
303 | m_log.Info("[FreeSwitchDirectory] HandleLoadNetworkLists called"); | ||
304 | |||
305 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
306 | string domain = (string) request["domain"]; | ||
307 | |||
308 | Hashtable response = new Hashtable(); | ||
309 | response["content_type"] = "text/xml"; | ||
310 | response["keepalive"] = false; | ||
311 | response["int_response_code"]=200; | ||
312 | response["str_response_string"] = String.Format( | ||
313 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
314 | "<document type=\"freeswitch/xml\">\r\n" + | ||
315 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
316 | "<domain name=\"{0}\">\r\n" + | ||
317 | "<params>\r\n" + | ||
318 | "<param name=\"dial-string\" value=\"{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
319 | "</params>\r\n" + | ||
320 | "<groups name=\"default\"><users/></groups>\r\n" + | ||
321 | "<variables>\r\n"+ | ||
322 | "<variable name=\"default_gateway\" value=\"$${{default_provider}}\"/>\r\n"+ | ||
323 | "</variables>\r\n"+ | ||
324 | "</domain>\r\n" + | ||
325 | "</section>\r\n" + | ||
326 | "</document>\r\n" | ||
327 | , domain); | ||
328 | |||
329 | |||
330 | return response; | ||
331 | } | ||
332 | |||
333 | } | ||
334 | |||
335 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs new file mode 100644 index 0000000..a8f9de6 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs | |||
@@ -0,0 +1,571 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Net; | ||
31 | using System.Web; | ||
32 | using System.Text; | ||
33 | using System.Xml; | ||
34 | using System.Collections; | ||
35 | using System.Collections.Generic; | ||
36 | using System.Reflection; | ||
37 | using OpenMetaverse; | ||
38 | using log4net; | ||
39 | using Nini.Config; | ||
40 | using Nwc.XmlRpc; | ||
41 | using OpenSim.Framework; | ||
42 | using OpenSim.Framework.Communications.Cache; | ||
43 | using OpenSim.Framework.Communications.Capabilities; | ||
44 | using OpenSim.Framework.Servers; | ||
45 | using OpenSim.Region.Framework.Interfaces; | ||
46 | using OpenSim.Region.Framework.Scenes; | ||
47 | using Caps = OpenSim.Framework.Communications.Capabilities.Caps; | ||
48 | |||
49 | namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice | ||
50 | { | ||
51 | public class FreeSwitchVoiceModule : IRegionModule | ||
52 | { | ||
53 | |||
54 | // Infrastructure | ||
55 | private static readonly ILog m_log = | ||
56 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
57 | private static readonly bool DUMP = true; | ||
58 | |||
59 | // Capability string prefixes | ||
60 | private static readonly string m_parcelVoiceInfoRequestPath = "0007/"; | ||
61 | private static readonly string m_provisionVoiceAccountRequestPath = "0008/"; | ||
62 | private static readonly string m_chatSessionRequestPath = "0009/"; | ||
63 | |||
64 | // Control info | ||
65 | private static bool m_WOF = true; | ||
66 | private static bool m_pluginEnabled = false; | ||
67 | |||
68 | // FreeSwitch server is going to contact us and ask us all | ||
69 | // sorts of things. | ||
70 | private static string m_freeSwitchServerUser; | ||
71 | private static string m_freeSwitchServerPass; | ||
72 | |||
73 | // SLVoice client will do a GET on this prefix | ||
74 | private static string m_freeSwitchAPIPrefix; | ||
75 | |||
76 | // We need to return some information to SLVoice | ||
77 | // figured those out via curl | ||
78 | // http://vd1.vivox.com/api2/viv_get_prelogin.php | ||
79 | // | ||
80 | // need to figure out whether we do need to return ALL of | ||
81 | // these... | ||
82 | private static string m_freeSwitchRealm; | ||
83 | private static string m_freeSwitchSIPProxy; | ||
84 | private static bool m_freeSwitchAttemptUseSTUN; | ||
85 | private static string m_freeSwitchSTUNServer; | ||
86 | private static string m_freeSwitchEchoServer; | ||
87 | private static int m_freeSwitchEchoPort; | ||
88 | private static string m_freeSwitchDefaultWellKnownIP; | ||
89 | private static int m_freeSwitchDefaultTimeout; | ||
90 | private static int m_freeSwitchSubscribeRetry; | ||
91 | private static string m_freeSwitchUrlResetPassword; | ||
92 | private static IPEndPoint m_FreeSwitchServiceIP; | ||
93 | |||
94 | private FreeSwitchDirectory m_FreeSwitchDirectory; | ||
95 | private FreeSwitchDialplan m_FreeSwitchDialplan; | ||
96 | |||
97 | private IConfig m_config; | ||
98 | |||
99 | public void Initialise(Scene scene, IConfigSource config) | ||
100 | { | ||
101 | |||
102 | m_config = config.Configs["FreeSwitchVoice"]; | ||
103 | |||
104 | if (null == m_config) | ||
105 | { | ||
106 | m_log.Info("[FreeSwitchVoice] no config found, plugin disabled"); | ||
107 | return; | ||
108 | } | ||
109 | |||
110 | if (!m_config.GetBoolean("enabled", false)) | ||
111 | { | ||
112 | m_log.Info("[FreeSwitchVoice] plugin disabled by configuration"); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | // This is only done the FIRST time this method is invoked. | ||
117 | if (m_WOF) | ||
118 | { | ||
119 | m_pluginEnabled = true; | ||
120 | m_WOF = false; | ||
121 | |||
122 | try | ||
123 | { | ||
124 | m_freeSwitchServerUser = m_config.GetString("freeswitch_server_user", String.Empty); | ||
125 | m_freeSwitchServerPass = m_config.GetString("freeswitch_server_pass", String.Empty); | ||
126 | m_freeSwitchAPIPrefix = m_config.GetString("freeswitch_api_prefix", String.Empty); | ||
127 | |||
128 | // XXX: get IP address of HTTP server. (This can be this OpenSim server or another, or could be a dedicated grid service or may live on the freeswitch server) | ||
129 | |||
130 | string serviceIP = m_config.GetString("freeswitch_service_server", String.Empty); | ||
131 | int servicePort = m_config.GetInt("freeswitch_service_port", 80); | ||
132 | IPAddress serviceIPAddress = IPAddress.Parse(serviceIP); | ||
133 | m_FreeSwitchServiceIP = new IPEndPoint(serviceIPAddress, servicePort); | ||
134 | |||
135 | m_freeSwitchRealm = m_config.GetString("freeswitch_realm", String.Empty); | ||
136 | m_freeSwitchSIPProxy = m_config.GetString("freeswitch_sip_proxy", m_freeSwitchRealm); | ||
137 | m_freeSwitchAttemptUseSTUN = m_config.GetBoolean("freeswitch_attempt_stun", true); | ||
138 | m_freeSwitchSTUNServer = m_config.GetString("freeswitch_stun_server", m_freeSwitchRealm); | ||
139 | m_freeSwitchEchoServer = m_config.GetString("freeswitch_echo_server", m_freeSwitchRealm); | ||
140 | m_freeSwitchEchoPort = m_config.GetInt("freeswitch_echo_port", 50505); | ||
141 | m_freeSwitchDefaultWellKnownIP = m_config.GetString("freeswitch_well_known_ip", m_freeSwitchRealm); | ||
142 | m_freeSwitchDefaultTimeout = m_config.GetInt("freeswitch_default_timeout", 5000); | ||
143 | m_freeSwitchSubscribeRetry = m_config.GetInt("freeswitch_subscribe_retry", 120); | ||
144 | m_freeSwitchUrlResetPassword = m_config.GetString("freeswitch_password_reset_url", String.Empty); | ||
145 | |||
146 | |||
147 | |||
148 | |||
149 | if (String.IsNullOrEmpty(m_freeSwitchServerUser) || | ||
150 | String.IsNullOrEmpty(m_freeSwitchServerPass) || | ||
151 | String.IsNullOrEmpty(m_freeSwitchRealm) || | ||
152 | String.IsNullOrEmpty(m_freeSwitchAPIPrefix)) | ||
153 | { | ||
154 | m_log.Error("[FreeSwitchVoice] plugin mis-configured"); | ||
155 | m_log.Info("[FreeSwitchVoice] plugin disabled: incomplete configuration"); | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | // set up http request handlers for | ||
160 | // - prelogin: viv_get_prelogin.php | ||
161 | // - signin: viv_signin.php | ||
162 | scene.CommsManager.HttpServer.AddHTTPHandler(String.Format("{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix), | ||
163 | FreeSwitchSLVoiceGetPreloginHTTPHandler); | ||
164 | |||
165 | // RestStreamHandler h = new RestStreamHandler("GET", String.Format("{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceGetPreloginHTTPHandler); | ||
166 | // scene.CommsManager.HttpServer.AddStreamHandler(h); | ||
167 | |||
168 | |||
169 | |||
170 | scene.CommsManager.HttpServer.AddHTTPHandler(String.Format("{0}/viv_signin.php", m_freeSwitchAPIPrefix), | ||
171 | FreeSwitchSLVoiceSigninHTTPHandler); | ||
172 | |||
173 | // set up http request handlers to provide | ||
174 | // on-demand FreeSwitch configuration to | ||
175 | // FreeSwitch's mod_curl_xml | ||
176 | scene.CommsManager.HttpServer.AddHTTPHandler(String.Format("{0}/freeswitch-config", m_freeSwitchAPIPrefix), | ||
177 | FreeSwitchConfigHTTPHandler); | ||
178 | |||
179 | m_log.InfoFormat("[FreeSwitchVoice] using FreeSwitch server {0}", m_freeSwitchRealm); | ||
180 | |||
181 | m_FreeSwitchDirectory = new FreeSwitchDirectory(); | ||
182 | m_FreeSwitchDialplan = new FreeSwitchDialplan(); | ||
183 | |||
184 | m_pluginEnabled = true; | ||
185 | m_WOF = false; | ||
186 | |||
187 | m_log.Info("[FreeSwitchVoice] plugin enabled"); | ||
188 | } | ||
189 | catch (Exception e) | ||
190 | { | ||
191 | m_log.ErrorFormat("[FreeSwitchVoice] plugin initialization failed: {0}", e.Message); | ||
192 | m_log.DebugFormat("[FreeSwitchVoice] plugin initialization failed: {0}", e.ToString()); | ||
193 | return; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | if (m_pluginEnabled) | ||
198 | { | ||
199 | // we need to capture scene in an anonymous method | ||
200 | // here as we need it later in the callbacks | ||
201 | scene.EventManager.OnRegisterCaps += delegate(UUID agentID, Caps caps) | ||
202 | { | ||
203 | OnRegisterCaps(scene, agentID, caps); | ||
204 | }; | ||
205 | |||
206 | |||
207 | |||
208 | } | ||
209 | } | ||
210 | |||
211 | public void PostInitialise() | ||
212 | { | ||
213 | } | ||
214 | |||
215 | public void Close() | ||
216 | { | ||
217 | } | ||
218 | |||
219 | public string Name | ||
220 | { | ||
221 | get { return "FreeSwitchVoiceModule"; } | ||
222 | } | ||
223 | |||
224 | public bool IsSharedModule | ||
225 | { | ||
226 | get { return true; } | ||
227 | } | ||
228 | |||
229 | |||
230 | // <summary> | ||
231 | // OnRegisterCaps is invoked via the scene.EventManager | ||
232 | // everytime OpenSim hands out capabilities to a client | ||
233 | // (login, region crossing). We contribute two capabilities to | ||
234 | // the set of capabilities handed back to the client: | ||
235 | // ProvisionVoiceAccountRequest and ParcelVoiceInfoRequest. | ||
236 | // | ||
237 | // ProvisionVoiceAccountRequest allows the client to obtain | ||
238 | // the voice account credentials for the avatar it is | ||
239 | // controlling (e.g., user name, password, etc). | ||
240 | // | ||
241 | // ParcelVoiceInfoRequest is invoked whenever the client | ||
242 | // changes from one region or parcel to another. | ||
243 | // | ||
244 | // Note that OnRegisterCaps is called here via a closure | ||
245 | // delegate containing the scene of the respective region (see | ||
246 | // Initialise()). | ||
247 | // </summary> | ||
248 | public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps) | ||
249 | { | ||
250 | m_log.DebugFormat("[FreeSwitchVoice] OnRegisterCaps: agentID {0} caps {1}", agentID, caps); | ||
251 | |||
252 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | ||
253 | caps.RegisterHandler("ProvisionVoiceAccountRequest", | ||
254 | new RestStreamHandler("POST", capsBase + m_provisionVoiceAccountRequestPath, | ||
255 | delegate(string request, string path, string param, | ||
256 | OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
257 | { | ||
258 | return ProvisionVoiceAccountRequest(scene, request, path, param, | ||
259 | agentID, caps); | ||
260 | })); | ||
261 | caps.RegisterHandler("ParcelVoiceInfoRequest", | ||
262 | new RestStreamHandler("POST", capsBase + m_parcelVoiceInfoRequestPath, | ||
263 | delegate(string request, string path, string param, | ||
264 | OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
265 | { | ||
266 | return ParcelVoiceInfoRequest(scene, request, path, param, | ||
267 | agentID, caps); | ||
268 | })); | ||
269 | caps.RegisterHandler("ChatSessionRequest", | ||
270 | new RestStreamHandler("POST", capsBase + m_chatSessionRequestPath, | ||
271 | delegate(string request, string path, string param, | ||
272 | OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
273 | { | ||
274 | return ChatSessionRequest(scene, request, path, param, | ||
275 | agentID, caps); | ||
276 | })); | ||
277 | } | ||
278 | |||
279 | /// <summary> | ||
280 | /// Callback for a client request for Voice Account Details | ||
281 | /// </summary> | ||
282 | /// <param name="scene">current scene object of the client</param> | ||
283 | /// <param name="request"></param> | ||
284 | /// <param name="path"></param> | ||
285 | /// <param name="param"></param> | ||
286 | /// <param name="agentID"></param> | ||
287 | /// <param name="caps"></param> | ||
288 | /// <returns></returns> | ||
289 | public string ProvisionVoiceAccountRequest(Scene scene, string request, string path, string param, | ||
290 | UUID agentID, Caps caps) | ||
291 | { | ||
292 | ScenePresence avatar = scene.GetScenePresence(agentID); | ||
293 | string avatarName = avatar.Name; | ||
294 | |||
295 | try | ||
296 | { | ||
297 | m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: request: {0}, path: {1}, param: {2}", | ||
298 | request, path, param); | ||
299 | |||
300 | //XmlElement resp; | ||
301 | string agentname = "x" + Convert.ToBase64String(agentID.GetBytes()); | ||
302 | string password = "1234";//temp hack//new UUID(Guid.NewGuid()).ToString().Replace('-','Z').Substring(0,16); | ||
303 | |||
304 | // XXX: we need to cache the voice credentials, as | ||
305 | // FreeSwitch is later going to come and ask us for | ||
306 | // those | ||
307 | |||
308 | agentname = agentname.Replace('+', '-').Replace('/', '_'); | ||
309 | |||
310 | // LLSDVoiceAccountResponse voiceAccountResponse = | ||
311 | // new LLSDVoiceAccountResponse(agentname, password, m_freeSwitchRealm, "http://etsvc02.hursley.ibm.com/api"); | ||
312 | LLSDVoiceAccountResponse voiceAccountResponse = | ||
313 | new LLSDVoiceAccountResponse(agentname, password, m_freeSwitchRealm, | ||
314 | String.Format("http://{0}/{1}/", m_FreeSwitchServiceIP, | ||
315 | m_freeSwitchAPIPrefix)); | ||
316 | |||
317 | string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse); | ||
318 | |||
319 | m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}", avatarName, r); | ||
320 | |||
321 | return r; | ||
322 | } | ||
323 | catch (Exception e) | ||
324 | { | ||
325 | m_log.ErrorFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}, retry later", avatarName, e.Message); | ||
326 | m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1} failed", avatarName, e.ToString()); | ||
327 | |||
328 | return "<llsd>undef</llsd>"; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | /// <summary> | ||
333 | /// Callback for a client request for ParcelVoiceInfo | ||
334 | /// </summary> | ||
335 | /// <param name="scene">current scene object of the client</param> | ||
336 | /// <param name="request"></param> | ||
337 | /// <param name="path"></param> | ||
338 | /// <param name="param"></param> | ||
339 | /// <param name="agentID"></param> | ||
340 | /// <param name="caps"></param> | ||
341 | /// <returns></returns> | ||
342 | public string ParcelVoiceInfoRequest(Scene scene, string request, string path, string param, | ||
343 | UUID agentID, Caps caps) | ||
344 | { | ||
345 | ScenePresence avatar = scene.GetScenePresence(agentID); | ||
346 | string avatarName = avatar.Name; | ||
347 | |||
348 | // - check whether we have a region channel in our cache | ||
349 | // - if not: | ||
350 | // create it and cache it | ||
351 | // - send it to the client | ||
352 | // - send channel_uri: as "sip:regionID@m_sipDomain" | ||
353 | try | ||
354 | { | ||
355 | LLSDParcelVoiceInfoResponse parcelVoiceInfo; | ||
356 | string channelUri; | ||
357 | |||
358 | if (null == scene.LandChannel) | ||
359 | throw new Exception(String.Format("region \"{0}\": avatar \"{1}\": land data not yet available", | ||
360 | scene.RegionInfo.RegionName, avatarName)); | ||
361 | |||
362 | |||
363 | |||
364 | // get channel_uri: check first whether estate | ||
365 | // settings allow voice, then whether parcel allows | ||
366 | // voice, if all do retrieve or obtain the parcel | ||
367 | // voice channel | ||
368 | LandData land = scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); | ||
369 | |||
370 | m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}", | ||
371 | scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param); | ||
372 | |||
373 | // TODO: EstateSettings don't seem to get propagated... | ||
374 | // if (!scene.RegionInfo.EstateSettings.AllowVoice) | ||
375 | // { | ||
376 | // m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": voice not enabled in estate settings", | ||
377 | // scene.RegionInfo.RegionName); | ||
378 | // channel_uri = String.Empty; | ||
379 | // } | ||
380 | // else | ||
381 | |||
382 | if ((land.Flags & (uint)Parcel.ParcelFlags.AllowVoiceChat) == 0) | ||
383 | { | ||
384 | m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": voice not enabled for parcel", | ||
385 | scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName); | ||
386 | channelUri = String.Empty; | ||
387 | } | ||
388 | else | ||
389 | { | ||
390 | channelUri = ChannelUri(scene, land); | ||
391 | } | ||
392 | |||
393 | // fill in our response to the client | ||
394 | Hashtable creds = new Hashtable(); | ||
395 | creds["channel_uri"] = channelUri; | ||
396 | |||
397 | parcelVoiceInfo = new LLSDParcelVoiceInfoResponse(scene.RegionInfo.RegionName, land.LocalID, creds); | ||
398 | string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo); | ||
399 | |||
400 | m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": {4}", | ||
401 | scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, r); | ||
402 | return r; | ||
403 | } | ||
404 | catch (Exception e) | ||
405 | { | ||
406 | m_log.ErrorFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": avatar \"{1}\": {2}, retry later", | ||
407 | scene.RegionInfo.RegionName, avatarName, e.Message); | ||
408 | m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": avatar \"{1}\": {2} failed", | ||
409 | scene.RegionInfo.RegionName, avatarName, e.ToString()); | ||
410 | |||
411 | return "<llsd>undef</llsd>"; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | |||
416 | /// <summary> | ||
417 | /// Callback for a client request for ChatSessionRequest | ||
418 | /// </summary> | ||
419 | /// <param name="scene">current scene object of the client</param> | ||
420 | /// <param name="request"></param> | ||
421 | /// <param name="path"></param> | ||
422 | /// <param name="param"></param> | ||
423 | /// <param name="agentID"></param> | ||
424 | /// <param name="caps"></param> | ||
425 | /// <returns></returns> | ||
426 | public string ChatSessionRequest(Scene scene, string request, string path, string param, | ||
427 | UUID agentID, Caps caps) | ||
428 | { | ||
429 | ScenePresence avatar = scene.GetScenePresence(agentID); | ||
430 | string avatarName = avatar.Name; | ||
431 | |||
432 | m_log.DebugFormat("[FreeSwitchVoice][CHATSESSION]: avatar \"{0}\": request: {1}, path: {2}, param: {3}", | ||
433 | avatarName, request, path, param); | ||
434 | return "<llsd>true</llsd>"; | ||
435 | } | ||
436 | |||
437 | |||
438 | public Hashtable FreeSwitchSLVoiceGetPreloginHTTPHandler(Hashtable request) | ||
439 | { | ||
440 | m_log.Debug("[FreeSwitchVoice] FreeSwitchSLVoiceGetPreloginHTTPHandler called"); | ||
441 | |||
442 | Hashtable response = new Hashtable(); | ||
443 | response["content_type"] = "text/xml"; | ||
444 | response["keepalive"] = false; | ||
445 | |||
446 | response["str_response_string"] = String.Format( | ||
447 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
448 | "<VCConfiguration>\r\n"+ | ||
449 | "<DefaultRealm>{0}</DefaultRealm>\r\n" + | ||
450 | "<DefaultSIPProxy>{1}</DefaultSIPProxy>\r\n"+ | ||
451 | "<DefaultAttemptUseSTUN>{2}</DefaultAttemptUseSTUN>\r\n"+ | ||
452 | "<DefaultEchoServer>{3}</DefaultEchoServer>\r\n"+ | ||
453 | "<DefaultEchoPort>{4}</DefaultEchoPort>\r\n"+ | ||
454 | "<DefaultWellKnownIP>{5}</DefaultWellKnownIP>\r\n"+ | ||
455 | "<DefaultTimeout>{6}</DefaultTimeout>\r\n"+ | ||
456 | "<UrlResetPassword>{7}</UrlResetPassword>\r\n"+ | ||
457 | "<UrlPrivacyNotice>{8}</UrlPrivacyNotice>\r\n"+ | ||
458 | "<UrlEulaNotice/>\r\n"+ | ||
459 | "<App.NoBottomLogo>false</App.NoBottomLogo>\r\n"+ | ||
460 | "</VCConfiguration>" | ||
461 | , | ||
462 | m_freeSwitchRealm,m_freeSwitchSIPProxy,m_freeSwitchAttemptUseSTUN, | ||
463 | m_freeSwitchSTUNServer,m_freeSwitchEchoServer,m_freeSwitchEchoPort, | ||
464 | m_freeSwitchDefaultWellKnownIP,m_freeSwitchDefaultTimeout,m_freeSwitchUrlResetPassword,""); | ||
465 | |||
466 | response["int_response_code"] = 200; | ||
467 | |||
468 | m_log.DebugFormat("[FreeSwitchVoice] FreeSwitchSLVoiceGetPreloginHTTPHandler return {0}",response["str_response_string"]); | ||
469 | return response; | ||
470 | } | ||
471 | |||
472 | public Hashtable FreeSwitchSLVoiceSigninHTTPHandler(Hashtable request) | ||
473 | { | ||
474 | m_log.Debug("[FreeSwitchVoice] FreeSwitchSLVoiceSigninHTTPHandler called"); | ||
475 | |||
476 | Hashtable response = new Hashtable(); | ||
477 | response["str_response_string"] = @"<response xsi:schemaLocation=""/xsd/error.xsd""> | ||
478 | <level0> | ||
479 | <status>OK</status> | ||
480 | <body> | ||
481 | <code>200</code> | ||
482 | <msg>auth successful</msg> | ||
483 | </body> | ||
484 | </level0> | ||
485 | </response>"; | ||
486 | response["int_response_code"] = 200; | ||
487 | return response; | ||
488 | } | ||
489 | |||
490 | |||
491 | public Hashtable FreeSwitchConfigHTTPHandler(Hashtable request) | ||
492 | { | ||
493 | m_log.DebugFormat("[FreeSwitchVoice] FreeSwitchConfigHTTPHandler called with {0}",request.ToString()); | ||
494 | |||
495 | Hashtable response = new Hashtable(); | ||
496 | |||
497 | // all the params come as NVPs in the request body | ||
498 | Hashtable requestBody = parseRequestBody((string) request["body"]); | ||
499 | |||
500 | // is this a dialplan or directory request | ||
501 | string section = (string) requestBody["section"]; | ||
502 | |||
503 | if(section=="directory") | ||
504 | response = m_FreeSwitchDirectory.HandleDirectoryRequest(requestBody); | ||
505 | else if (section=="dialplan") | ||
506 | response = m_FreeSwitchDialplan.HandleDialplanRequest(requestBody); | ||
507 | |||
508 | // XXX: re-generate dialplan: | ||
509 | // - conf == region UUID | ||
510 | // - conf number = region port | ||
511 | // -> TODO Initialise(): keep track of regions via events | ||
512 | // re-generate accounts for all avatars | ||
513 | // -> TODO Initialise(): keep track of avatars via events | ||
514 | m_log.DebugFormat("[FreeSwitchVoice] FreeSwitchConfigHTTPHandler return {0}",response["str_response_string"]); | ||
515 | return response; | ||
516 | } | ||
517 | |||
518 | public Hashtable parseRequestBody(string body) | ||
519 | { | ||
520 | Hashtable bodyParams = new Hashtable(); | ||
521 | // split string | ||
522 | string [] nvps = body.Split(new Char [] {'&'}); | ||
523 | |||
524 | foreach (string s in nvps) { | ||
525 | |||
526 | if (s.Trim() != "") | ||
527 | { | ||
528 | string [] nvp = s.Split(new Char [] {'='}); | ||
529 | bodyParams.Add(HttpUtility.UrlDecode(nvp[0]),HttpUtility.UrlDecode(nvp[1])); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | return bodyParams; | ||
534 | |||
535 | } | ||
536 | |||
537 | private string ChannelUri(Scene scene, LandData land) | ||
538 | { | ||
539 | |||
540 | string channelUri = null; | ||
541 | |||
542 | string landUUID; | ||
543 | string landName; | ||
544 | |||
545 | // Create parcel voice channel. If no parcel exists, then the voice channel ID is the same | ||
546 | // as the directory ID. Otherwise, it reflects the parcel's ID. | ||
547 | |||
548 | if (land.LocalID != 1 && (land.Flags & (uint)Parcel.ParcelFlags.UseEstateVoiceChan) == 0) | ||
549 | { | ||
550 | landName = String.Format("{0}:{1}", scene.RegionInfo.RegionName, land.Name); | ||
551 | landUUID = land.GlobalID.ToString(); | ||
552 | m_log.DebugFormat("[FreeSwitchVoice]: Region:Parcel \"{0}\": parcel id {1}: using channel name {2}", | ||
553 | landName, land.LocalID, landUUID); | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | landName = String.Format("{0}:{1}", scene.RegionInfo.RegionName, scene.RegionInfo.RegionName); | ||
558 | landUUID = scene.RegionInfo.RegionID.ToString(); | ||
559 | m_log.DebugFormat("[FreeSwitchVoice]: Region:Parcel \"{0}\": parcel id {1}: using channel name {2}", | ||
560 | landName, land.LocalID, landUUID); | ||
561 | } | ||
562 | System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); | ||
563 | channelUri = String.Format("sip:confctl-{0}@{1}", "x" + Convert.ToBase64String(encoding.GetBytes(landUUID)), m_freeSwitchRealm); | ||
564 | |||
565 | //channelUri="sip:confctl-3001@9.20.151.43"; | ||
566 | //channelUri="sip:opensimconf-3001@9.20.151.43"; | ||
567 | |||
568 | return channelUri; | ||
569 | } | ||
570 | } | ||
571 | } | ||